示例#1
0
 public String GetLastMail(AccountData account)
 {
     for (int i = 0; i < 20; i++)
     {
         Pop3Client pop3Client = new Pop3Client("localhost", 110, account.Name, account.Password, false);
         pop3Client.Connect();
         pop3Client.Authenticate();
         if (pop3Client.GetMessageCount() > 0)
         {
             OpaqueMail.MailMessage message = pop3Client.GetMessage(1);
             string body = message.Body;
             pop3Client.DeleteMessage(1);
             pop3Client.LogOut();
             return(body);
         }
         else
         {
             System.Threading.Thread.Sleep(3000);
         }
     }
     return(null);
 }
示例#2
0
        /// <summary>
        /// Process a transmitted message to import any signing certificates for subsequent S/MIME encryption.
        /// </summary>
        /// <param name="o">A ProcessMessageArguments object containing message parameters.</param>
        private void ProcessMessage(object o)
        {
            ProcessMessageArguments arguments = (ProcessMessageArguments)o;

            // Export the message to a local directory.
            if (!string.IsNullOrEmpty(arguments.ExportDirectory))
            {
                string messageId = Functions.ReturnBetween(arguments.MessageText.ToLower(), "message-id: <", ">");
                if (string.IsNullOrEmpty(messageId))
                    messageId = Guid.NewGuid().ToString();

                string fileName = ProxyFunctions.GetExportFileName(arguments.ExportDirectory, messageId, arguments.InstanceId, arguments.UserName);
                File.WriteAllText(fileName, arguments.MessageText);
            }

            // Only parse the message if it contains a known S/MIME content type.
            string canonicalMessageText = arguments.MessageText.ToLower();
            if (canonicalMessageText.IndexOf("application/x-pkcs7-signature") > -1 || canonicalMessageText.IndexOf("application/pkcs7-mime") > -1)
            {
                try
                {
                    // Parse the message.
                    MailMessage message = new MailMessage(arguments.MessageText);

                    // If the message contains a signing certificate that we haven't processed on this session, import it.
                    if (message.SmimeSigningCertificate != null && !SmimeCertificatesReceived.Contains(message.SmimeSigningCertificate))
                    {
                        // Import the certificate to the Local Machine store.
                        ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Importing certificate with Serial Number {" + message.SmimeSigningCertificate.SerialNumber + "}.", Proxy.LogLevel.Information, LogLevel);
                        CertHelper.InstallWindowsCertificate(message.SmimeSigningCertificate, StoreLocation.LocalMachine);

                        // Remember this ceriticate to avoid importing it again this session.
                        SmimeCertificatesReceived.Add(message.SmimeSigningCertificate);
                    }
                }
                catch (Exception ex)
                {
                    if (arguments.DebugMode || System.Diagnostics.Debugger.IsAttached)
                        ProxyFunctions.Log(LogWriter, SessionId, "Exception while processing message: " + ex.ToString(), Proxy.LogLevel.Error, LogLevel);
                    else
                        ProxyFunctions.Log(LogWriter, SessionId, "Exception while processing message: " + ex.Message, Proxy.LogLevel.Error, LogLevel);
                }
            }
        }
 /// <summary>
 /// Appends a message to the specified mailbox.
 /// </summary>
 /// <param name="mailboxName">The name of the mailbox to append to.</param>
 /// <param name="message">The raw message to append.</param>
 /// <param name="flags">Optional flags to be applied for the message.</param>
 /// <param name="date">Optional date for the message.</param>
 public bool AppendMessage(string mailboxName, MailMessage message, string[] flags, DateTime? date)
 {
     return Task.Run(() => AppendMessageAsync(mailboxName, message, flags, date)).Result;
 }
 /// <summary>
 /// Appends a message to the specified mailbox.
 /// </summary>
 /// <param name="mailboxName">The name of the mailbox to append to.</param>
 /// <param name="message">The raw message to append.</param>
 public bool AppendMessage(string mailboxName, MailMessage message)
 {
     return Task.Run(() => AppendMessageAsync(mailboxName, message)).Result;
 }
 /// <summary>
 /// Appends messages to the specified mailbox.
 /// </summary>
 /// <param name="mailboxName">The name of the mailbox to append to.</param>
 /// <param name="messages">The raw messages to append.</param>
 public bool AppendMessages(string mailboxName, MailMessage[] messages)
 {
     return Task.Run(() => AppendMessagesAsync(mailboxName, messages, new string[] { }, null)).Result;
 }
示例#6
0
        /// <summary>
        /// Appends messages to the specified mailbox.
        /// </summary>
        /// <param name="mailboxName">The name of the mailbox to append to.</param>
        /// <param name="messages">The raw messages to append.</param>
        /// <param name="flags">Optional flags to be applied for the message.</param>
        /// <param name="date">Optional date for the message.</param>
        public async Task<bool> AppendMessagesAsync(string mailboxName, MailMessage[] messages, string[] flags, DateTime? date)
        {
            string[] rawMessages = new string[messages.Length];
            for (int i = 0; i < messages.Length; i++)
                rawMessages[i] = messages[i].RawMessage;

            return await AppendMessagesAsync(mailboxName, rawMessages, flags, date);
        }
