public override void Connect (string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default (CancellationToken))
{
if (host == null)
throw new ArgumentNullException (nameof (host));
if (host.Length == 0)
throw new ArgumentException ("The host name cannot be empty.", nameof (host));
if (port < 0 || port > 65535)
throw new ArgumentOutOfRangeException (nameof (port));
CheckDisposed ();
if (IsConnected)
throw new InvalidOperationException ("The ImapClient is already connected.");
Stream stream;
bool starttls;
Uri uri;
ComputeDefaultValues (host, ref port, ref options, out uri, out starttls);
#if !NETFX_CORE
#if COREFX
var ipAddresses = Dns.GetHostAddressesAsync (uri.DnsSafeHost).GetAwaiter ().GetResult ();
#else
var ipAddresses = Dns.GetHostAddresses (uri.DnsSafeHost);
#endif
Socket socket = null;
for (int i = 0; i < ipAddresses.Length; i++) {
socket = new Socket (ipAddresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp) {
ReceiveTimeout = timeout,
SendTimeout = timeout
};
try {
cancellationToken.ThrowIfCancellationRequested ();
if (LocalEndPoint != null)
socket.Bind (LocalEndPoint);
socket.Connect (ipAddresses[i], port);
break;
} catch (OperationCanceledException) {
socket.Dispose ();
throw;
} catch {
socket.Dispose ();
if (i + 1 == ipAddresses.Length)
throw;
}
}
if (socket == null)
throw new IOException (string.Format ("Failed to resolve host: {0}", host));
engine.Uri = uri;
if (options == SecureSocketOptions.SslOnConnect) {
var ssl = new SslStream (new NetworkStream (socket, true), false, ValidateRemoteCertificate);
try {
#if COREFX
ssl.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, true).GetAwaiter ().GetResult ();
#else
ssl.AuthenticateAsClient (host, ClientCertificates, SslProtocols, true);
#endif
} catch {
ssl.Dispose ();
throw;
}
secure = true;
stream = ssl;
} else {
stream = new NetworkStream (socket, true);
secure = false;
}
#else
var protection = options == SecureSocketOptions.SslOnConnect ? SocketProtectionLevel.Tls12 : SocketProtectionLevel.PlainSocket;
socket = new StreamSocket ();
try {
cancellationToken.ThrowIfCancellationRequested ();
socket.ConnectAsync (new HostName (host), port.ToString (), protection)
.AsTask (cancellationToken)
.GetAwaiter ()
.GetResult ();
} catch {
socket.Dispose ();
socket = null;
throw;
}
stream = new DuplexStream (socket.InputStream.AsStreamForRead (0), socket.OutputStream.AsStreamForWrite (0));
secure = options == SecureSocketOptions.SslOnConnect;
engine.Uri = uri;
#endif
if (stream.CanTimeout) {
stream.WriteTimeout = timeout;
stream.ReadTimeout = timeout;
}
ProtocolLogger.LogConnect (uri);
engine.Connect (new ImapStream (stream, socket, ProtocolLogger), cancellationToken);
try {
// Only query the CAPABILITIES if the greeting didn't include them.
if (engine.CapabilitiesVersion == 0)
engine.QueryCapabilities (cancellationToken);
if (options == SecureSocketOptions.StartTls && (engine.Capabilities & ImapCapabilities.StartTLS) == 0)
throw new NotSupportedException ("The IMAP server does not support the STARTTLS extension.");
if (starttls && (engine.Capabilities & ImapCapabilities.StartTLS) != 0) {
var ic = engine.QueueCommand (cancellationToken, null, "STARTTLS\r\n");
engine.Wait (ic);
ProcessResponseCodes (ic);
if (ic.Response == ImapCommandResponse.Ok) {
#if !NETFX_CORE
var tls = new SslStream (stream, false, ValidateRemoteCertificate);
#if COREFX
tls.AuthenticateAsClientAsync (host, ClientCertificates, SslProtocols, true).GetAwaiter ().GetResult ();
#else
tls.AuthenticateAsClient (host, ClientCertificates, SslProtocols, true);
#endif
engine.Stream.Stream = tls;
#else
socket.UpgradeToSslAsync (SocketProtectionLevel.Tls12, new HostName (host))
.AsTask (cancellationToken)
.GetAwaiter ()
.GetResult ();
#endif
secure = true;
// 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 (options == SecureSocketOptions.StartTls) {
throw ImapCommandException.Create ("STARTTLS", ic);
}
}
} catch {
engine.Disconnect ();
secure = false;
throw;
}
engine.Disconnected += OnEngineDisconnected;
OnConnected ();
}