Example #1
0
        /// <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);
            }
        }
Example #2
0
        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);
        }
Example #3
0
        /// <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;
        }
Example #4
0
        /// <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 ();
        }
Example #5
0
        /// <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);
            }
        }