示例#7
0
 /// <summary>
 /// Appends a message to the specified mailbox.
 /// </summary>
 /// <param name="mailboxName">The name of the mailbox to append to.</param>
 /// <param name="message">The raw message to append.</param>
 /// <param name="flags">Optional flags to be applied for the message.</param>
 /// <param name="date">Optional date for the message.</param>
 public async Task<bool> AppendMessageAsync(string mailboxName, MailMessage message, string[] flags, DateTime? date)
 {
     return await AppendMessageAsync(mailboxName, message.RawMessage, flags, date);
 }
示例#8
0
 /// <summary>
 /// Sends the specified message to an SMTP server for delivery.
 /// Performs requested S/MIME signing and encryption.
 /// </summary>
 /// <param name="message">An OpaqueMail.MailMessage that contains the message to send.</param>
 public async Task SendAsync(MailMessage message)
 {
     await SmimeSendRawAsync(message);
 }
示例#9
0
        /// <summary>
        /// Helper function to load an instance of a message in a specified mailbox based on its index, optionally returning only headers and/or setting the "Seen" flag.
        /// </summary>
        /// <param name="mailboxName">The mailbox to load from.</param>
        /// <param name="id">The identifier of the message to load, either its index or UID.</param>
        /// <param name="headersOnly">Return only the message's headers when true; otherwise, return the message and body.</param>        
        /// <param name="setSeenFlag">Whether to touch the message and set its "Seen" flag.</param>
        /// <param name="isUid">Whether the identifer is an UID.</param>
        /// <param name="seekStart">Index of first character of the message body to return.</param>
        /// <param name="seekEnd">Number of characters of the message body to return.</param>
        private async Task<MailMessage> GetMessageHelper(string mailboxName, int id, bool headersOnly, bool setSeenFlag, int seekStart, int seekEnd, bool isUid)
        {
            // Protect against commands being called out of order.
            if (!IsAuthenticated)
                throw new ImapException("Must be connected to the server and authenticated prior to calling the FETCH command.");

            if (mailboxName != CurrentMailboxName)
                await SelectMailboxAsync(mailboxName);

            string uidPrefix = isUid ? "UID " : "";

            // Generate a unique command tag for tracking this command and its response.
            string commandTag = UniqueCommandTag();

            // Format the command depending on whether we want headers only,
            //      whether this is a partial request, and
            //      whether we want to mark messages as seen.
            if (headersOnly)
                await SendCommandAsync(commandTag, uidPrefix + "FETCH " + id + " (BODY[HEADER] UID)\r\n");
            else
            {
                if (setSeenFlag)
                {
                    if (seekStart > -1)
                    {
                        if (seekEnd > -1)
                            await SendCommandAsync(commandTag, uidPrefix + "FETCH " + id + " (BODY[]<" + seekStart + "> UID FLAGS)\r\n");
                        else
                            await SendCommandAsync(commandTag, uidPrefix + "FETCH " + id + " (BODY[]<" + seekStart + "." + seekEnd + "> UID FLAGS)\r\n");
                    }
                    else
                        await SendCommandAsync(commandTag, uidPrefix + "FETCH " + id + " (BODY[] UID FLAGS)\r\n");
                }
                else
                {
                    if (seekStart > -1)
                    {
                        if (seekEnd > -1)
                            await SendCommandAsync(commandTag, uidPrefix + "FETCH " + id + " (BODY.PEEK[]<" + seekStart + "> UID FLAGS)\r\n");
                        else
                            await SendCommandAsync(commandTag, uidPrefix + "FETCH " + id + " (BODY.PEEK[]<" + seekStart + "." + seekEnd + "> UID FLAGS)\r\n");
                    }
                    else
                        await SendCommandAsync(commandTag, uidPrefix + "FETCH " + id + " (BODY.PEEK[] UID FLAGS)\r\n");
                }
            }

            string response = await ReadDataAsync(commandTag, "FETCH");

            // Ensure the message was actually found.
            if (response.IndexOf("\r\n") > -1)
            {
                // Read the message's UID and flags.
                int uid = 0;
                int.TryParse(Functions.ReturnBetween(response, "UID ", " "), out uid);
                string flagsString = Functions.ReturnBetween(response, "FLAGS (", ")");

                // Strip IMAP response padding.
                int lineBreak = response.IndexOf("\r\n");
                string firstLine = response.Substring(0, lineBreak).ToUpper();

                int bodyLength = -1;
                int.TryParse(Functions.ReturnBetween(firstLine, "BODY[] {", "}"), out bodyLength);
                if (bodyLength > 0)
                    response = response.Substring(lineBreak + 2, bodyLength);
                else
                {
                    response = response.Substring(response.IndexOf("\r\n") + 2);
                    response = response.Substring(0, response.Length - 2);
                }

                MailMessage message = new MailMessage(response, ProcessingFlags);
                message.ImapUid = uid;
                message.Mailbox = mailboxName;
                message.ParseFlagsString(flagsString);
                return message;
            }
            else
                return null;
        }
