/// <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); } }
internal void ReplayConnect(string hostName, Stream replayStream, CancellationToken cancellationToken) { CheckDisposed (); if (hostName == null) throw new ArgumentNullException ("hostName"); if (replayStream == null) throw new ArgumentNullException ("replayStream"); probed = ProbedCapabilities.None; host = hostName; engine.Connect (new Pop3Stream (replayStream, logger), cancellationToken); engine.QueryCapabilities (cancellationToken); }
/// <summary> /// Gets the UID of the message at the specified index. /// </summary> /// <returns>The message UID.</returns> /// <param name="index">The message index.</param> /// <param name="cancellationToken">A cancellation token.</param> /// <exception cref="System.ArgumentOutOfRangeException"> /// <paramref name="index"/> is not a valid message index. /// </exception> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="Pop3Client"/> has been disposed. /// </exception> /// <exception cref="InvalidOperationException"> /// The <see cref="Pop3Client"/> is not connected. /// </exception> /// <exception cref="System.UnauthorizedAccessException"> /// The <see cref="Pop3Client"/> is not authenticated. /// </exception> /// <exception cref="System.NotSupportedException"> /// The POP3 server does not support the UIDL extension. /// </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"> /// The POP3 command failed. /// </exception> /// <exception cref="Pop3ProtocolException"> /// A POP3 protocol error occurred. /// </exception> public string GetMessageUid(int index, CancellationToken cancellationToken) { CheckDisposed (); CheckConnected (); if (engine.State != Pop3EngineState.Transaction) throw new UnauthorizedAccessException (); if (!SupportsUids && (probed & ProbedCapabilities.UIDL) != 0) throw new NotSupportedException ("The POP3 server does not support the UIDL extension."); if (index < 0 || index >= count) throw new ArgumentOutOfRangeException ("index"); string uid = null; var pc = engine.QueueCommand (cancellationToken, (pop3, cmd, text) => { if (cmd.Status != Pop3CommandStatus.Ok) return; // the response should be "<seqid> <uid>" var tokens = text.Split (new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries); int seqid; if (tokens.Length < 2) { cmd.Exception = CreatePop3ParseException ("Pop3 server returned an incomplete response to the UIDL command."); return; } if (!int.TryParse (tokens[0], out seqid) || seqid < 1) { cmd.Exception = CreatePop3ParseException ("Pop3 server returned an unexpected response to the UIDL command."); return; } if (seqid != index + 1) { cmd.Exception = CreatePop3ParseException ("Pop3 server returned the UID for the wrong message."); return; } uid = tokens[1]; }, "UIDL {0}", index + 1); while (engine.Iterate () < pc.Id) { // continue processing commands } probed |= ProbedCapabilities.UIDL; if (pc.Status != Pop3CommandStatus.Ok) { if (!SupportsUids) throw new NotSupportedException ("The POP3 server does not support the UIDL extension."); throw CreatePop3Exception (pc); } if (pc.Exception != null) throw pc.Exception; engine.Capabilities |= Pop3Capabilities.UIDL; uids[uid] = index + 1; return uid; }
/// <summary> /// Gets the full list of available message UIDs. /// </summary> /// <returns>The message uids.</returns> /// <param name="cancellationToken">A cancellation token.</param> /// <exception cref="System.ObjectDisposedException"> /// The <see cref="Pop3Client"/> has been disposed. /// </exception> /// <exception cref="InvalidOperationException"> /// The <see cref="Pop3Client"/> is not connected. /// </exception> /// <exception cref="System.UnauthorizedAccessException"> /// The <see cref="Pop3Client"/> is not authenticated. /// </exception> /// <exception cref="System.NotSupportedException"> /// The POP3 server does not support the UIDL extension. /// </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"> /// The POP3 command failed. /// </exception> /// <exception cref="Pop3ProtocolException"> /// A POP3 protocol error occurred. /// </exception> public string[] GetMessageUids(CancellationToken cancellationToken) { CheckDisposed (); CheckConnected (); if (engine.State != Pop3EngineState.Transaction) throw new UnauthorizedAccessException (); if (!SupportsUids && (probed & ProbedCapabilities.UIDL) != 0) throw new NotSupportedException ("The POP3 server does not support the UIDL extension."); uids.Clear (); var pc = engine.QueueCommand (cancellationToken, (pop3, cmd, text) => { if (cmd.Status != Pop3CommandStatus.Ok) return; do { var response = engine.ReadLine (cmd.CancellationToken).TrimEnd (); if (response == ".") break; if (cmd.Exception != null) continue; var tokens = response.Split (new [] { ' ' }, StringSplitOptions.RemoveEmptyEntries); int seqid; if (tokens.Length < 2) { cmd.Exception = CreatePop3ParseException ("Pop3 server returned an incomplete response to the UIDL command."); continue; } if (!int.TryParse (tokens[0], out seqid)) { cmd.Exception = CreatePop3ParseException ("Pop3 server returned an invalid response to the UIDL command."); continue; } uids.Add (tokens[1], seqid); } while (true); }, "UIDL"); while (engine.Iterate () < pc.Id) { // continue processing commands } probed |= ProbedCapabilities.UIDL; if (pc.Status != Pop3CommandStatus.Ok) { if (!SupportsUids) throw new NotSupportedException ("The POP3 server does not support the UIDL extension."); throw CreatePop3Exception (pc); } if (pc.Exception != null) throw pc.Exception; engine.Capabilities |= Pop3Capabilities.UIDL; return uids.Keys.ToArray (); }
/// <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 "pop3", 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 "pop3s", 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.</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 "pop3" to make a clear-text connection or "pop3s" 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.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 (IsConnected) throw new InvalidOperationException ("The Pop3Client is already connected."); bool pop3s = uri.Scheme.ToLowerInvariant () == "pop3s"; int port = uri.Port > 0 ? uri.Port : (pop3s ? 995 : 110); var ipAddresses = Dns.GetHostAddresses (uri.DnsSafeHost); Socket socket = null; Stream stream; 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 (pop3s) { 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 (!pop3s && engine.Capabilities.HasFlag (Pop3Capabilities.StartTLS)) { 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); } }