public void TestNoQuery () { var uri = new Uri ("imap://imap.gmail.com/"); var query = uri.ParsedQuery (); Assert.AreEqual (0, query.Count, "Unexpected number of queries."); }
public void TestSimpleQuery () { var uri = new Uri ("imap://imap.gmail.com/?starttls=false"); var query = uri.ParsedQuery (); Assert.AreEqual (1, query.Count, "Unexpected number of queries."); Assert.AreEqual ("false", query["starttls"], "Unexpected value for 'starttls'."); }
/// <summary> /// Establish a connection to the specified mail server. /// </summary> /// <remarks> /// Establishes a connection to the specified mail server. /// </remarks> /// <example> /// <code language="c#" source="Examples\SmtpExamples.cs" region="SendMessageUri"/> /// </example> /// <param name="uri">The server URI.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// The <paramref name="uri"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentException"> /// The <paramref name="uri"/> is not an absolute URI. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="MailService"/> has been disposed. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The <see cref="MailService"/> is already connected. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ProtocolException"> /// A protocol error occurred. /// </exception> public void Connect (Uri uri, CancellationToken cancellationToken = default (CancellationToken)) { if (uri == null) throw new ArgumentNullException ("uri"); if (!uri.IsAbsoluteUri) throw new ArgumentException ("The uri must be absolute.", "uri"); var protocol = uri.Scheme.ToLowerInvariant (); var query = uri.ParsedQuery (); SecureSocketOptions options; string value; // Note: early versions of MailKit used "pop3" and "pop3s" if (protocol == "pop3s") protocol = "pops"; else if (protocol == "pop3") protocol = "pop"; if (protocol == Protocol + "s") { options = SecureSocketOptions.SslOnConnect; } else if (protocol != Protocol) { throw new ArgumentException ("Unknown URI scheme.", "uri"); } else if (query.TryGetValue ("starttls", out value)) { switch (value.ToLowerInvariant ()) { default: options = SecureSocketOptions.StartTlsWhenAvailable; break; case "always": case "true": case "yes": options = SecureSocketOptions.StartTls; break; case "never": case "false": case "no": options = SecureSocketOptions.None; break; } } else { options = SecureSocketOptions.StartTlsWhenAvailable; } Connect (uri.Host, uri.Port < 0 ? 0 : uri.Port, options, cancellationToken); }
/// <summary> /// Establishes a connection to the specified SMTP server. /// </summary> /// <remarks> /// <para>Establishes a connection to an SMTP or SMTP/S server. If the schema /// in the uri is "smtp", a clear-text connection is made and defaults to using /// port 25 if no port is specified in the URI. However, if the schema in the /// uri is "smtps", an SSL connection is made using the /// <see cref="ClientCertificates"/> and defaults to port 465 unless a port /// is specified in the URI.</para> /// <para>It should be noted that when using a clear-text SMTP connection, /// if the server advertizes support for the STARTTLS extension, the client /// will automatically switch into TLS mode before authenticating unless the /// <paramref name="uri"/> contains a query string to disable it.</para> /// If a successful connection is made, the <see cref="AuthenticationMechanisms"/> /// and <see cref="Capabilities"/> properties will be populated. /// </remarks> /// <param name="uri">The server URI. The <see cref="System.Uri.Scheme"/> should either /// be "smtp" to make a clear-text connection or "smtps" to make an SSL connection.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// <para>The <paramref name="uri"/> is <c>null</c>.</para> /// </exception> /// <exception cref="System.ArgumentException"> /// The <paramref name="uri"/> is not an absolute URI. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="SmtpClient"/> has been disposed. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The <see cref="SmtpClient"/> is already connected. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="SmtpCommandException"> /// An SMTP command failed. /// </exception> /// <exception cref="SmtpProtocolException"> /// An SMTP protocol error occurred. /// </exception> public void Connect(Uri uri, CancellationToken cancellationToken = default (CancellationToken)) { CheckDisposed (); if (uri == null) throw new ArgumentNullException ("uri"); if (!uri.IsAbsoluteUri) throw new ArgumentException ("The uri must be absolute.", "uri"); if (IsConnected) throw new InvalidOperationException ("The SmtpClient is already connected."); capabilities = SmtpCapabilities.None; authmechs.Clear (); MaxSize = 0; var smtps = uri.Scheme.ToLowerInvariant () == "smtps"; var port = uri.Port > 0 ? uri.Port : (smtps ? 465 : 25); var query = uri.ParsedQuery (); SmtpResponse response = null; string value; var starttls = !smtps && (!query.TryGetValue ("starttls", out value) || Convert.ToBoolean (value)); #if !NETFX_CORE var ipAddresses = Dns.GetHostAddresses (uri.DnsSafeHost); Socket socket = null; for (int i = 0; i < ipAddresses.Length; i++) { socket = new Socket (ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp); cancellationToken.ThrowIfCancellationRequested (); try { socket.Connect (ipAddresses[i], port); localEndPoint = socket.LocalEndPoint; break; } catch { if (i + 1 == ipAddresses.Length) throw; } } if (smtps) { var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate); ssl.AuthenticateAsClient (uri.Host, ClientCertificates, SslProtocols.Default, true); stream = ssl; } else { stream = new NetworkStream (socket, true); } #else socket = new StreamSocket (); cancellationToken.ThrowIfCancellationRequested (); socket.ConnectAsync (new HostName (uri.DnsSafeHost), port.ToString (), smtps ? SocketProtectionLevel.Tls12 : SocketProtectionLevel.PlainSocket) .AsTask (cancellationToken) .GetAwaiter () .GetResult (); stream = new DuplexStream (socket.InputStream.AsStreamForRead (), socket.OutputStream.AsStreamForWrite ()); #endif if (stream.CanTimeout) { stream.WriteTimeout = timeout; stream.ReadTimeout = timeout; } host = uri.Host; logger.LogConnect (uri); try { // read the greeting response = ReadResponse (cancellationToken); if (response.StatusCode != SmtpStatusCode.ServiceReady) throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); // Send EHLO and get a list of supported extensions Ehlo (cancellationToken); if (starttls && (capabilities & SmtpCapabilities.StartTLS) != 0) { response = SendCommand ("STARTTLS", cancellationToken); if (response.StatusCode != SmtpStatusCode.ServiceReady) throw new SmtpCommandException (SmtpErrorCode.UnexpectedStatusCode, response.StatusCode, response.Response); #if !NETFX_CORE var tls = new SslStream (stream, false, ValidateRemoteCertificate); tls.AuthenticateAsClient (uri.Host, ClientCertificates, SslProtocols.Tls, true); stream = tls; #else socket.UpgradeToSslAsync (SocketProtectionLevel.Tls12, new HostName (uri.DnsSafeHost)) .AsTask (cancellationToken) .GetAwaiter () .GetResult (); #endif // Send EHLO again and get the new list of supported extensions Ehlo (cancellationToken); } IsConnected = true; } catch { stream.Dispose (); stream = null; throw; } }
/// <summary> /// Establishes a connection to the specified POP3 server. /// </summary> /// <remarks> /// <para>Establishes a connection to an POP3 or POP3/S server. If the schema /// in the uri is "pop", a clear-text connection is made and defaults to using /// port 110 if no port is specified in the URI. However, if the schema in the /// uri is "pops", an SSL connection is made using the /// <see cref="ClientCertificates"/> and defaults to port 995 unless a port /// is specified in the URI.</para> /// <para>It should be noted that when using a clear-text POP3 connection, /// if the server advertizes support for the STLS extension, the client /// will automatically switch into TLS mode before authenticating unless /// the <paramref name="uri"/> contains a query string to disable it.</para> /// If a successful connection is made, the <see cref="AuthenticationMechanisms"/> /// and <see cref="Capabilities"/> properties will be populated. /// </remarks> /// <param name="uri">The server URI. The <see cref="System.Uri.Scheme"/> should either /// be "pop" to make a clear-text connection or "pops" to make an SSL connection.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// The <paramref name="uri"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentException"> /// The <paramref name="uri"/> is not an absolute URI. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="Pop3Client"/> has been disposed. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The <see cref="Pop3Client"/> is already connected. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="Pop3CommandException"> /// A POP3 command failed. /// </exception> /// <exception cref="Pop3ProtocolException"> /// A POP3 protocol error occurred. /// </exception> public void Connect(Uri uri, CancellationToken cancellationToken) { CheckDisposed (); if (uri == null) throw new ArgumentNullException ("uri"); if (!uri.IsAbsoluteUri) throw new ArgumentException ("The uri must be absolute.", "uri"); if (IsConnected) throw new InvalidOperationException ("The Pop3Client is already connected."); var scheme = uri.Scheme.ToLowerInvariant (); var pops = scheme == "pops" || scheme == "pop3s"; var port = uri.Port > 0 ? uri.Port : (pops ? 995 : 110); var query = uri.ParsedQuery (); #if !NETFX_CORE && !WINDOWS_APP && !WINDOWS_PHONE_APP var ipAddresses = Dns.GetHostAddresses (uri.DnsSafeHost); Socket socket = null; #endif Stream stream; string value; var starttls = !pops && (!query.TryGetValue ("starttls", out value) || Convert.ToBoolean (value)); #if !NETFX_CORE && !WINDOWS_APP && !WINDOWS_PHONE_APP for (int i = 0; i < ipAddresses.Length; i++) { socket = new Socket (ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp); cancellationToken.ThrowIfCancellationRequested (); try { socket.Connect (ipAddresses[i], port); break; } catch (Exception) { if (i + 1 == ipAddresses.Length) throw; } } if (pops) { var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate); ssl.AuthenticateAsClient (uri.Host, ClientCertificates, SslProtocols.Default, true); stream = ssl; } else { stream = new NetworkStream (socket, true); } #else socket = new StreamSocket (); cancellationToken.ThrowIfCancellationRequested (); socket.ConnectAsync (new HostName (uri.DnsSafeHost), port.ToString (), pops ? SocketProtectionLevel.Tls12 : SocketProtectionLevel.PlainSocket) .AsTask (cancellationToken) .GetAwaiter () .GetResult (); stream = new DuplexStream (socket.InputStream.AsStreamForRead (), socket.OutputStream.AsStreamForWrite ()); #endif probed = ProbedCapabilities.None; host = uri.Host; logger.LogConnect (uri); engine.Connect (new Pop3Stream (stream, logger), cancellationToken); engine.QueryCapabilities (cancellationToken); if (starttls && (engine.Capabilities & Pop3Capabilities.StartTLS) != 0) { SendCommand (cancellationToken, "STLS"); #if !NETFX_CORE && !WINDOWS_APP && !WINDOWS_PHONE_APP var tls = new SslStream (stream, false, ValidateRemoteCertificate); tls.AuthenticateAsClient (uri.Host, ClientCertificates, SslProtocols.Tls, true); engine.Stream.Stream = tls; #else socket.UpgradeToSslAsync (SocketProtectionLevel.Tls12, new HostName (uri.DnsSafeHost)) .AsTask (cancellationToken) .GetAwaiter () .GetResult (); #endif // re-issue a CAPA command engine.QueryCapabilities (cancellationToken); } }
/// <summary> /// Establishes a connection to the specified IMAP server. /// </summary> /// <remarks> /// <para>Establishes a connection to an IMAP or IMAP/S server. If the schema /// in the uri is "imap", a clear-text connection is made and defaults to using /// port 143 if no port is specified in the URI. However, if the schema in the /// uri is "imaps", an SSL connection is made using the /// <see cref="ClientCertificates"/> and defaults to port 993 unless a port /// is specified in the URI.</para> /// <para>It should be noted that when using a clear-text IMAP connection, /// if the server advertizes support for the STARTTLS extension, the client /// will automatically switch into TLS mode before authenticating unless the /// <paramref name="uri"/> contains a query string to disable it.</para> /// <para>If the IMAP server advertizes the COMPRESS extension and either does not /// support the STARTTLS extension or the <paramref name="uri"/> explicitly disabled /// the use of the STARTTLS extension, then the client will automatically opt into /// using a compressed data connection to optimize bandwidth usage unless the /// <paramref name="uri"/> contains a query string to explicitly disable it.</para> /// <para>If a successful connection is made, the <see cref="AuthenticationMechanisms"/> /// and <see cref="Capabilities"/> properties will be populated.</para> /// </remarks> /// <param name="uri">The server URI. The <see cref="System.Uri.Scheme"/> should either /// be "imap" to make a clear-text connection or "imaps" to make an SSL connection.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// The <paramref name="uri"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentException"> /// The <paramref name="uri"/> is not an absolute URI. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The <see cref="ImapClient"/> is already connected. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// An IMAP protocol error occurred. /// </exception> public void Connect(Uri uri, CancellationToken cancellationToken) { CheckDisposed (); if (uri == null) throw new ArgumentNullException ("uri"); if (!uri.IsAbsoluteUri) throw new ArgumentException ("The uri must be absolute.", "uri"); if (IsConnected) throw new InvalidOperationException ("The ImapClient is already connected."); var imaps = uri.Scheme.ToLowerInvariant () == "imaps"; var port = uri.Port > 0 ? uri.Port : (imaps ? 993 : 143); var query = uri.ParsedQuery (); Stream stream; string value; var starttls = !imaps && (!query.TryGetValue ("starttls", out value) || Convert.ToBoolean (value)); var compress = !imaps && (!query.TryGetValue ("compress", out value) || Convert.ToBoolean (value)); #if !NETFX_CORE var ipAddresses = Dns.GetHostAddresses (uri.DnsSafeHost); Socket socket = null; for (int i = 0; i < ipAddresses.Length; i++) { socket = new Socket (ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp); cancellationToken.ThrowIfCancellationRequested (); try { socket.Connect (ipAddresses[i], port); break; } catch { if (i + 1 == ipAddresses.Length) throw; } } if (imaps) { var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate); ssl.AuthenticateAsClient (uri.Host, ClientCertificates, SslProtocols.Default, true); stream = ssl; } else { stream = new NetworkStream (socket, true); } #else socket = new StreamSocket (); cancellationToken.ThrowIfCancellationRequested (); socket.ConnectAsync (new HostName (uri.DnsSafeHost), port.ToString (), imaps ? SocketProtectionLevel.Tls12 : SocketProtectionLevel.PlainSocket) .AsTask (cancellationToken) .GetAwaiter () .GetResult (); stream = new DuplexStream (socket.InputStream.AsStreamForRead (), socket.OutputStream.AsStreamForWrite ()); #endif host = uri.Host; logger.LogConnect (uri); engine.Connect (new ImapStream (stream, logger), cancellationToken); // Only query the CAPABILITIES if the greeting didn't include them. if (engine.CapabilitiesVersion == 0) engine.QueryCapabilities (cancellationToken); if (starttls && (engine.Capabilities & ImapCapabilities.StartTLS) != 0) { var ic = engine.QueueCommand (cancellationToken, null, "STARTTLS\r\n"); engine.Wait (ic); if (ic.Result == ImapCommandResult.Ok) { #if !NETFX_CORE var tls = new SslStream (stream, false, ValidateRemoteCertificate); tls.AuthenticateAsClient (uri.Host, ClientCertificates, SslProtocols.Tls, true); engine.Stream.Stream = tls; #else socket.UpgradeToSslAsync (SocketProtectionLevel.Tls12, new HostName (uri.DnsSafeHost)) .AsTask (cancellationToken) .GetAwaiter () .GetResult (); #endif // Query the CAPABILITIES again if the server did not include an // untagged CAPABILITIES response to the STARTTLS command. if (engine.CapabilitiesVersion == 1) engine.QueryCapabilities (cancellationToken); } } else if (compress && (engine.Capabilities & ImapCapabilities.Compress) != 0) { var ic = engine.QueueCommand (cancellationToken, null, "COMPRESS DEFLATE\r\n"); engine.Wait (ic); if (ic.Result == ImapCommandResult.Ok) { var unzip = new DeflateStream (stream, CompressionMode.Decompress); var zip = new DeflateStream (stream, CompressionMode.Compress); engine.Stream.Stream = new DuplexStream (unzip, zip); // Query the CAPABILITIES again if the server did not include an // untagged CAPABILITIES response to the COMPRESS command. if (engine.CapabilitiesVersion == 1) engine.QueryCapabilities (cancellationToken); } } }
/// <summary> /// Establishes a connection to the specified IMAP server. /// </summary> /// <remarks> /// <para>Establishes a connection to an IMAP or IMAP/S server. If the schema /// in the uri is "imap", a clear-text connection is made and defaults to using /// port 143 if no port is specified in the URI. However, if the schema in the /// uri is "imaps", an SSL connection is made using the /// <see cref="ClientCertificates"/> and defaults to port 993 unless a port /// is specified in the URI.</para> /// <para>It should be noted that when using a clear-text IMAP connection, /// if the server advertizes support for the STARTTLS extension, the client /// will automatically switch into TLS mode before authenticating unless the /// <paramref name="uri"/> contains a query string to disable it.</para> /// <para>If the IMAP server advertizes the COMPRESS extension and either does not /// support the STARTTLS extension or the <paramref name="uri"/> explicitly disabled /// the use of the STARTTLS extension, then the client will automatically opt into /// using a compressed data connection to optimize bandwidth usage unless the /// <paramref name="uri"/> contains a query string to explicitly disable it.</para> /// <para>If a successful connection is made, the <see cref="AuthenticationMechanisms"/> /// and <see cref="Capabilities"/> properties will be populated.</para> /// </remarks> /// <param name="uri">The server URI. The <see cref="System.Uri.Scheme"/> should either /// be "imap" to make a clear-text connection or "imaps" to make an SSL connection.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// The <paramref name="uri"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentException"> /// The <paramref name="uri"/> is not an absolute URI. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="ImapClient"/> has been disposed. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The <see cref="ImapClient"/> is already connected. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="ImapProtocolException"> /// An IMAP protocol error occurred. /// </exception> public void Connect(Uri uri, CancellationToken cancellationToken) { CheckDisposed (); if (uri == null) throw new ArgumentNullException ("uri"); if (!uri.IsAbsoluteUri) throw new ArgumentException ("The uri must be absolute.", "uri"); if (IsConnected) throw new InvalidOperationException ("The ImapClient is already connected."); bool imaps = uri.Scheme.ToLowerInvariant () == "imaps"; int port = uri.Port > 0 ? uri.Port : (imaps ? 993 : 143); var ipAddresses = Dns.GetHostAddresses (uri.DnsSafeHost); var query = uri.ParsedQuery (); Socket socket = null; Stream stream; string value; var starttls = !imaps && (!query.TryGetValue ("starttls", out value) || Convert.ToBoolean (value)); var compress = !imaps && (!query.TryGetValue ("compress", out value) || Convert.ToBoolean (value)); for (int i = 0; i < ipAddresses.Length; i++) { socket = new Socket (ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp); cancellationToken.ThrowIfCancellationRequested (); try { socket.Connect (ipAddresses[i], port); break; } catch (Exception) { if (i + 1 == ipAddresses.Length) throw; } } if (imaps) { var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate); ssl.AuthenticateAsClient (uri.Host, ClientCertificates, SslProtocols.Default, true); stream = ssl; } else { stream = new NetworkStream (socket, true); } host = uri.Host; logger.LogConnect (uri); engine.Connect (new ImapStream (stream, logger), cancellationToken); // Only query the CAPABILITIES if the greeting didn't include them. if (engine.CapabilitiesVersion == 0) engine.QueryCapabilities (cancellationToken); if (!starttls) engine.Capabilities &= ~ImapCapabilities.StartTLS; if (!compress) engine.Capabilities &= ~ImapCapabilities.Compress; }
/// <summary> /// Establishes a connection to the specified POP3 server. /// </summary> /// <remarks> /// <para>Establishes a connection to an POP3 or POP3/S server. If the schema /// in the uri is "pop", a clear-text connection is made and defaults to using /// port 110 if no port is specified in the URI. However, if the schema in the /// uri is "pops", an SSL connection is made using the /// <see cref="ClientCertificates"/> and defaults to port 995 unless a port /// is specified in the URI.</para> /// <para>It should be noted that when using a clear-text POP3 connection, /// if the server advertizes support for the STLS extension, the client /// will automatically switch into TLS mode before authenticating unless /// the <paramref name="uri"/> contains a query string to disable it.</para> /// If a successful connection is made, the <see cref="AuthenticationMechanisms"/> /// and <see cref="Capabilities"/> properties will be populated. /// </remarks> /// <param name="uri">The server URI. The <see cref="System.Uri.Scheme"/> should either /// be "pop" to make a clear-text connection or "pops" to make an SSL connection.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <exception cref="System.ArgumentNullException"> /// The <paramref name="uri"/> is <c>null</c>. /// </exception> /// <exception cref="System.ArgumentException"> /// The <paramref name="uri"/> is not an absolute URI. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="Pop3Client"/> has been disposed. /// </exception> /// <exception cref="System.InvalidOperationException"> /// The <see cref="Pop3Client"/> is already connected. /// </exception> /// <exception cref="System.OperationCanceledException"> /// The operation was canceled via the cancellation token. /// </exception> /// <exception cref="System.IO.IOException"> /// An I/O error occurred. /// </exception> /// <exception cref="Pop3CommandException"> /// A POP3 command failed. /// </exception> /// <exception cref="Pop3ProtocolException"> /// A POP3 protocol error occurred. /// </exception> public void Connect(Uri uri, CancellationToken cancellationToken) { CheckDisposed (); if (uri == null) throw new ArgumentNullException ("uri"); if (!uri.IsAbsoluteUri) throw new ArgumentException ("The uri must be absolute.", "uri"); if (IsConnected) throw new InvalidOperationException ("The Pop3Client is already connected."); var scheme = uri.Scheme.ToLowerInvariant (); bool pops = scheme == "pops" || scheme == "pop3s"; int port = uri.Port > 0 ? uri.Port : (pops ? 995 : 110); var ipAddresses = Dns.GetHostAddresses (uri.DnsSafeHost); var query = uri.ParsedQuery (); Socket socket = null; Stream stream; string value; var starttls = !pops && (!query.TryGetValue ("starttls", out value) || Convert.ToBoolean (value)); for (int i = 0; i < ipAddresses.Length; i++) { socket = new Socket (ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp); cancellationToken.ThrowIfCancellationRequested (); try { socket.Connect (ipAddresses[i], port); break; } catch (Exception) { if (i + 1 == ipAddresses.Length) throw; } } if (pops) { var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate); ssl.AuthenticateAsClient (uri.Host, ClientCertificates, SslProtocols.Default, true); stream = ssl; } else { stream = new NetworkStream (socket, true); } probed = ProbedCapabilities.None; host = uri.Host; logger.LogConnect (uri); engine.Connect (new Pop3Stream (stream, logger), cancellationToken); engine.QueryCapabilities (cancellationToken); if (starttls && (engine.Capabilities & Pop3Capabilities.StartTLS) != 0) { SendCommand (cancellationToken, "STLS"); var tls = new SslStream (stream, false, ValidateRemoteCertificate); tls.AuthenticateAsClient (uri.Host, ClientCertificates, SslProtocols.Tls, true); engine.Stream.Stream = tls; // re-issue a CAPA command engine.QueryCapabilities (cancellationToken); } }