示例#10
0
        /// <summary>
        /// Create a byte array containing a signed S/MIME message.
        /// </summary>
        /// <param name="buffer">A byte array to streamline bit shuffling.</param>
        /// <param name="contentBytes">The contents of the envelope to be encrypted.</param>
        /// <param name="message">An OpaqueMail.MailMessage that contains the message to send.</param>
        /// <param name="alreadyEncrypted">Whether a portion of the message has previously been signed, as when triple wrapping.</param>
        private byte[] SmimeSign(byte[] buffer, byte[] contentBytes, MailMessage message, bool alreadyEncrypted)
        {
            if (message.SmimeSigningCertificate == null)
            {
                // If the implementation requires S/MIME signing (the default), throw an error if there's no certificate.
                if ((message.SmimeSettingsMode & SmimeSettingsMode.RequireExactSettings) > 0)
                    throw new SmtpException("Trying to send a signed message, but no signing certificate has been assigned.");
                else
                    return contentBytes;
            }

            // First, create a buffer for tracking the unsigned portion of this message.
            StringBuilder unsignedMessageBuilder = new StringBuilder(Constants.SMALLSBSIZE);

            // If triple wrapping, the previous layer was an encrypted envelope and needs to be Base64 encoded.
            if (alreadyEncrypted)
            {
                unsignedMessageBuilder.Append("Content-Type: application/pkcs7-mime; smime-type=enveloped-data;\r\n\tname=\"smime.p7m\"\r\n");
                unsignedMessageBuilder.Append("Content-Transfer-Encoding: base64\r\n");
                unsignedMessageBuilder.Append("Content-Description: \"S/MIME Cryptographic envelopedCms\"\r\n");
                unsignedMessageBuilder.Append("Content-Disposition: attachment; filename=\"smime.p7m\"\r\n\r\n");

                unsignedMessageBuilder.Append(Functions.ToBase64String(contentBytes));
            }
            else
                unsignedMessageBuilder.Append(Encoding.UTF8.GetString(contentBytes));

            // Prepare the signing parameters.
            ContentInfo contentInfo = new ContentInfo(Encoding.UTF8.GetBytes(unsignedMessageBuilder.ToString()));
            SignedCms signedCms = new SignedCms(contentInfo, true);

            CmsSigner signer = new CmsSigner(message.SubjectIdentifierType, message.SmimeSigningCertificate);
            signer.IncludeOption = X509IncludeOption.WholeChain;

            // Sign the current time.
            if ((message.SmimeSigningOptionFlags & SmimeSigningOptionFlags.SignTime) > 0)
            {
                Pkcs9SigningTime signingTime = new Pkcs9SigningTime();
                signer.SignedAttributes.Add(signingTime);
            }

            // Encode the signed message.
            signedCms.ComputeSignature(signer);
            byte[] signedBytes = signedCms.Encode();

            // Embed the signed and original version of the message using MIME.
            StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE);

            // Build the MIME message by embedding the unsigned and signed portions.
            messageBuilder.Append("This is a multi-part S/MIME signed message.\r\n\r\n");
            messageBuilder.Append("--" + (alreadyEncrypted ? SmimeTripleSignedCmsBoundaryName : SmimeSignedCmsBoundaryName) + "\r\n");
            messageBuilder.Append(unsignedMessageBuilder.ToString());
            messageBuilder.Append("\r\n--" + (alreadyEncrypted ? SmimeTripleSignedCmsBoundaryName : SmimeSignedCmsBoundaryName) + "\r\n");
            messageBuilder.Append("Content-Type: application/x-pkcs7-signature; smime-type=signed-data; name=\"smime.p7s\"\r\n");
            messageBuilder.Append("Content-Transfer-Encoding: base64\r\n");
            messageBuilder.Append("Content-Description: \"S/MIME Cryptographic signedCms\"\r\n");
            messageBuilder.Append("Content-Disposition: attachment; filename=\"smime.p7s\"\r\n\r\n");
            messageBuilder.Append(Functions.ToBase64String(signedBytes, 0, signedBytes.Length));
            messageBuilder.Append("\r\n--" + (alreadyEncrypted ? SmimeTripleSignedCmsBoundaryName : SmimeSignedCmsBoundaryName) + "--\r\n");

            return Encoding.UTF8.GetBytes(messageBuilder.ToString());
        }
