예제 #1
0
        public static MailAddressCollection Parse(string addresses)
        {
            // Escape embedded encoding.
            addresses = Functions.DecodeMailHeader(addresses);

            // Create a new collection of MailAddresses to be returned.
            MailAddressCollection addressCollection = new MailAddressCollection();

            int    cursor = 0, lastCursor = 0;
            string displayName = "";

            while (cursor < addresses.Length)
            {
                int quoteCursor = addresses.IndexOf("\"", cursor, StringComparison.Ordinal);
                if (quoteCursor == -1)
                {
                    quoteCursor = addresses.Length + 1;
                }
                int aposCursor = addresses.IndexOf("'", cursor, StringComparison.Ordinal);
                if (aposCursor == -1)
                {
                    aposCursor = addresses.Length + 1;
                }
                int angleCursor = addresses.IndexOf("<", cursor, StringComparison.Ordinal);
                if (angleCursor == -1)
                {
                    angleCursor = addresses.Length + 1;
                }
                int bracketCursor = addresses.IndexOf("[", cursor, StringComparison.Ordinal);
                if (bracketCursor == -1)
                {
                    bracketCursor = addresses.Length + 1;
                }
                int parenthesisCursor = addresses.IndexOf("(", cursor, StringComparison.Ordinal);
                if (parenthesisCursor == -1)
                {
                    parenthesisCursor = addresses.Length + 1;
                }
                int commaCursor = addresses.IndexOf(",", cursor, StringComparison.Ordinal);
                if (commaCursor == -1)
                {
                    commaCursor = addresses.Length + 1;
                }
                int semicolonCursor = addresses.IndexOf(";", cursor, StringComparison.Ordinal);
                if (semicolonCursor == -1)
                {
                    semicolonCursor = addresses.Length + 1;
                }

                bool processed = false;
                if (quoteCursor < aposCursor && quoteCursor < angleCursor && quoteCursor < bracketCursor && quoteCursor < parenthesisCursor && quoteCursor < commaCursor && quoteCursor < semicolonCursor)
                {
                    // The address display name is enclosed in quotes.
                    int endQuoteCursor = addresses.IndexOf("\"", quoteCursor + 1, StringComparison.Ordinal);
                    if (endQuoteCursor > -1)
                    {
                        displayName = addresses.Substring(quoteCursor + 1, endQuoteCursor - quoteCursor - 1).Replace("\\(", "(").Replace("\\)", ")");
                        cursor      = endQuoteCursor + 1;
                    }
                    else
                    {
                        cursor = addresses.Length;
                    }

                    processed = true;
                }
                else if (aposCursor < angleCursor && aposCursor < bracketCursor && aposCursor < parenthesisCursor && aposCursor < commaCursor && aposCursor < semicolonCursor)
                {
                    // The address display name may be enclosed in apostrophes.
                    int endAposCursor = addresses.IndexOf("'", aposCursor + 1, StringComparison.Ordinal);
                    if (endAposCursor > -1)
                    {
                        displayName = addresses.Substring(aposCursor + 1, endAposCursor - aposCursor - 1).Replace("\\(", "(").Replace("\\)", ")");
                        cursor      = endAposCursor + 1;
                    }
                    else
                    {
                        // The address contains an apostophe, but it's not enclosed in apostrophes.
                        angleCursor = addresses.IndexOf("<", cursor, StringComparison.Ordinal);
                        if (angleCursor == -1)
                        {
                            angleCursor = addresses.Length + 1;
                        }
                        bracketCursor = addresses.IndexOf("[", cursor, StringComparison.Ordinal);
                        if (bracketCursor == -1)
                        {
                            bracketCursor = addresses.Length + 1;
                        }

                        if (angleCursor < bracketCursor)
                        {
                            displayName = addresses.Substring(lastCursor, angleCursor - lastCursor).Trim().Replace("\\(", "(").Replace("\\)", ")");
                            cursor      = angleCursor;
                        }
                        else if (bracketCursor > -1)
                        {
                            displayName = addresses.Substring(lastCursor, bracketCursor - lastCursor).Trim().Replace("\\(", "(").Replace("\\)", ")");
                            cursor      = angleCursor;
                        }
                        else
                        {
                            cursor = addresses.Length;
                        }
                    }

                    processed = true;
                }
                else if (angleCursor < bracketCursor && angleCursor < parenthesisCursor && angleCursor < commaCursor && angleCursor < semicolonCursor)
                {
                    // The address is enclosed in angle brackets.
                    int endAngleCursor = addresses.IndexOf(">", angleCursor + 1, StringComparison.Ordinal);
                    if (endAngleCursor > -1)
                    {
                        // If we didn't find a display name between quotes or apostrophes, look at all characters prior to the angle bracket.
                        if (displayName.Length < 1)
                        {
                            displayName = addresses.Substring(lastCursor, angleCursor - lastCursor).Trim().Replace("\\(", "(").Replace("\\)", ")");
                        }

                        string address = addresses.Substring(angleCursor + 1, endAngleCursor - angleCursor - 1);
                        addressCollection.Add(new MailAddress(address, displayName));

                        displayName = "";
                        cursor      = endAngleCursor + 1;
                    }
                    else
                    {
                        cursor = addresses.Length;
                    }

                    processed = true;
                }
                else if (bracketCursor < parenthesisCursor && bracketCursor < commaCursor && bracketCursor < semicolonCursor)
                {
                    // The address is enclosed in brackets.
                    int endBracketCursor = addresses.IndexOf("]", bracketCursor + 1, StringComparison.Ordinal);
                    if (endBracketCursor > -1)
                    {
                        // If we didn't find a display name between quotes or apostrophes, look at all characters prior to the bracket.
                        if (displayName.Length < 1)
                        {
                            displayName = addresses.Substring(lastCursor, bracketCursor - lastCursor).Trim().Replace("\\(", "(").Replace("\\)", ")");
                        }

                        string address = addresses.Substring(bracketCursor + 1, endBracketCursor - bracketCursor - 1);
                        if (displayName.Length > 0)
                        {
                            addressCollection.Add(new MailAddress(address, displayName));
                        }
                        else
                        {
                            addressCollection.Add(new MailAddress(address));
                        }

                        displayName = "";
                        cursor      = endBracketCursor + 1;

                        processed = true;
                    }
                    else
                    {
                        cursor = addresses.Length;
                    }
                }
                else if (parenthesisCursor < commaCursor && parenthesisCursor < semicolonCursor)
                {
                    if ((parenthesisCursor == 0) || (addresses[parenthesisCursor - 1] != '\\'))
                    {
                        // The display name is enclosed in parentheses.
                        int endParenthesisCursor = 0;
                        while (endParenthesisCursor > -1)
                        {
                            endParenthesisCursor = addresses.IndexOf(")", parenthesisCursor + 1, StringComparison.Ordinal);
                            if (endParenthesisCursor > 0)
                            {
                                if (addresses[endParenthesisCursor - 1] != '\\')
                                {
                                    break;
                                }
                            }
                            else
                            {
                                break;
                            }
                        }

                        string address = addresses.Substring(lastCursor, parenthesisCursor - lastCursor).Trim();
                        displayName = addresses.Substring(parenthesisCursor + 1, endParenthesisCursor - parenthesisCursor - 1).Replace("\\(", "(").Replace("\\)", ")");
                        addressCollection.Add(new MailAddress(address, displayName));

                        cursor = parenthesisCursor + 1;

                        processed = true;
                    }
                }

                if (!processed)
                {
                    if (commaCursor < semicolonCursor)
                    {
                        if (commaCursor > lastCursor)
                        {
                            // We've found the next address, delimited by a comma.
                            string address = addresses.Substring(cursor, commaCursor - cursor).Trim();
                            addressCollection.Add(new MailAddress(address));
                        }

                        cursor = commaCursor + 1;
                    }
                    else if (semicolonCursor < addresses.Length)
                    {
                        if (semicolonCursor > lastCursor)
                        {
                            // We've found the next address, delimited by a semicolon.
                            string address = addresses.Substring(cursor, semicolonCursor - cursor).Trim();
                            addressCollection.Add(new MailAddress(address));
                        }

                        cursor = semicolonCursor + 1;
                    }
                    else
                    {
                        // Process any remaining address.
                        string address = addresses.Substring(cursor).Trim();
                        addressCollection.Add(new MailAddress(address));

                        cursor = addresses.Length;
                    }
                }

                lastCursor = cursor;
            }

            // If no encoded email address was parsed, try adding the entire string.
            if (addressCollection.Count < 1)
            {
                addressCollection.Add(addresses);
            }

            return(addressCollection);
        }
예제 #2
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 SmimeResolvePublicKeys(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;
                    }
                }
            }
        }