示例#11
0
        /// <summary>
        /// Helper function for sending the specified message to an SMTP server for delivery with S/MIME encoding.
        /// </summary>
        /// <param name="message">An OpaqueMail.MailMessage that contains the message to send.</param>
        private async Task SmimeSendAsync(MailMessage message)
        {
            // Require one or more recipients.
            if (message.To.Count + message.CC.Count + message.Bcc.Count < 1)
                throw new SmtpException("One or more recipients must be specified via the '.To', '.CC', or '.Bcc' collections.");

            // Require a signing certificate to be specified.
            if ((message.SmimeSigned || message.SmimeTripleWrapped) && message.SmimeSigningCertificate == null)
                throw new SmtpException("A signing certificate must be passed prior to signing.");

            // Ensure the rendering engine expects MIME encoding.
            message.Headers["MIME-Version"] = "1.0";

            // OpaqueMail optional setting for protecting the subject.
            // Note: This is not part of the current RFC specifcation and should only be used when sending to other OpaqueMail agents.
            if ((message.SmimeEncryptedEnvelope || message.SmimeTripleWrapped) && (message.SmimeEncryptionOptionFlags & (SmimeEncryptionOptionFlags.EncryptSubject)) > 0)
            {
                message.Headers["X-Subject-Encryption"] = "true";
                message.Body = "Subject: " + message.Subject + "\r\n" + message.Body;
                message.Subject = Guid.NewGuid().ToString();
            }

            // Generate a multipart/mixed message containing the email's body, alternate views, and attachments.
            string MIMEMessage = await message.MimeEncode("7bit", SmimeBoundaryName);
            message.Headers["Content-Type"] = "multipart/mixed; boundary=\"" + SmimeBoundaryName + "\"";
            message.Headers["Content-Transfer-Encoding"] = "7bit";

            // Skip the MIME header.
            message.Body = MIMEMessage;
            message.Body = message.Body.Substring(message.Body.IndexOf("\r\n\r\n") + 4);

            // Determine the body encoding, defaulting to UTF-8.
            Encoding bodyEncoding = message.BodyEncoding != null ? message.BodyEncoding : new UTF8Encoding();
            Encoder bodyEncoder = bodyEncoding.GetEncoder();

            // Encode and return the message.
            char[] chars = MIMEMessage.ToCharArray();
            byte[] MIMEMessageBytes = new byte[bodyEncoder.GetByteCount(chars, 0, chars.Length, false)];
            int byteCount = bodyEncoder.GetBytes(chars, 0, chars.Length, MIMEMessageBytes, 0, true);

            // Handle S/MIME signing.
            bool successfullySigned = false;
            if (message.SmimeSigned || message.SmimeTripleWrapped)
            {
                int unsignedSize = MIMEMessageBytes.Length;
                MIMEMessageBytes = SmimeSign(buffer, MIMEMessageBytes, message, false);
                successfullySigned = MIMEMessageBytes.Length != unsignedSize;

                if (successfullySigned)
                {
                    // Remove any prior content dispositions.
                    if (message.Headers["Content-Disposition"] != null)
                        message.Headers.Remove("Content-Disposition");

                    message.Headers["Content-Type"] = "multipart/signed; protocol=\"application/x-pkcs7-signature\"; micalg=sha1;\r\n\tboundary=\"" + SmimeSignedCmsBoundaryName + "\"";
                    message.Headers["Content-Transfer-Encoding"] = "7bit";
                    message.Body = Encoding.UTF8.GetString(MIMEMessageBytes);
                }
            }

            // Handle S/MIME envelope encryption.
            bool successfullyEncrypted = false;
            if (message.SmimeEncryptedEnvelope || message.SmimeTripleWrapped)
            {
                int unencryptedSize = MIMEMessageBytes.Length;
                MIMEMessageBytes = SmimeEncryptEnvelope(MIMEMessageBytes, message, successfullySigned);
                successfullyEncrypted = MIMEMessageBytes.Length != unencryptedSize;

                // If the message won't be triple-wrapped, wrap the encrypted message with MIME.
                if (successfullyEncrypted && (!successfullySigned || !message.SmimeTripleWrapped))
                {
                    message.Headers["Content-Type"] = "application/pkcs7-mime; name=smime.p7m;\r\n\tsmime-type=enveloped-data";
                    message.Headers["Content-Transfer-Encoding"] = "base64";

                    message.Body = Functions.ToBase64String(MIMEMessageBytes) + "\r\n";
                }
            }

            // Handle S/MIME triple wrapping (i.e. signing, envelope encryption, then signing again).
            if (successfullyEncrypted)
            {
                if (message.SmimeTripleWrapped)
                {
                    message.Headers["Content-Type"] = "multipart/signed; protocol=\"application/x-pkcs7-signature\"; micalg=sha1;\r\n\tboundary=\"" + SmimeTripleSignedCmsBoundaryName + "\"";
                    message.Headers["Content-Transfer-Encoding"] = "7bit";

                    message.Body = Encoding.UTF8.GetString(SmimeSign(buffer, MIMEMessageBytes, message, true));
                }
                else
                    message.Headers["Content-Disposition"] = "attachment; filename=smime.p7m";
            }

            await SmimeSendRawAsync(message);
        }
示例#12
0
        /// <summary>
        /// Create a byte array containing an encrypted S/MIME envelope.
        /// </summary>
        /// <param name="contentBytes">The contents of the envelope to be encrypted.</param>
        /// <param name="message">An OpaqueMail.MailMessage that contains the message to send.</param>
        private byte[] SmimeEncryptEnvelope(byte[] contentBytes, MailMessage message, bool alreadySigned)
        {
            // Resolve recipient public keys.
            Dictionary<string, MailAddress> addressesNeedingPublicKeys;
            HashSet<string> addressesWithPublicKeys;
            ResolvePublicKeys(message, out addressesWithPublicKeys, out addressesNeedingPublicKeys);

            // Throw an error if we're unable to encrypt the message for one or more recipients and encryption is explicitly required.
            if (addressesNeedingPublicKeys.Count > 0)
            {
                // If the implementation requires S/MIME encryption (the default), throw an error if there's no certificate.
                if ((message.SmimeSettingsMode & SmimeSettingsMode.RequireExactSettings) > 0)
                {
                    StringBuilder exceptionMessage = new StringBuilder(Constants.TINYSBSIZE);
                    exceptionMessage.Append("Trying to send encrypted message to one or more recipients without a trusted public key.\r\nRecipients without public keys: ");
                    foreach (string addressNeedingPublicKey in addressesNeedingPublicKeys.Keys)
                        exceptionMessage.Append(addressNeedingPublicKey + ", ");
                    exceptionMessage.Remove(exceptionMessage.Length - 2, 2);

                    throw new SmtpException(exceptionMessage.ToString());
                }
                else
                    return contentBytes;
            }

            if (alreadySigned)
            {
                // If already signed, prepend S/MIME headers.
                StringBuilder contentBuilder = new StringBuilder(Constants.TINYSBSIZE);
                contentBuilder.Append("Content-Type: multipart/signed; protocol=\"application/x-pkcs7-signature\"; micalg=sha1;\r\n\tboundary=\"" + SmimeSignedCmsBoundaryName + "\"\r\n");
                contentBuilder.Append("Content-Transfer-Encoding: 7bit\r\n\r\n");

                contentBytes = Encoding.UTF8.GetBytes(contentBuilder.ToString() + Encoding.UTF8.GetString(contentBytes));
            }

            // Prepare the encryption envelope.
            ContentInfo contentInfo = new ContentInfo(contentBytes);
            EnvelopedCms envelope;

            // If a specific algorithm is specified, choose that.  Otherwise, negotiate which algorithm to use.
            if (SmimeAlgorithmIdentifier != null)
                envelope = new EnvelopedCms(contentInfo, SmimeAlgorithmIdentifier);
            else
                envelope = new EnvelopedCms(contentInfo);

            // Encrypt the symmetric session key using each recipient's public key.
            foreach (string addressWithPublicKey in addressesWithPublicKeys)
            {
                CmsRecipient recipient = new CmsRecipient(SmimeCertificateCache[addressWithPublicKey]);
                envelope.Encrypt(recipient);
            }

            return envelope.Encode();
        }
示例#13
0
        /// <summary>
        /// Sends the specified message to an SMTP server for delivery without making modifications to the body.
        /// Necessary because the standard SmtpClient.Send() may slightly alter messages, invalidating signatures.
        /// </summary>
        /// <param name="message">An OpaqueMail.MailMessage that contains the message to send.</param>
        private async Task SmimeSendRawAsync(MailMessage message)
        {
            // Connect to the SMTP server.
            TcpClient SmtpTcpClient = new TcpClient();
            SmtpTcpClient.Connect(Host, Port);
            Stream SmtpStream = SmtpTcpClient.GetStream();

            // Use stream readers and writers to simplify I/O.
            StreamReader reader = new StreamReader(SmtpStream);
            StreamWriter writer = new StreamWriter(SmtpStream);
            writer.AutoFlush = true;

            // Read the welcome message.
            string response = await reader.ReadLineAsync();

            // Send EHLO and find out server capabilities.
            await writer.WriteLineAsync("EHLO " + Host);
            char[] charBuffer = new char[Constants.SMALLBUFFERSIZE];
            int bytesRead = await reader.ReadAsync(charBuffer, 0, Constants.SMALLBUFFERSIZE);
            response = new string(charBuffer, 0, bytesRead);
            if (!response.StartsWith("2"))
                throw new SmtpException("Unable to connect to remote server '" + Host + "'.  Sent 'EHLO' and received '" + response + "'.");

            // Stand up a TLS/SSL stream.
            if (EnableSsl)
            {
                await writer.WriteLineAsync("STARTTLS");
                response = await reader.ReadLineAsync();
                if (!response.StartsWith("2"))
                    throw new SmtpException("Unable to start TLS/SSL protection with '" + Host + "'.  Received '" + response + "'.");

                SmtpStream = new SslStream(SmtpStream);
                ((SslStream)SmtpStream).AuthenticateAsClient(Host);

                reader = new StreamReader(SmtpStream);
                writer = new StreamWriter(SmtpStream);
                writer.AutoFlush = true;

                await writer.WriteLineAsync("EHLO " + Host);
                bytesRead = await reader.ReadAsync(charBuffer, 0, Constants.SMALLBUFFERSIZE);
                response = new string(charBuffer, 0, bytesRead);
            }

            // Authenticate using the AUTH LOGIN command.
            if (Credentials != null)
            {
                NetworkCredential cred = (NetworkCredential)Credentials;
                await writer.WriteLineAsync("AUTH LOGIN");
                response = await reader.ReadLineAsync();
                if (!response.StartsWith("3"))
                    throw new SmtpException("Unable to authenticate with server '" + Host + "'.  Received '" + response + "'.");
                await writer.WriteLineAsync(Functions.ToBase64String(cred.UserName));
                response = await reader.ReadLineAsync();
                await writer.WriteLineAsync(Functions.ToBase64String(cred.Password));
                response = await reader.ReadLineAsync();
                if (!response.StartsWith("2"))
                    throw new SmtpException("Unable to authenticate with server '" + Host + "'.  Received '" + response + "'.");
            }

            // Build our raw headers block.
            StringBuilder rawHeaders = new StringBuilder(Constants.SMALLSBSIZE);

            // Specify who the message is from.
            rawHeaders.Append(Functions.SpanHeaderLines("From: " + Functions.EncodeMailHeader(Functions.ToMailAddressString(message.From))) + "\r\n");
            await writer.WriteLineAsync("MAIL FROM:<" + message.From.Address + ">");
            response = await reader.ReadLineAsync();
            if (!response.StartsWith("2"))
                throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'MAIL FROM' and received '" + response + "'.");

            // Identify all recipients of the message.
            if (message.To.Count > 0)
                rawHeaders.Append(Functions.SpanHeaderLines("To: " + Functions.EncodeMailHeader(message.To.ToString())) + "\r\n");
            foreach (MailAddress address in message.To)
            {
                await writer.WriteLineAsync("RCPT TO:<" + address.Address + ">");
                response = await reader.ReadLineAsync();
                if (!response.StartsWith("2"))
                    throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'RCPT TO' and received '" + response + "'.");
            }

            if (message.CC.Count > 0)
                rawHeaders.Append(Functions.SpanHeaderLines("CC: " + Functions.EncodeMailHeader(message.CC.ToString())) + "\r\n");
            foreach (MailAddress address in message.CC)
            {
                await writer.WriteLineAsync("RCPT TO:<" + address.Address + ">");
                response = await reader.ReadLineAsync();
                if (!response.StartsWith("2"))
                    throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'RCPT TO' and received '" + response + "'.");
            }

            foreach (MailAddress address in message.Bcc)
            {
                await writer.WriteLineAsync("RCPT TO:<" + address.Address + ">");
                response = await reader.ReadLineAsync();
                if (!response.StartsWith("2"))
                    throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'RCPT TO' and received '" + response + "'.");
            }

            // Ensure a content type is set.
            if (string.IsNullOrEmpty(message.ContentType))
            {
                if (Functions.AppearsHTML(message.Body))
                    message.ContentType = "text/html";
                else
                    message.ContentType = "text/plain";
            }
            message.Headers["Content-Type"] = message.ContentType + (!string.IsNullOrEmpty(message.CharSet) ? "; charset=\"" + message.CharSet + "\"" : "");

            // If the body hasn't been processed, handle encoding of extended characters.
            if (string.IsNullOrEmpty(message.RawBody))
            {
                bool extendedCharacterFound = false;
                foreach (char headerCharacter in message.Body.ToCharArray())
                {
                    if (headerCharacter > 127)
                    {
                        extendedCharacterFound = true;
                        break;
                    }
                }

                if (extendedCharacterFound)
                {
                    message.Headers["Content-Transfer-Encoding"] = "base64";
                    message.Body = Functions.ToBase64String(message.Body);
                }

                message.RawBody = message.Body;
            }

            // Send the raw message.
            await writer.WriteLineAsync("DATA");
            response = await reader.ReadLineAsync();
            if (!response.StartsWith("3"))
                throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent 'DATA' and received '" + response + "'.");

            rawHeaders.Append(Functions.SpanHeaderLines("Subject: " + Functions.EncodeMailHeader(message.Subject)) + "\r\n");
            foreach (string rawHeader in message.Headers)
            {
                switch (rawHeader.ToUpper())
                {
                    case "BCC":
                    case "CC":
                    case "FROM":
                    case "SUBJECT":
                    case "TO":
                        break;
                    default:
                        rawHeaders.Append(Functions.SpanHeaderLines(rawHeader + ": " + message.Headers[rawHeader]) + "\r\n");
                        break;
                }
            }

            await writer.WriteAsync(rawHeaders.ToString() + "\r\n" + message.RawBody + "\r\n.\r\n");

            response = await reader.ReadLineAsync();
            if (!response.StartsWith("2"))
                throw new SmtpException("Exception communicating with server '" + Host + "'.  Sent message and received '" + response + "'.");

            // Clean up this connection.
            await writer.WriteLineAsync("QUIT");

            writer.Dispose();
            reader.Dispose();

            SmtpStream.Dispose();
            SmtpTcpClient.Close();
        }
示例#14
0
        /// <summary>
        /// Helper function to look up and validate public keys for each recipient.
        /// </summary>
        /// <param name="message">An OpaqueMail.MailMessage that contains the message to send.</param>
        /// <param name="addressesWithPublicKeys">Collection containing recipients with valid public keys.</param>
        /// <param name="addressesNeedingPublicKeys">Collection containing recipients without valid public keys.</param>
        private void ResolvePublicKeys(MailMessage message, out HashSet<string> addressesWithPublicKeys, out Dictionary<string, MailAddress> addressesNeedingPublicKeys)
        {
            // Initialize collections for all recipients.
            addressesWithPublicKeys = new HashSet<string>();
            addressesNeedingPublicKeys = new Dictionary<string, MailAddress>();

            MailAddressCollection[] addressRanges = new MailAddressCollection[] { message.To, message.CC, message.Bcc };
            foreach (MailAddressCollection addressRange in addressRanges)
            {
                foreach (MailAddress toAddress in addressRange)
                {
                    string canonicalToAddress = toAddress.Address.ToUpper();
                    if (SmimeCertificateCache.ContainsKey(canonicalToAddress))
                    {
                        if (!addressesWithPublicKeys.Contains(canonicalToAddress))
                            addressesWithPublicKeys.Add(canonicalToAddress);
                    }
                    else
                    {
                        if (!addressesNeedingPublicKeys.ContainsKey(canonicalToAddress))
                            addressesNeedingPublicKeys.Add(canonicalToAddress, toAddress);
                    }
                }
            }

            // If any addresses haven't been mapped to public keys, map them.
            if (addressesNeedingPublicKeys.Count > 0)
            {
                // Read from the Windows certificate store if valid certificates aren't specified.
                if (SmimeValidCertificates == null || SmimeValidCertificates.Count < 1)
                {
                    // Load from the current user.
                    X509Store store = new X509Store(StoreLocation.CurrentUser);
                    store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
                    SmimeValidCertificates = store.Certificates;
                    store.Close();

                    // Add any tied to the local machine.
                    store = new X509Store(StoreLocation.LocalMachine);
                    store.Open(OpenFlags.OpenExistingOnly | OpenFlags.ReadOnly);
                    SmimeValidCertificates.AddRange(store.Certificates);
                    store.Close();
                }

                // Loop through certificates and check for matching recipients.
                foreach (X509Certificate2 cert in SmimeValidCertificates)
                {
                    // Look at certificates with email subject names.
                    string canonicalCertSubject = "";
                    if (cert.Subject.StartsWith("E="))
                        canonicalCertSubject = cert.Subject.Substring(2).ToUpper();
                    else if (cert.Subject.StartsWith("CN="))
                        canonicalCertSubject = cert.Subject.Substring(3).ToUpper();
                    else
                        canonicalCertSubject = cert.Subject.ToUpper();

                    int certSubjectComma = canonicalCertSubject.IndexOf(",");
                    if (certSubjectComma > -1)
                        canonicalCertSubject = canonicalCertSubject.Substring(0, certSubjectComma);

                    // Only proceed if the key is for a recipient of this email.
                    if (!addressesNeedingPublicKeys.ContainsKey(canonicalCertSubject))
                        continue;

                    // Verify the certificate chain.
                    if ((message.SmimeEncryptionOptionFlags & SmimeEncryptionOptionFlags.RequireCertificateVerification) > 0)
                    {
                        if (!cert.Verify())
                            continue;
                    }

                    // Ensure valid key usage scenarios.
                    if ((message.SmimeEncryptionOptionFlags & SmimeEncryptionOptionFlags.RequireKeyUsageOfDataEncipherment) > 0 || (message.SmimeEncryptionOptionFlags & SmimeEncryptionOptionFlags.RequireEnhancedKeyUsageofSecureEmail) > 0)
                    {
                        bool keyDataEncipherment = false, enhancedKeySecureEmail = false;
                        foreach (X509Extension extension in cert.Extensions)
                        {
                            if (!keyDataEncipherment && extension.Oid.FriendlyName == "Key Usage")
                            {
                                X509KeyUsageExtension ext = (X509KeyUsageExtension)extension;
                                if ((ext.KeyUsages & X509KeyUsageFlags.DataEncipherment) != X509KeyUsageFlags.None)
                                {
                                    keyDataEncipherment = true;

                                    if (!((message.SmimeEncryptionOptionFlags & SmimeEncryptionOptionFlags.RequireEnhancedKeyUsageofSecureEmail) > 0))
                                        break;
                                }
                            }
                            if (!enhancedKeySecureEmail && extension.Oid.FriendlyName == "Enhanced Key Usage")
                            {
                                X509EnhancedKeyUsageExtension ext = (X509EnhancedKeyUsageExtension)extension;
                                OidCollection oids = ext.EnhancedKeyUsages;
                                foreach (Oid oid in oids)
                                {
                                    if (oid.FriendlyName == "Secure Email")
                                    {
                                        enhancedKeySecureEmail = true;
                                        break;
                                    }
                                }
                            }
                        }
                        if ((message.SmimeEncryptionOptionFlags & SmimeEncryptionOptionFlags.RequireKeyUsageOfDataEncipherment) > 0 && !keyDataEncipherment)
                            continue;
                        if ((message.SmimeEncryptionOptionFlags & SmimeEncryptionOptionFlags.RequireEnhancedKeyUsageofSecureEmail) > 0 && !enhancedKeySecureEmail)
                            continue;
                    }

                    // If we've made it this far, we can use the certificate for a recipient.
                    MailAddress originalAddress = addressesNeedingPublicKeys[canonicalCertSubject];
                    SmimeCertificateCache.Add(canonicalCertSubject, cert);
                    addressesWithPublicKeys.Add(canonicalCertSubject);
                    addressesNeedingPublicKeys.Remove(canonicalCertSubject);

                    // Shortcut to abort processing of additional certificates if all recipients are accounted for.
                    if (addressesNeedingPublicKeys.Count < 1)
                        break;
                }
            }
        }
示例#15
0
        /// <summary>
        /// Check whether all recipients on the message have valid public keys and will be able to receive S/MIME encrypted envelopes.
        /// </summary>
        /// <param name="message">An OpaqueMail.MailMessage that contains the message to send.</param>
        public bool SmimeVerifyAllRecipientsHavePublicKeys(MailMessage message)
        {
            // Prepare recipient keys if this message will be encrypted.
            Dictionary<string, MailAddress> addressesNeedingPublicKeys;
            HashSet<string> addressesWithPublicKeys;
            ResolvePublicKeys(message, out addressesWithPublicKeys, out addressesNeedingPublicKeys);

            return addressesNeedingPublicKeys.Count < 1;
        }
示例#16
0
 /// <summary>
 /// Appends a message to the specified mailbox.
 /// </summary>
 /// <param name="mailboxName">The name of the mailbox to append to.</param>
 /// <param name="message">The raw message to append.</param>
 public async Task<bool> AppendMessageAsync(string mailboxName, MailMessage message)
 {
     return await AppendMessageAsync(mailboxName, message.RawMessage, message.RawFlags.ToArray(), message.Date);
 }
示例#17
0
        /// <summary>
        /// Helper function to retrieve a specific message from the server based on its index or UID, optionally returning only headers.
        /// </summary>
        /// <param name="index">The index number of the message to return.</param>
        /// <param name="uid">The UID of the message, as returned by a UIDL command.</param>
        /// <param name="headersOnly">Return only the message's headers when true; otherwise, return the message and body.</param>
        private async Task<MailMessage> GetMessageHelper(int index, string uid, bool headersOnly)
        {
            // Protect against commands being called out of order.
            if (!IsAuthenticated)
                throw new Pop3Exception("Must be connected to the server and authenticated prior to calling the RETR command.");

            bool processed = false;
            string response = "";

            // Determine whether we're using the index number or UID string.
            string messageID = index > -1 ? index.ToString() : uid;

            // If retrieving headers only, first try the POP3 TOP command.
            if (headersOnly && (ServerSupportsTop != false))
            {
                await SendCommandAsync("TOP " + messageID + " 0\r\n");
                response = await ReadDataAsync("\r\n.\r\n");

                if (LastCommandResult)
                    processed = true;
                ServerSupportsTop = processed;
            }
            if (!processed)
            {
                await SendCommandAsync("RETR " + messageID + "\r\n");
                response = await ReadDataAsync("\r\n.\r\n");
            }

            if (LastCommandResult && response.Length > 0)
            {
                MailMessage message = new MailMessage(response, ProcessingFlags);

                if (string.IsNullOrEmpty(uid) && ServerSupportsUIDL != null)
                {
                    message.Index = index;
                    message.Pop3Uidl = await GetUidlAsync(index);
                }
                else
                    message.Pop3Uidl = uid;

                return message;
            }

            // If unable to find or parse the message, return null.
            return null;
        }
示例#18
0
 /// <summary>
 /// Sends the specified message to an SMTP server for delivery.
 /// Performs requested S/MIME signing and encryption.
 /// </summary>
 /// <param name="message">An OpaqueMail.MailMessage that contains the message to send.</param>
 public void Send(MailMessage message)
 {
     Task.Run(() => SmimeSendRawAsync(message)).Wait();
 }