private void m_pTimerNoop_Elapsed(object sender, ElapsedEventArgs e)
        {
            try
            {
                lock (this.LockSynchronizer)
                {
                    if (this.TCP_Client.IsConnected)
                    {
                        this.m_pClient.TcpStream.WriteLine("NOOP");
                        SmartStream.ReadLineAsyncOP op = new SmartStream.ReadLineAsyncOP(new byte[8000], SizeExceededAction.JunkAndThrowException);
                        op.Completed += Op_Completed;

                        if (!this.m_pClient.TcpStream.ReadLine(op, false))
                        {
                            Op_Completed(op, new EventArgs <SmartStream.ReadLineAsyncOP>(op));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this.Disconnect();
                throw new Exception(ex.Message);
            }
        }
Exemple #2
0
        /// <summary>
        /// Reads line from TCP client.
        /// </summary>
        /// <returns></returns>
        internal string ReadLine()
        {
            SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[8000], SizeExceededAction.JunkAndThrowException);
            this.TcpClient.TcpStream.ReadLine(readLineOP, false);

            return(readLineOP.LineUtf8);
        }
Exemple #3
0
        /// <summary>
        /// Starts reading incoming command from the connected client.
        /// </summary>
        private void BeginReadCmd()
        {
            if (this.IsDisposed)
            {
                return;
            }

            try
            {
                SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[32000], SizeExceededAction.JunkAndThrowException);
                // This event is raised only if read period-terminated opeartion completes asynchronously.
                readLineOP.Completed += new EventHandler <EventArgs <SmartStream.ReadLineAsyncOP> >(delegate(object sender, EventArgs <SmartStream.ReadLineAsyncOP> e)
                {
                    if (ProcessCmd(readLineOP))
                    {
                        BeginReadCmd();
                    }
                });
                // Process incoming commands while, command reading completes synchronously.
                while (this.TcpStream.ReadLine(readLineOP, true))
                {
                    if (!ProcessCmd(readLineOP))
                    {
                        break;
                    }
                }
            }
            catch (Exception x)
            {
                OnError(x);
            }
        }
 internal string ReadLine()
 {
     if (!this.TCP_Client.IsConnected)
     {
         throw new Exception("You must connect first");
     }
     SmartStream.ReadLineAsyncOP readLineAsyncTask = new SmartStream.ReadLineAsyncOP(new byte[8000], SizeExceededAction.JunkAndThrowException);
     this.TCP_Client.TcpStream.ReadLine(readLineAsyncTask, false);
     return(readLineAsyncTask.LineUtf8);
 }
Exemple #5
0
                /// <summary>
                /// Assigns data line info from rea line operation.
                /// </summary>
                /// <param name="op">Read line operation.</param>
                /// <exception cref="ArgumentNullException">Is raised when <b>op</b> is null reference.</exception>
                public void AssignFrom(SmartStream.ReadLineAsyncOP op)
                {
                    if (op == null)
                    {
                        throw new ArgumentNullException();
                    }

                    m_BytesInBuffer = op.BytesInBuffer;
                    Array.Copy(op.Buffer, m_pLineBuffer, op.BytesInBuffer);
                }
Exemple #6
0
            /// <summary>
            /// Default constructor.
            /// </summary>
            /// <param name="stream">Stream from where to read body part.</param>
            /// <param name="boundary">Boundry ID what separates body parts.</param>
            /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> or <b>boundary</b> is null reference.</exception>
            public _MultipartReader(SmartStream stream, string boundary)
            {
                if (stream == null)
                {
                    throw new ArgumentNullException("stream");
                }
                if (boundary == null)
                {
                    throw new ArgumentNullException("boundary");
                }

                m_pStream  = stream;
                m_Boundary = boundary;

                m_pReadLineOP   = new SmartStream.ReadLineAsyncOP(new byte[32000], SizeExceededAction.ThrowException);
                m_pTextPreamble = new StringBuilder();
                m_pTextEpilogue = new StringBuilder();
            }
Exemple #7
0
        private void m_pTimerNoop_Elapsed(object sender, ElapsedEventArgs e)
        {
            try{
                /* Noop
                 * Responses:
                 +OK
                 */

                lock (this.LockSynchronizer){
                    m_pClient.TcpStream.WriteLine("NOOP");

                    SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[8000], SizeExceededAction.JunkAndThrowException);
                    m_pClient.TcpStream.ReadLine(readLineOP, false);
                }
            }
            catch {
            }
        }
Exemple #8
0
        /// <summary>
        /// Completes command reading operation.
        /// </summary>
        /// <param name="op">Operation.</param>
        /// <returns>Returns true if server should start reading next command.</returns>
        private bool ProcessCmd(SmartStream.ReadLineAsyncOP op)
        {
            bool readNextCommand = true;

            try
            {
                // We are already disposed.
                if (this.IsDisposed)
                {
                    return(false);
                }
                // Check errors.
                if (op.Error != null)
                {
                    OnError(op.Error);
                }
                // Remote host shut-down(Socket.ShutDown) socket.
                if (op.BytesInBuffer == 0)
                {
                    //LogAddText("The remote host '" + this.RemoteEndPoint.ToString() + "' shut down socket.");
                    Dispose();

                    return(false);
                }

                // Log.
                //if (this.Server.Logger != null)
                //{
                //    this.Server.Logger.AddRead(this.ID, this.AuthenticatedUserIdentity, op.BytesInBuffer, op.LineUtf8, this.LocalEndPoint, this.RemoteEndPoint);
                //}
                //if (op.LineBytesInBuffer > 23000) return readNextCommand;

                //if (op.LineBytesInBuffer > 5)
                OnPacketReceived(Encoding.UTF8.GetString(op.Buffer, 0, op.LineBytesInBuffer));
            }
            catch (Exception x)
            {
                OnError(x);
            }

            return(readNextCommand);
        }
Exemple #9
0
        /// <summary>
        /// Reads and logs specified line from connected host.
        /// </summary>
        /// <returns>Returns readed line.</returns>
        protected string ReadLine()
        {
            SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[32000], SizeExceededAction.JunkAndThrowException);
            this.TcpStream.ReadLine(args, false);
            if (args.Error != null)
            {
                throw args.Error;
            }
            string line = args.LineUtf8;

            if (args.BytesInBuffer > 0)
            {
                LogAddRead(args.BytesInBuffer, line);
            }
            else
            {
                LogAddText("Remote host closed connection.");
            }

            return(line);
        }
        /// <summary>
        /// Fetches specifes messages specified fetch items.
        /// </summary>
        /// <param name="sequence_set">IMAP sequence-set.</param>
        /// <param name="fetchFlags">Specifies what data to fetch from IMAP server.</param>
        /// <param name="setSeenFlag">If true message seen flag is setted.</param>
        /// <param name="uidFetch">Specifies if sequence-set contains message UIDs or message numbers.</param>
        /// <returns>Returns requested fetch items.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when IMAP client is not connected,not authenticated and folder not selected.</exception>
        public IMAP_FetchItem[] FetchMessages(IMAP_SequenceSet sequence_set,
                                              IMAP_FetchItem_Flags fetchFlags,
                                              bool setSeenFlag,
                                              bool uidFetch)
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (!IsConnected)
            {
                throw new InvalidOperationException("You must connect first.");
            }
            if (!IsAuthenticated)
            {
                throw new InvalidOperationException("The command is only valid in authenticated state.");
            }
            if (m_SelectedFolder.Length == 0)
            {
                throw new InvalidOperationException("The command is only valid in selected state.");
            }

            List<IMAP_FetchItem> fetchItems = new List<IMAP_FetchItem>();

            //--- Construct FETCH command line -----------------------------------------------------------------------//
            string fetchCmdLine = GetNextCmdTag();
            if (uidFetch)
            {
                fetchCmdLine += " UID";
            }
            fetchCmdLine += " FETCH " + sequence_set.ToSequenceSetString() + " (UID";

            // FLAGS
            if ((fetchFlags & IMAP_FetchItem_Flags.MessageFlags) != 0)
            {
                fetchCmdLine += " FLAGS";
            }
            // RFC822.SIZE
            if ((fetchFlags & IMAP_FetchItem_Flags.Size) != 0)
            {
                fetchCmdLine += " RFC822.SIZE";
            }
            // INTERNALDATE
            if ((fetchFlags & IMAP_FetchItem_Flags.InternalDate) != 0)
            {
                fetchCmdLine += " INTERNALDATE";
            }
            // ENVELOPE
            if ((fetchFlags & IMAP_FetchItem_Flags.Envelope) != 0)
            {
                fetchCmdLine += " ENVELOPE";
            }
            // BODYSTRUCTURE
            if ((fetchFlags & IMAP_FetchItem_Flags.BodyStructure) != 0)
            {
                fetchCmdLine += " BODYSTRUCTURE";
            }
            // BODY[] or BODY.PEEK[]
            if ((fetchFlags & IMAP_FetchItem_Flags.Message) != 0)
            {
                if (setSeenFlag)
                {
                    fetchCmdLine += " BODY[]";
                }
                else
                {
                    fetchCmdLine += " BODY.PEEK[]";
                }
            }
            // BODY[HEADER] or BODY.PEEK[HEADER] ---> This needed only if full message isn't requested.
            if ((fetchFlags & IMAP_FetchItem_Flags.Message) == 0 &&
                (fetchFlags & IMAP_FetchItem_Flags.Header) != 0)
            {
                if (setSeenFlag)
                {
                    fetchCmdLine += " BODY[HEADER]";
                }
                else
                {
                    fetchCmdLine += " BODY.PEEK[HEADER]";
                }
            }
            //--------------------------------------------------------------------------------------------------------//

            fetchCmdLine += ")";

            // Send fetch command line to server.
            int countWritten = TcpStream.WriteLine(fetchCmdLine);
            LogAddWrite(countWritten, fetchCmdLine);

            // Read un-tagged response lines while we get final response line.
            byte[] lineBuffer = new byte[Workaround.Definitions.MaxStreamLineLength];
            string line = "";
            while (true)
            {
                SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(lineBuffer,
                                                                                   SizeExceededAction.
                                                                                       JunkAndThrowException);
                TcpStream.ReadLine(args, false);
                if (args.Error != null)
                {
                    throw args.Error;
                }
                line = args.LineUtf8;
                LogAddRead(args.BytesInBuffer, line);

                // We have un-tagged resposne.
                if (line.StartsWith("*"))
                {
                    if (IsStatusResponse(line))
                    {
                        ProcessStatusResponse(line);
                    }
                    else
                    {
                        int no = 0;
                        int uid = 0;
                        int size = 0;
                        byte[] data = null;
                        IMAP_MessageFlags flags = IMAP_MessageFlags.Recent;
                        string envelope = "";
                        string bodystructure = "";
                        string internalDate = "";

                        // Remove *
                        line = RemoveCmdTag(line);

                        // Get message number
                        no = Convert.ToInt32(line.Substring(0, line.IndexOf(" ")));

                        // Get rid of FETCH  and parse params. Reply:* 1 FETCH (UID 12 BODY[] ...)
                        line = line.Substring(line.IndexOf("FETCH (") + 7);

                        StringReader r = new StringReader(line);
                        // Loop fetch result fields
                        while (r.Available > 0)
                        {
                            r.ReadToFirstChar();

                            // Fetch command closing ) parenthesis
                            if (r.SourceString == ")")
                            {
                                break;
                            }

                                #region UID <value>

                                // UID <value>
                            else if (r.StartsWith("UID", false))
                            {
                                // Remove UID word from reply
                                r.ReadSpecifiedLength("UID".Length);
                                r.ReadToFirstChar();

                                // Read <value>
                                string word = r.ReadWord();
                                if (word == null)
                                {
                                    throw new Exception("IMAP server didn't return UID <value> !");
                                }
                                else
                                {
                                    uid = Convert.ToInt32(word);
                                }
                            }

                                #endregion

                                #region RFC822.SIZE <value>

                                // RFC822.SIZE <value>
                            else if (r.StartsWith("RFC822.SIZE", false))
                            {
                                // Remove RFC822.SIZE word from reply
                                r.ReadSpecifiedLength("RFC822.SIZE".Length);
                                r.ReadToFirstChar();

                                // Read <value>
                                string word = r.ReadWord();
                                if (word == null)
                                {
                                    throw new Exception("IMAP server didn't return RFC822.SIZE <value> !");
                                }
                                else
                                {
                                    try
                                    {
                                        size = Convert.ToInt32(word);
                                    }
                                    catch
                                    {
                                        throw new Exception(
                                            "IMAP server returned invalid RFC822.SIZE <value> '" + word +
                                            "' !");
                                    }
                                }
                            }

                                #endregion

                                #region INTERNALDATE <value>

                                // INTERNALDATE <value>
                            else if (r.StartsWith("INTERNALDATE", false))
                            {
                                // Remove INTERNALDATE word from reply
                                r.ReadSpecifiedLength("INTERNALDATE".Length);
                                r.ReadToFirstChar();

                                // Read <value>
                                string word = r.ReadWord();
                                if (word == null)
                                {
                                    throw new Exception("IMAP server didn't return INTERNALDATE <value> !");
                                }
                                else
                                {
                                    internalDate = word;
                                }
                            }

                                #endregion

                                #region ENVELOPE (<envelope-string>)

                            else if (r.StartsWith("ENVELOPE", false))
                            {
                                // Remove ENVELOPE word from reply
                                r.ReadSpecifiedLength("ENVELOPE".Length);
                                r.ReadToFirstChar();

                                /* 
                                    Handle string literals {count-to-read}<CRLF>data(length = count-to-read).
                                    (string can be quoted string or literal)
                                    Loop while get envelope,invalid response or timeout.
                                */

                                while (true)
                                {
                                    try
                                    {
                                        envelope = r.ReadParenthesized();
                                        break;
                                    }
                                    catch (Exception x)
                                    {
                                        string s = r.ReadToEnd();

                                        /* partial_envelope {count-to-read}
                                            Example: ENVELOPE ("Mon, 03 Apr 2006 10:10:10 GMT" {35}
                                        */
                                        if (s.EndsWith("}"))
                                        {
                                            // Get partial envelope and append it back to reader
                                            r.AppenString(s.Substring(0, s.LastIndexOf('{')));

                                            // Read remaining envelope and append it to reader.
                                            int countToRead =
                                                Convert.ToInt32(s.Substring(s.LastIndexOf('{') + 1,
                                                                            s.LastIndexOf('}') -
                                                                            s.LastIndexOf('{') - 1));
                                            string reply = TcpStream.ReadFixedCountString(countToRead);
                                            LogAddRead(countToRead, reply);
                                            r.AppenString(TextUtils.QuoteString(reply));

                                            // Read fetch continuing line.
                                            TcpStream.ReadLine(args, false);
                                            if (args.Error != null)
                                            {
                                                throw args.Error;
                                            }
                                            line = args.LineUtf8;
                                            LogAddRead(args.BytesInBuffer, line);
                                            r.AppenString(line);
                                        }
                                            // Unexpected response
                                        else
                                        {
                                            throw x;
                                        }
                                    }
                                }
                            }

                                #endregion

                                #region BODYSTRUCTURE (<bodystructure-string>)

                            else if (r.StartsWith("BODYSTRUCTURE", false))
                            {
                                // Remove BODYSTRUCTURE word from reply
                                r.ReadSpecifiedLength("BODYSTRUCTURE".Length);
                                r.ReadToFirstChar();

                                bodystructure = r.ReadParenthesized();
                            }

                                #endregion

                                #region BODY[] or BODY[HEADER]

                                // BODY[] or BODY[HEADER]
                            else if (r.StartsWith("BODY", false))
                            {
                                if (r.StartsWith("BODY[]", false))
                                {
                                    // Remove BODY[]
                                    r.ReadSpecifiedLength("BODY[]".Length);
                                }
                                else if (r.StartsWith("BODY[HEADER]", false))
                                {
                                    // Remove BODY[HEADER]
                                    r.ReadSpecifiedLength("BODY[HEADER]".Length);
                                }
                                else
                                {
                                    throw new Exception("Invalid FETCH response: " + r.SourceString);
                                }
                                r.ReadToFirstChar();

                                // We must now have {<size-to-read>}, or there is error
                                if (!r.StartsWith("{"))
                                {
                                    throw new Exception("Invalid FETCH BODY[] or BODY[HEADER] response: " +
                                                        r.SourceString);
                                }
                                // Read <size-to-read>
                                int dataLength = Convert.ToInt32(r.ReadParenthesized());

                                // Read data
                                MemoryStream storeStrm = new MemoryStream(dataLength);
                                TcpStream.ReadFixedCount(storeStrm, dataLength);
                                LogAddRead(dataLength, "Readed " + dataLength + " bytes.");
                                data = storeStrm.ToArray();

                                // Read fetch continuing line.
                                TcpStream.ReadLine(args, false);
                                if (args.Error != null)
                                {
                                    throw args.Error;
                                }
                                line = args.LineUtf8;
                                LogAddRead(args.BytesInBuffer, line);
                                r.AppenString(line);
                            }

                                #endregion

                                #region FLAGS (<flags-list>)

                                // FLAGS (<flags-list>)
                            else if (r.StartsWith("FLAGS", false))
                            {
                                // Remove FLAGS word from reply
                                r.ReadSpecifiedLength("FLAGS".Length);
                                r.ReadToFirstChar();

                                // Read (<flags-list>)
                                string flagsList = r.ReadParenthesized();
                                if (flagsList == null)
                                {
                                    throw new Exception("IMAP server didn't return FLAGS (<flags-list>) !");
                                }
                                else
                                {
                                    flags = IMAP_Utils.ParseMessageFlags(flagsList);
                                }
                            }

                                #endregion

                            else
                            {
                                throw new Exception("Not supported fetch reply: " + r.SourceString);
                            }
                        }

                        fetchItems.Add(new IMAP_FetchItem(no,
                                                          uid,
                                                          size,
                                                          data,
                                                          flags,
                                                          internalDate,
                                                          envelope,
                                                          bodystructure,
                                                          fetchFlags));
                    }
                }
                else
                {
                    break;
                }
            }

            if (!RemoveCmdTag(line).ToUpper().StartsWith("OK"))
            {
                throw new IMAP_ClientException(line);
            }

            return fetchItems.ToArray();
        }
        /// <summary>
        /// Parses MIME header from the specified stream.
        /// </summary>
        /// <param name="stream">MIME header stream.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        public void Parse(SmartStream stream)
        {
            //TODO: ���� ��������� �������! �������� ����! �� ��� ���� �������� � utf8 �����

            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            var headers          = new List <KeyValuePair <string, byte[]> >();
            var currentMemStream = new MemoryStream();

            SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                                     SizeExceededAction.
                                                                                     ThrowException);
            while (true)
            {
                stream.ReadLine(readLineOP, false);
                if (readLineOP.Error != null)
                {
                    throw readLineOP.Error;
                }
                // We reached end of stream.
                if (readLineOP.BytesInBuffer == 0)
                {
                    if (currentMemStream.Length > 0)
                    {
                        AddToBinaryDict(headers, currentMemStream);
                    }
                    m_IsModified = false;

                    break;
                }
                // We got blank header terminator line.
                if (readLineOP.LineBytesInBuffer == 0)
                {
                    if (currentMemStream.Length > 0)
                    {
                        AddToBinaryDict(headers, currentMemStream);
                    }
                    m_IsModified = false;

                    break;
                }

                string line       = Encoding.UTF8.GetString(readLineOP.Buffer, 0, readLineOP.BytesInBuffer);
                var    realBuffer = new List <byte>();

                if ((line.StartsWith("From: \"") || line.StartsWith("To: \"")) && !line.EndsWith(">\r\n"))
                {
                    var tmpArr = new byte[readLineOP.BytesInBuffer];
                    Array.Copy(readLineOP.Buffer, 0, tmpArr, 0, readLineOP.BytesInBuffer);
                    realBuffer.AddRange(tmpArr);
                    do
                    {
                        stream.ReadLine(readLineOP, false);

                        if (readLineOP.LineBytesInBuffer == 0)
                        {
                            break;
                        }

                        line = Encoding.UTF8.GetString(readLineOP.Buffer, 0, readLineOP.BytesInBuffer);

                        tmpArr = new byte[readLineOP.BytesInBuffer];
                        Array.Copy(readLineOP.Buffer, 0, tmpArr, 0, readLineOP.BytesInBuffer);
                        realBuffer.AddRange(tmpArr);
                    } while (!line.EndsWith(">\r\n"));

                    if (realBuffer.Count > 0)
                    {
                        line = Encoding.UTF8.GetString(realBuffer.ToArray());
                    }
                }


                // New header field starts.
                if (currentMemStream.Length == 0)
                {
                    currentMemStream.Write(readLineOP.Buffer, 0, readLineOP.BytesInBuffer);
                }
                // Header field continues.
                else if (char.IsWhiteSpace(line[0]))
                {
                    currentMemStream.Write(readLineOP.Buffer, 0, readLineOP.BytesInBuffer);
                }
                // Current header field closed, new starts.
                else
                {
                    AddToBinaryDict(headers, currentMemStream);

                    currentMemStream = new MemoryStream();
                    if (realBuffer.Count > 0)
                    {
                        currentMemStream.Write(realBuffer.ToArray(), 0, realBuffer.Count);
                    }
                    else
                    {
                        currentMemStream.Write(readLineOP.Buffer, 0, readLineOP.BytesInBuffer);
                    }
                }
            }
            //Process dictionary
            //Find content type
            var contentTypeHeader =
                headers
                .Where(x => x.Value != null)
                .Where(x => "content-type".Equals(x.Key, StringComparison.OrdinalIgnoreCase))
                .Select(x => Encoding.UTF8.GetString(x.Value))
                .SingleOrDefault();
            var encoding = Encoding.UTF8;

            if (contentTypeHeader != null)
            {
                var mime = MIME_h_ContentType.Parse(contentTypeHeader);
                if (!string.IsNullOrEmpty(mime.Param_Charset))
                {
                    encoding = EncodingTools.GetEncodingByCodepageName(mime.Param_Charset) ?? Encoding.UTF8;
                }
                else
                {
                    //Join headers
                    var subjectRaw =
                        headers
                        .Where(x => x.Value != null)
                        .Where(x => "subject".Equals(x.Key, StringComparison.OrdinalIgnoreCase))
                        .Select(x => x.Value)
                        .SingleOrDefault();
                    //Try to detect hueristic
                    encoding = subjectRaw != null?EncodingTools.DetectInputCodepage(subjectRaw) : Encoding.UTF8;
                }
            }

            foreach (var keyValuePair in headers)
            {
                Add(encoding.GetString(keyValuePair.Value));
            }
        }
        /// <summary>
        /// Parses header fields from stream. Stream position stays where header reading ends.
        /// </summary>
        /// <param name="stream">Stream from where to parse.</param>
        public void Parse(SmartStream stream)
        {
            /* Rfc 2822 2.2 Header Fields
             *                  Header fields are lines composed of a field name, followed by a colon
             *                  (":"), followed by a field body, and terminated by CRLF.  A field
             *                  name MUST be composed of printable US-ASCII characters (i.e.,
             *                  characters that have values between 33 and 126, inclusive), except
             *                  colon.  A field body may be composed of any US-ASCII characters,
             *                  except for CR and LF.  However, a field body may contain CRLF when
             *                  used in header "folding" and  "unfolding" as described in section
             *                  2.2.3.  All field bodies MUST conform to the syntax described in
             *                  sections 3 and 4 of this standard.
             *
             *             Rfc 2822 2.2.3 Long Header Fields
             *                  The process of moving from this folded multiple-line representation
             *                  of a header field to its single line representation is called
             *                  "unfolding". Unfolding is accomplished by simply removing any CRLF
             *                  that is immediately followed by WSP.  Each header field should be
             *                  treated in its unfolded form for further syntactic and semantic
             *                  evaluation.
             *
             *                  Example:
             *                          Subject: aaaaa<CRLF>
             *                          <TAB or SP>aaaaa<CRLF>
             */

            m_pHeaderFields.Clear();

            SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                               SizeExceededAction.
                                                                               JunkAndThrowException);
            stream.ReadLine(args, false);
            if (args.Error != null)
            {
                throw args.Error;
            }
            string line = args.LineUtf8;

            while (line != null)
            {
                // End of header reached
                if (line == "")
                {
                    break;
                }

                // Store current header line and read next. We need to read 1 header line to ahead,
                // because of multiline header fields.
                string headerField = line;
                stream.ReadLine(args, false);
                if (args.Error != null)
                {
                    throw args.Error;
                }
                line = args.LineUtf8;

                // See if header field is multiline. See comment above.
                while (line != null && (line.StartsWith("\t") || line.StartsWith(" ")))
                {
                    headerField += line;
                    stream.ReadLine(args, false);
                    if (args.Error != null)
                    {
                        throw args.Error;
                    }
                    line = args.LineUtf8;
                }

                string[] name_value = headerField.Split(new[] { ':' }, 2);
                // There must be header field name and value, otherwise invalid header field
                if (name_value.Length == 2)
                {
                    Add(name_value[0] + ":", name_value[1].Trim());
                }
            }
        }
 /// <summary>
 /// This method is called after TCP client has sucessfully connected.
 /// </summary>
 protected override void OnConnected()
 {
     SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                        SizeExceededAction.
                                                                            JunkAndThrowException);
     TcpStream.ReadLine(args, false);
     if (args.Error != null)
     {
         throw args.Error;
     }
     string line = args.LineUtf8;
     LogAddRead(args.BytesInBuffer, line);
     line = RemoveCmdTag(line);
     if (line.ToUpper().StartsWith("OK"))
     {
         // Clear path separator, so next access will get it.
         m_PathSeparator = '\0';
     }
     else
     {
         throw new Exception("Server returned: " + line);
     }
     GetCapabilities();
     if (Capabilities.Contains("STARTTLS") && Capabilities.Contains("LOGINDISABLED"))
     {
         //Switch to ssl
         StartTLS();
         //get caps again
         GetCapabilities();
     }
 }
Exemple #14
0
        /// <summary>
        /// Parses MIME header from the specified stream.
        /// </summary>
        /// <param name="stream">MIME header stream.</param>
        /// <param name="encoding">Headers fields reading encoding. If not sure, UTF-8 is recommended.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> or <b>encoding</b> is null.</exception>
        public void Parse(SmartStream stream, Encoding encoding)
        {
            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }
            if (encoding == null)
            {
                throw new ArgumentNullException("encoding");
            }

            StringBuilder currentHeader = new StringBuilder();

            SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[84000], SizeExceededAction.ThrowException);
            while (true)
            {
                stream.ReadLine(readLineOP, false);
                if (readLineOP.Error != null)
                {
                    throw readLineOP.Error;
                }
                // We reached end of stream.
                else if (readLineOP.BytesInBuffer == 0)
                {
                    if (currentHeader.Length > 0)
                    {
                        Add(currentHeader.ToString());
                    }
                    m_IsModified = false;

                    return;
                }
                // We got blank header terminator line.
                else if (readLineOP.LineBytesInBuffer == 0)
                {
                    if (currentHeader.Length > 0)
                    {
                        Add(currentHeader.ToString());
                    }
                    m_IsModified = false;

                    return;
                }
                else
                {
                    string line = encoding.GetString(readLineOP.Buffer, 0, readLineOP.BytesInBuffer);

                    // New header field starts.
                    if (currentHeader.Length == 0)
                    {
                        currentHeader.Append(line);
                    }
                    // Header field continues.
                    else if (char.IsWhiteSpace(line[0]))
                    {
                        currentHeader.Append(line);
                    }
                    // Current header field closed, new starts.
                    else
                    {
                        Add(currentHeader.ToString());

                        currentHeader = new StringBuilder();
                        currentHeader.Append(line);
                    }
                }
            }
        }
        /// <summary>
        /// Renames specified folder.
        /// </summary>
        /// <param name="sourceFolderName">Source folder name.</param>
        /// <param name="destinationFolderName">Destination folder name.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when IMAP client is not connected and authenticated.</exception>
        public void RenameFolder(string sourceFolderName, string destinationFolderName)
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (!IsConnected)
            {
                throw new InvalidOperationException("You must connect first.");
            }
            if (!IsAuthenticated)
            {
                throw new InvalidOperationException("The RENAME command is only valid in authenticated state.");
            }

            string line = GetNextCmdTag() + " RENAME " +
                          TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(sourceFolderName)) + " " +
                          TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(destinationFolderName));
            int countWritten = TcpStream.WriteLine(line);
            LogAddWrite(countWritten, line);

            SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                               SizeExceededAction.
                                                                                   JunkAndThrowException);
            TcpStream.ReadLine(args, false);
            if (args.Error != null)
            {
                throw args.Error;
            }
            line = args.LineUtf8;
            LogAddRead(args.BytesInBuffer, line);
            line = RemoveCmdTag(line);
            if (!line.ToUpper().StartsWith("OK"))
            {
                throw new IMAP_ClientException(line);
            }
        }
        /// <summary>
        /// Switches IMAP connection to SSL.
        /// </summary>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when IMAP client is not connected or is authenticated or is already secure connection.</exception>
        public void StartTLS()
        {
            /* RFC 2595 3. IMAP STARTTLS extension.
             
                Example:    C: a001 CAPABILITY
                            S: * CAPABILITY IMAP4rev1 STARTTLS LOGINDISABLED
                            S: a001 OK CAPABILITY completed
                            C: a002 STARTTLS
                            S: a002 OK Begin TLS negotiation now
                            <TLS negotiation, further commands are under TLS layer>
            */

            if (IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (!IsConnected)
            {
                throw new InvalidOperationException("You must connect first.");
            }
            if (IsAuthenticated)
            {
                throw new InvalidOperationException(
                    "The STARTTLS command is only valid in non-authenticated state !");
            }
            if (IsSecureConnection)
            {
                throw new InvalidOperationException("Connection is already secure.");
            }

            string line = GetNextCmdTag() + " STARTTLS";
            int countWritten = TcpStream.WriteLine(line);
            LogAddWrite(countWritten, line);

            SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                               SizeExceededAction.
                                                                                   JunkAndThrowException);
            TcpStream.ReadLine(args, false);
            if (args.Error != null)
            {
                throw args.Error;
            }
            line = args.LineUtf8;
            LogAddRead(args.BytesInBuffer, line);
            line = RemoveCmdTag(line);
            if (!line.ToUpper().StartsWith("OK"))
            {
                throw new IMAP_ClientException(line);
            }

            SwitchToSecure();
        }
        /// <summary>
        /// Starts reading incoming command from the connected client.
        /// </summary>
        private void BeginReadCmd()
        {
            if (IsDisposed)
            {
                return;
            }

            try
            {
                SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                                         SizeExceededAction.
                                                                                             JunkAndThrowException);
                // This event is raised only if read period-terminated opeartion completes asynchronously.
                readLineOP.Completed += delegate
                                            {
                                                if (ProcessCmd(readLineOP))
                                                {
                                                    BeginReadCmd();
                                                }
                                            };
                // Process incoming commands while, command reading completes synchronously.
                while (TcpStream.ReadLine(readLineOP, true))
                {
                    if (!ProcessCmd(readLineOP))
                    {
                        break;
                    }
                }
            }
            catch (Exception x)
            {
                OnError(x);
            }
        }
        private void AUTH(string cmdText)
        {
            // RFC 5321 3.1.
            if (m_SessionRejected)
            {
                WriteLine("503 Bad sequence of commands: Session rejected.");
                return;
            }

            /* RFC 4954 
			    AUTH mechanism [initial-response]

                Arguments:
                    mechanism: A string identifying a [SASL] authentication mechanism.

                    initial-response: An optional initial client response.  If
                    present, this response MUST be encoded as described in Section
                    4 of [BASE64] or contain a single character "=".

                Restrictions:
                    After an AUTH command has been successfully completed, no more
                    AUTH commands may be issued in the same session.  After a
                    successful AUTH command completes, a server MUST reject any
                    further AUTH commands with a 503 reply.

                    The AUTH command is not permitted during a mail transaction.
                    An AUTH command issued during a mail transaction MUST be
                    rejected with a 503 reply.
             
                A server challenge is sent as a 334 reply with the text part
                containing the [BASE64] encoded string supplied by the SASL
                mechanism.  This challenge MUST NOT contain any text other
                than the BASE64 encoded challenge.
             
                In SMTP, a server challenge that contains no data is defined 
                as a 334 reply with no text part. Note that there is still a space 
                following the reply code, so the complete response line is "334 ".
             
                If the client wishes to cancel the authentication exchange, 
                it issues a line with a single "*". If the server receives 
                such a response, it MUST reject the AUTH command by sending a 501 reply.
			*/

            if (IsAuthenticated)
            {
                WriteLine("503 Bad sequence of commands: you are already authenticated.");
                return;
            }
            if (m_pFrom != null)
            {
                WriteLine(
                    "503 Bad sequence of commands: The AUTH command is not permitted during a mail transaction.");
                return;
            }

            #region Parse parameters

            string[] arguments = cmdText.Split(' ');
            if (arguments.Length > 2)
            {
                WriteLine("501 Syntax error, syntax: AUTH SP mechanism [SP initial-response] CRLF");
                return;
            }
            string initialClientResponse = "";
            if (arguments.Length == 2)
            {
                if (arguments[1] == "=")
                {
                    // Skip.
                }
                else
                {
                    try
                    {
                        initialClientResponse = Encoding.UTF8.GetString(Convert.FromBase64String(arguments[1]));
                    }
                    catch
                    {
                        WriteLine(
                            "501 Syntax error: Parameter 'initial-response' value must be BASE64 or contain a single character '='.");
                        return;
                    }
                }
            }
            string mechanism = arguments[0];

            #endregion

            if (!Authentications.ContainsKey(mechanism))
            {
                WriteLine("501 Not supported authentication mechanism.");
                return;
            }

            string clientResponse = initialClientResponse;
            AUTH_SASL_ServerMechanism auth = Authentications[mechanism];
            while (true)
            {
                string serverResponse = auth.Continue(clientResponse);
                // Authentication completed.
                if (auth.IsCompleted)
                {
                    if (auth.IsAuthenticated)
                    {
                        m_pUser = new GenericIdentity(auth.UserName, "SASL-" + auth.Name);

                        WriteLine("235 2.7.0 Authentication succeeded.");
                    }
                    else
                    {
                        WriteLine("535 5.7.8 Authentication credentials invalid.");
                    }
                    break;
                }
                    // Authentication continues.
                else
                {
                    // Send server challange.
                    if (string.IsNullOrEmpty(serverResponse))
                    {
                        WriteLine("334 ");
                    }
                    else
                    {
                        WriteLine("334 " + Convert.ToBase64String(Encoding.UTF8.GetBytes(serverResponse)));
                    }

                    // Read client response. 
                    SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                                             SizeExceededAction
                                                                                                 .
                                                                                                 JunkAndThrowException);
                    TcpStream.ReadLine(readLineOP, false);
                    if (readLineOP.Error != null)
                    {
                        throw readLineOP.Error;
                    }
                    clientResponse = readLineOP.LineUtf8;
                    // Log
                    if (Server.Logger != null)
                    {
                        Server.Logger.AddRead(ID,
                                              AuthenticatedUserIdentity,
                                              readLineOP.BytesInBuffer,
                                              "base64 auth-data",
                                              LocalEndPoint,
                                              RemoteEndPoint);
                    }

                    // Client canceled authentication.
                    if (clientResponse == "*")
                    {
                        WriteLine("501 Authentication canceled.");
                        return;
                    }
                        // We have base64 client response, decode it.
                    else
                    {
                        try
                        {
                            clientResponse = Encoding.UTF8.GetString(Convert.FromBase64String(clientResponse));
                        }
                        catch
                        {
                            WriteLine("501 Invalid client response '" + clientResponse + "'.");
                            return;
                        }
                    }
                }
            }
        }
Exemple #19
0
        /// <summary>
        /// Gets files and directories in the current server directory.
        /// </summary>
        /// <param name="path">Directory or file name which listing to get. Value null means current directory will be listed.</param>
        /// <returns>Returns current working directory listing.</returns>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when FTP client is not connected or FTP data connection has active read/write operation.</exception>
        /// <exception cref="FTP_ClientException">Is raised when FTP server returns error.</exception>
        public FTP_ListItem[] GetList(string path)
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (!IsConnected)
            {
                throw new InvalidOperationException("You must connect first.");
            }
            if (m_pDataConnection.IsActive)
            {
                throw new InvalidOperationException(
                    "There is already active read/write operation on data connection.");
            }

            List<FTP_ListItem> retVal = new List<FTP_ListItem>();

            // Set transfer mode
            SetTransferType(TransferType.Binary);

            if (m_TransferMode == FTP_TransferMode.Passive)
            {
                Pasv();
            }
            else
            {
                Port();
            }

            // If FTP server supports MLSD command, use it to get directory listing.
            // MLSD is standard way to get dir listing, while LIST command isn't any strict standard.
            bool mlsdSupported = false;
            foreach (string feature in m_pExtCapabilities)
            {
                if (feature.ToLower().StartsWith("mlsd"))
                {
                    mlsdSupported = true;
                    break;
                }
            }

            #region MLSD

            if (mlsdSupported)
            {
                if (string.IsNullOrEmpty(path))
                {
                    WriteLine("MLSD");
                }
                else
                {
                    WriteLine("MLSD " + path);
                }

                string[] response = ReadResponse();
                if (!response[0].StartsWith("1"))
                {
                    throw new FTP_ClientException(response[0]);
                }

                MemoryStream ms = new MemoryStream();
                m_pDataConnection.ReadAll(ms);

                response = ReadResponse();
                if (!response[0].StartsWith("2"))
                {
                    throw new FTP_ClientException(response[0]);
                }

                byte[] lineBuffer = new byte[8000];
                ms.Position = 0;
                SmartStream mlsdStream = new SmartStream(ms, true);
                while (true)
                {
                    SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(lineBuffer,
                                                                                       SizeExceededAction.
                                                                                           JunkAndThrowException);
                    mlsdStream.ReadLine(args, false);
                    if (args.Error != null)
                    {
                        throw args.Error;
                    }
                    string line = args.LineUtf8;

                    // We reached end of stream, we readed whole list sucessfully.
                    if (line == null)
                    {
                        break;
                    }
                    else
                    {
                        string[] parameters = line.Substring(0, line.LastIndexOf(';')).Split(';');
                        string name = line.Substring(line.LastIndexOf(';') + 1).Trim();

                        string type = "";
                        long size = 0;
                        DateTime modified = DateTime.MinValue;
                        foreach (string parameter in parameters)
                        {
                            string[] name_value = parameter.Split('=');
                            if (name_value[0].ToLower() == "type")
                            {
                                type = name_value[1].ToLower();
                            }
                            else if (name_value[0].ToLower() == "size")
                            {
                                size = Convert.ToInt32(name_value[1]);
                            }
                            else if (name_value[0].ToLower() == "modify")
                            {
                                modified = DateTime.ParseExact(name_value[1],
                                                               "yyyyMMddHHmmss",
                                                               DateTimeFormatInfo.InvariantInfo);
                            }
                            else
                            {
                                // Other options won't interest us, skip them.
                            }
                        }

                        if (type == "dir")
                        {
                            retVal.Add(new FTP_ListItem(name, 0, modified, true));
                        }
                        else if (type == "file")
                        {
                            retVal.Add(new FTP_ListItem(name, size, modified, false));
                        }
                    }
                }
            }

                #endregion

                #region LIST

            else
            {
                if (string.IsNullOrEmpty(path))
                {
                    WriteLine("LIST");
                }
                else
                {
                    WriteLine("LIST " + path);
                }

                string[] response = ReadResponse();
                if (!response[0].StartsWith("1"))
                {
                    throw new FTP_ClientException(response[0]);
                }

                MemoryStream ms = new MemoryStream();
                m_pDataConnection.ReadAll(ms);

                response = ReadResponse();
                if (!response[0].StartsWith("2"))
                {
                    throw new FTP_ClientException(response[0]);
                }

                ms.Position = 0;
                SmartStream listStream = new SmartStream(ms, true);
                SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[8000],
                                                                                   SizeExceededAction.
                                                                                       JunkAndThrowException);
                listStream.ReadLine(args, false);
                if (args.Error != null)
                {
                    throw args.Error;
                }
                string line = args.LineUtf8;

                string listingType = "unix";
                // Dedect listing.
                if (line != null)
                {
                    try
                    {
                        StringReader r = new StringReader(line);
                        DateTime modified = DateTime.ParseExact(r.ReadWord() + " " + r.ReadWord(),
                                                                new[] {"MM-dd-yy hh:mmtt"},
                                                                DateTimeFormatInfo.InvariantInfo,
                                                                DateTimeStyles.None);
                        listingType = "win";
                    }
                    catch {}
                }

                string[] winDateFormats = new[] {"M-d-yy h:mmtt"};
                string[] unixFormats = new[] {"MMM d H:mm", "MMM d yyyy"};

                byte[] lineBuffer = new byte[8000];
                while (line != null)
                {
                    // Windows listing.                 
                    if (listingType == "win")
                    {
                        // MM-dd-yy hh:mm <DIR> directoryName
                        // MM-dd-yy hh:mm size  fileName

                        StringReader r = new StringReader(line);
                        // Read date
                        DateTime modified = DateTime.ParseExact(r.ReadWord() + " " + r.ReadWord(),
                                                                winDateFormats,
                                                                DateTimeFormatInfo.InvariantInfo,
                                                                DateTimeStyles.None);

                        r.ReadToFirstChar();
                        // We have directory.
                        if (r.StartsWith("<dir>", false))
                        {
                            r.ReadSpecifiedLength(5);
                            r.ReadToFirstChar();

                            retVal.Add(new FTP_ListItem(r.ReadToEnd(), 0, modified, true));
                        }
                            // We have file
                        else
                        {
                            // Read file size
                            long size = Convert.ToInt64(r.ReadWord());
                            r.ReadToFirstChar();

                            retVal.Add(new FTP_ListItem(r.ReadToEnd(), size, modified, false));
                        }
                    }
                        // Unix listing
                    else
                    {
                        // "d"directoryAtttributes xx xx xx 0 MMM d HH:mm/yyyy directoryName
                        // fileAtttributes xx xx xx fileSize MMM d HH:mm/yyyy fileName

                        StringReader r = new StringReader(line);
                        string attributes = r.ReadWord();
                        r.ReadWord();
                        r.ReadWord();
                        r.ReadWord();
                        long size = Convert.ToInt64(r.ReadWord());
                        DateTime modified =
                            DateTime.ParseExact(r.ReadWord() + " " + r.ReadWord() + " " + r.ReadWord(),
                                                unixFormats,
                                                DateTimeFormatInfo.InvariantInfo,
                                                DateTimeStyles.None);
                        r.ReadToFirstChar();
                        string name = r.ReadToEnd();
                        if (name != "." && name != "..")
                        {
                            if (attributes.StartsWith("d"))
                            {
                                retVal.Add(new FTP_ListItem(name, 0, modified, true));
                            }
                            else
                            {
                                retVal.Add(new FTP_ListItem(name, size, modified, false));
                            }
                        }
                    }

                    listStream.ReadLine(args, false);
                    if (args.Error != null)
                    {
                        throw args.Error;
                    }
                    line = args.LineUtf8;
                }
            }

            #endregion

            return retVal.ToArray();
        }
        /// <summary>
        /// Parses header fields from stream. Stream position stays where header reading ends.
        /// </summary>
        /// <param name="stream">Stream from where to parse.</param>
        public void Parse(SmartStream stream)
        {
            /* Rfc 2822 2.2 Header Fields
				Header fields are lines composed of a field name, followed by a colon
				(":"), followed by a field body, and terminated by CRLF.  A field
				name MUST be composed of printable US-ASCII characters (i.e.,
				characters that have values between 33 and 126, inclusive), except
				colon.  A field body may be composed of any US-ASCII characters,
				except for CR and LF.  However, a field body may contain CRLF when
				used in header "folding" and  "unfolding" as described in section
				2.2.3.  All field bodies MUST conform to the syntax described in
				sections 3 and 4 of this standard. 
				
			   Rfc 2822 2.2.3 Long Header Fields
				The process of moving from this folded multiple-line representation
				of a header field to its single line representation is called
				"unfolding". Unfolding is accomplished by simply removing any CRLF
				that is immediately followed by WSP.  Each header field should be
				treated in its unfolded form for further syntactic and semantic
				evaluation.
				
				Example:
					Subject: aaaaa<CRLF>
					<TAB or SP>aaaaa<CRLF>
			*/

            m_pHeaderFields.Clear();

            SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                               SizeExceededAction.
                                                                                   JunkAndThrowException);
            stream.ReadLine(args, false);
            if (args.Error != null)
            {
                throw args.Error;
            }
            string line = args.LineUtf8;

            while (line != null)
            {
                // End of header reached
                if (line == "")
                {
                    break;
                }

                // Store current header line and read next. We need to read 1 header line to ahead,
                // because of multiline header fields.
                string headerField = line;
                stream.ReadLine(args, false);
                if (args.Error != null)
                {
                    throw args.Error;
                }
                line = args.LineUtf8;

                // See if header field is multiline. See comment above.				
                while (line != null && (line.StartsWith("\t") || line.StartsWith(" ")))
                {
                    headerField += line;
                    stream.ReadLine(args, false);
                    if (args.Error != null)
                    {
                        throw args.Error;
                    }
                    line = args.LineUtf8;
                }

                string[] name_value = headerField.Split(new[] {':'}, 2);
                // There must be header field name and value, otherwise invalid header field
                if (name_value.Length == 2)
                {
                    Add(name_value[0] + ":", name_value[1].Trim());
                }
            }
        }
Exemple #21
0
        /// <summary>
        /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
        /// </summary>
        /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and (offset + count - 1) replaced by the bytes read from the current source.</param>
        /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data read from the current stream.</param>
        /// <param name="count">The maximum number of bytes to be read from the current stream.</param>
        /// <returns>The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>buffer</b> is null reference.</exception>
        /// <exception cref="ArgumentException">Is raised when any of the arguments has invalid value.</exception>
        /// <exception cref="NotSupportedException">Is raised when reading not supported.</exception>
        public override int Read(byte[] buffer, int offset, int count)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }
            if (offset < 0 || offset > buffer.Length)
            {
                throw new ArgumentException("Invalid argument 'offset' value.");
            }
            if (offset + count > buffer.Length)
            {
                throw new ArgumentException("Invalid argument 'count' value.");
            }
            if ((m_AccessMode & FileAccess.Read) == 0)
            {
                throw new NotSupportedException();
            }

            while (true)
            {
                // Read next quoted-printable line and decode it.
                if (m_DecodedOffset >= m_DecodedCount)
                {
                    m_DecodedOffset = 0;
                    m_DecodedCount  = 0;
                    SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(line_buf,
                                                                                             SizeExceededAction
                                                                                             .
                                                                                             ThrowException);
                    m_pStream.ReadLine(readLineOP, false);
                    // IO error reading line.
                    if (readLineOP.Error != null)
                    {
                        throw readLineOP.Error;
                    }
                    // We reached end of stream.
                    else if (readLineOP.BytesInBuffer == 0)
                    {
                        return(0);
                    }
                    // Decode quoted-printable line.
                    else
                    {
                        // Process bytes.
                        bool softLineBreak = false;
                        int  lineLength    = readLineOP.LineBytesInBuffer;
                        for (int i = 0; i < readLineOP.LineBytesInBuffer; i++)
                        {
                            byte b = readLineOP.Buffer[i];
                            // We have soft line-break.
                            if (b == '=' && i == (lineLength - 1))
                            {
                                softLineBreak = true;
                            }
                            // We should have =XX char.
                            else if (b == '=')
                            {
                                byte b1 = readLineOP.Buffer[++i];
                                byte b2 = readLineOP.Buffer[++i];

                                string b1b2 = ((char)b1).ToString() + (char)b2;
                                byte   b1b2_num;
                                if (byte.TryParse(b1b2, NumberStyles.HexNumber, null, out b1b2_num))
                                {
                                    m_pDecodedBuffer[m_DecodedCount++] = b1b2_num;
                                }
                                else
                                {
                                    m_pDecodedBuffer[m_DecodedCount++] = b;
                                    m_pDecodedBuffer[m_DecodedCount++] = b1;
                                    m_pDecodedBuffer[m_DecodedCount++] = b2;
                                }
                            }
                            // Normal char.
                            else
                            {
                                m_pDecodedBuffer[m_DecodedCount++] = b;
                            }
                        }

                        if (!softLineBreak)
                        {
                            m_pDecodedBuffer[m_DecodedCount++] = (byte)'\r';
                            m_pDecodedBuffer[m_DecodedCount++] = (byte)'\n';
                        }
                    }
                }

                // We some decoded data, return it.
                if (m_DecodedOffset < m_DecodedCount)
                {
                    int countToCopy = Math.Min(count, m_DecodedCount - m_DecodedOffset);
                    Array.Copy(m_pDecodedBuffer, m_DecodedOffset, buffer, offset, countToCopy);
                    m_DecodedOffset += countToCopy;

                    return(countToCopy);
                }
            }
        }
        /// <summary>
        /// Authenticates user.
        /// </summary>
        /// <param name="userName">User name.</param>
        /// <param name="password">Password.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when IMAP client is not connected or is already authenticated.</exception>
        public void Authenticate(string userName, string password)
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (!IsConnected)
            {
                throw new InvalidOperationException("You must connect first.");
            }
            if (IsAuthenticated)
            {
                throw new InvalidOperationException("Session is already authenticated.");
            }

            string line = GetNextCmdTag() + " LOGIN " + TextUtils.QuoteString(userName) + " " +
                          TextUtils.QuoteString(password);
            int countWritten = TcpStream.WriteLine(line);
            LogAddWrite(countWritten,
                        string.IsNullOrEmpty(password) ? line : line.Replace(password, "<***REMOVED***>"));

            SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                               SizeExceededAction.
                                                                                   JunkAndThrowException);
            TcpStream.ReadLine(args, false);
            if (args.Error != null)
            {
                throw args.Error;
            }
            line = args.LineUtf8;
            LogAddRead(args.BytesInBuffer, line);
            line = RemoveCmdTag(line);
            if (line.ToUpper().StartsWith("OK"))
            {
                m_pAuthdUserIdentity = new GenericIdentity(userName, "login");
            }
            else
            {
                throw new IMAP_ClientException(line);
            }
        }
        /// <summary>
        /// Creates specified folder.
        /// </summary>
        /// <param name="folderName">Folder name. Eg. test, Inbox/SomeSubFolder. NOTE: use GetFolderSeparator() to get right folder separator.</param>
        /// <exception cref="ObjectDisposedException">Is raised when this object is disposed and this method is accessed.</exception>
        /// <exception cref="InvalidOperationException">Is raised when IMAP client is not connected and authenticated.</exception>
        public void CreateFolder(string folderName)
        {
            if (IsDisposed)
            {
                throw new ObjectDisposedException(GetType().Name);
            }
            if (!IsConnected)
            {
                throw new InvalidOperationException("You must connect first.");
            }
            if (!IsAuthenticated)
            {
                throw new InvalidOperationException("The CREATE command is only valid in authenticated state.");
            }

            // Ensure that we send right separator to server, we accept both \ and /.
            folderName = folderName.Replace('\\', PathSeparator).Replace('/', PathSeparator);

            string line = GetNextCmdTag() + " CREATE " +
                          TextUtils.QuoteString(Core.Encode_IMAP_UTF7_String(folderName));
            int countWritten = TcpStream.WriteLine(line);
            LogAddWrite(countWritten, line);

            SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                               SizeExceededAction.
                                                                                   JunkAndThrowException);
            TcpStream.ReadLine(args, false);
            if (args.Error != null)
            {
                throw args.Error;
            }
            line = args.LineUtf8;
            LogAddRead(args.BytesInBuffer, line);
            line = RemoveCmdTag(line);
            if (!line.ToUpper().StartsWith("OK"))
            {
                throw new IMAP_ClientException(line);
            }
        }
Exemple #24
0
            /// <summary>
            /// Start operation processing.
            /// </summary>
            public void Start()
            {
                /* RFC 3501.
                    literal = "{" number "}" CRLF *CHAR8
                              ; Number represents the number of CHAR8s
                */

                // TODO: Async
                // TODO: Limit total command size. 64k ?

                // If initial command line ends with literal string, read literal string and remaining command text.
                if(EndsWithLiteralString(m_InitialCmdLine)){
                    StringBuilder cmdText     = new StringBuilder();
                    int           literalSize = GetLiteralSize(m_InitialCmdLine);

                    // Add initial command line part to command text.
                    cmdText.Append(RemoveLiteralSpecifier(m_InitialCmdLine));

                    SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[32000],SizeExceededAction.JunkAndThrowException);
                    while(true){
                        #region Read literal string

                        // Send "+ Continue".
                        m_pSession.WriteLine("+ Continue.");

                        // Read literal string.
                        MemoryStream msLiteral = new MemoryStream();
                        m_pSession.TcpStream.ReadFixedCount(msLiteral,literalSize);

                        // Log
                        m_pSession.LogAddRead(literalSize,m_pCharset.GetString(msLiteral.ToArray()));

                        // Add to command text as quoted string.
                        cmdText.Append(TextUtils.QuoteString(m_pCharset.GetString(msLiteral.ToArray())));

                        #endregion

                        #region Read continuing command text

                        // Read continuing command text.
                        m_pSession.TcpStream.ReadLine(readLineOP,false);

                        // We have error.
                        if(readLineOP.Error != null){
                            throw readLineOP.Error;
                        }
                        else{
                            string line = readLineOP.LineUtf8;

                            // Log
                            m_pSession.LogAddRead(readLineOP.BytesInBuffer,line);

                            // Add command line part to command text.
                            if(EndsWithLiteralString(line)){
                                cmdText.Append(RemoveLiteralSpecifier(line));
                            }
                            else{
                                cmdText.Append(line);
                            }

                            // No more literal string, we are done.
                            if(!EndsWithLiteralString(line)){
                                break;
                            }
                            else{
                                literalSize = GetLiteralSize(line);
                            }
                        }

                        #endregion
                    }

                    m_CmdLine = cmdText.ToString();
                }
                // We have no literal string, so initial cmd line is final.
                else{
                    m_CmdLine = m_InitialCmdLine;
                }
            }
        /// <summary>
        /// Reads and logs specified line from connected host.
        /// </summary>
        /// <returns>Returns readed line.</returns>
        protected string ReadLine()
        {
            SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                               SizeExceededAction.
                                                                                   JunkAndThrowException);
            TcpStream.ReadLine(args, false);
            if (args.Error != null)
            {
                throw args.Error;
            }
            string line = args.LineUtf8;
            if (args.BytesInBuffer > 0)
            {
                LogAddRead(args.BytesInBuffer, line);
            }
            else
            {
                LogAddText("Remote host closed connection.");
            }

            return line;
        }
            /// <summary>
            /// Default constructor.
            /// </summary>
            /// <param name="stream">Stream from where to read body part.</param>
            /// <param name="boundary">Boundry ID what separates body parts.</param>
            /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> or <b>boundary</b> is null reference.</exception>
            public _MultipartReader(SmartStream stream, string boundary)
            {
                if (stream == null)
                {
                    throw new ArgumentNullException("stream");
                }
                if (boundary == null)
                {
                    throw new ArgumentNullException("boundary");
                }

                m_pStream = stream;
                m_Boundary = boundary;

                m_pReadLineOP = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                SizeExceededAction.ThrowException);
                m_pTextPreamble = new StringBuilder();
                m_pTextEpilogue = new StringBuilder();
            }
        /// <summary>
        /// Reads next continuing FETCH line and stores to fetch reader 'r'.
        /// </summary>
        /// <param name="imap">IMAP client.</param>
        /// <param name="r">String reader.</param>
        /// <param name="callback">Fetch completion callback.</param>
        /// <returns>Returns true if completed asynchronously or false if completed synchronously.</returns>
        /// <exception cref="ArgumentNullException">Is raised when <b>imap</b>,<b>r</b> or <b>callback</b> is null reference.</exception>
        private bool ReadNextFetchLine(IMAP_Client imap, StringReader r, EventHandler <EventArgs <Exception> > callback)
        {
            if (imap == null)
            {
                throw new ArgumentNullException("imap");
            }
            if (r == null)
            {
                throw new ArgumentNullException("r");
            }
            if (callback == null)
            {
                throw new ArgumentNullException("callback");
            }

            SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[64000], SizeExceededAction.JunkAndThrowException);
            readLineOP.Completed += delegate(object sender, EventArgs <SmartStream.ReadLineAsyncOP> e){
                try{
                    // Read line failed.
                    if (readLineOP.Error != null)
                    {
                        callback(this, new EventArgs <Exception>(readLineOP.Error));
                    }
                    else
                    {
                        // Log.
                        imap.LogAddRead(readLineOP.BytesInBuffer, readLineOP.LineUtf8);

                        // Append fetch line to fetch reader.
                        r.AppendString(readLineOP.LineUtf8);

                        ParseDataItems(imap, r, callback);
                    }
                }
                catch (Exception x) {
                    callback(this, new EventArgs <Exception>(x));
                }
                finally{
                    readLineOP.Dispose();
                }
            };

            // Read line completed synchronously.
            if (imap.TcpStream.ReadLine(readLineOP, true))
            {
                try{
                    // Read line failed.
                    if (readLineOP.Error != null)
                    {
                        callback(this, new EventArgs <Exception>(readLineOP.Error));

                        return(true);
                    }
                    else
                    {
                        // Log.
                        imap.LogAddRead(readLineOP.BytesInBuffer, readLineOP.LineUtf8);

                        // Append fetch line to fetch reader.
                        r.AppendString(readLineOP.LineUtf8);

                        return(false);
                    }
                }
                finally{
                    readLineOP.Dispose();
                }
            }

            return(true);
        }
Exemple #28
0
        /// <summary>
        /// Parses mime entity from stream.
        /// </summary>
        /// <param name="stream">Data stream from where to read data.</param>
        /// <param name="toBoundary">Entity data is readed to specified boundary.</param>
        /// <returns>Returns false if last entity. Returns true for mulipart entity, if there are more entities.</returns>
        internal bool Parse(SmartStream stream, string toBoundary)
        {
            // Clear header fields
            m_pHeader.Clear();
            m_pHeaderFieldCache.Clear();

            // Parse header
            m_pHeader.Parse(stream);

            // Parse entity and child entities if any (Conent-Type: multipart/xxx...)

            // Multipart entity
            if ((ContentType & MediaType_enum.Multipart) != 0)
            {
                // There must be be boundary ID (rfc 1341 7.2.1  The Content-Type field for multipart entities requires one parameter,
                // "boundary", which is used to specify the encapsulation boundary.)
                string boundaryID = ContentType_Boundary;
                if (boundaryID == null)
                {
                    // This is invalid message, just skip this mime entity
                }
                else
                {
                    // There is one or more mime entities

                    // Find first boundary start position
                    SmartStream.ReadLineAsyncOP args = new SmartStream.ReadLineAsyncOP(new byte[8000],
                                                                                       SizeExceededAction.
                                                                                           JunkAndThrowException);
                    stream.ReadLine(args, false);
                    if (args.Error != null)
                    {
                        throw args.Error;
                    }
                    string lineString = args.LineUtf8;

                    while (lineString != null)
                    {
                        if (lineString.StartsWith("--" + boundaryID))
                        {
                            break;
                        }

                        stream.ReadLine(args, false);
                        if (args.Error != null)
                        {
                            throw args.Error;
                        }
                        lineString = args.LineUtf8;
                    }
                    // This is invalid entity, boundary start not found. Skip that entity.
                    if (string.IsNullOrEmpty(lineString))
                    {
                        return false;
                    }

                    // Start parsing child entities of this entity
                    while (true)
                    {
                        // Parse and add child entity
                        MimeEntity childEntity = new MimeEntity();
                        ChildEntities.Add(childEntity);

                        // This is last entity, stop parsing
                        if (childEntity.Parse(stream, boundaryID) == false)
                        {
                            break;
                        }
                        // else{
                        // There are more entities, parse them
                    }

                    // This entity is child of mulipart entity.
                    // All this entity child entities are parsed,
                    // we need to move stream position to next entity start.
                    if (!string.IsNullOrEmpty(toBoundary))
                    {
                        stream.ReadLine(args, false);
                        if (args.Error != null)
                        {
                            throw args.Error;
                        }
                        lineString = args.LineUtf8;

                        while (lineString != null)
                        {
                            if (lineString.StartsWith("--" + toBoundary))
                            {
                                break;
                            }

                            stream.ReadLine(args, false);
                            if (args.Error != null)
                            {
                                throw args.Error;
                            }
                            lineString = args.LineUtf8;
                        }

                        // Invalid boundary end, there can't be more entities 
                        if (string.IsNullOrEmpty(lineString))
                        {
                            return false;
                        }

                        // See if last boundary or there is more. Last boundary ends with --
                        if (lineString.EndsWith(toBoundary + "--"))
                        {
                            return false;
                        }
                        // else{
                        // There are more entities					
                        return true;
                    }
                }
            }
                // Singlepart entity.
            else
            {
                // Boundary is specified, read data to specified boundary.
                if (!string.IsNullOrEmpty(toBoundary))
                {
                    MemoryStream entityData = new MemoryStream();
                    SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                                             SizeExceededAction
                                                                                                 .
                                                                                                 JunkAndThrowException);

                    // Read entity data while get boundary end tag --boundaryID-- or EOS.
                    while (true)
                    {
                        stream.ReadLine(readLineOP, false);
                        if (readLineOP.Error != null)
                        {
                            throw readLineOP.Error;
                        }
                        // End of stream reached. Normally we should get boundary end tag --boundaryID--, but some x mailers won't add it, so
                        // if we reach EOS, consider boundary closed.
                        if (readLineOP.BytesInBuffer == 0)
                        {
                            // Just return data what was readed.
                            m_EncodedData = entityData.ToArray();
                            return false;
                        }
                            // We readed a line.
                        else
                        {
                            // We have boundary start/end tag or just "--" at the beginning of line.
                            if (readLineOP.LineBytesInBuffer >= 2 && readLineOP.Buffer[0] == '-' &&
                                readLineOP.Buffer[1] == '-')
                            {
                                string lineString = readLineOP.LineUtf8;
                                // We have boundary end tag, no more boundaries.
                                if (lineString == "--" + toBoundary + "--")
                                {
                                    m_EncodedData = entityData.ToArray();
                                    return false;
                                }
                                    // We have new boundary start.
                                else if (lineString == "--" + toBoundary)
                                {
                                    m_EncodedData = entityData.ToArray();
                                    return true;
                                }
                                else
                                {
                                    // Just skip
                                }
                            }

                            // Write readed line.
                            entityData.Write(readLineOP.Buffer, 0, readLineOP.BytesInBuffer);
                        }
                    }
                }
                    // Boundary isn't specified, read data to the stream end. 
                else
                {
                    MemoryStream ms = new MemoryStream();
                    stream.ReadAll(ms);
                    m_EncodedData = ms.ToArray();
                }
            }

            return false;
        }
        /// <summary>
        /// Parses MIME header from the specified stream.
        /// </summary>
        /// <param name="stream">MIME header stream.</param>
        /// <exception cref="ArgumentNullException">Is raised when <b>stream</b> is null.</exception>
        public void Parse(SmartStream stream)
        {
            //TODO: ���� ��������� �������! �������� ����! �� ��� ���� �������� � utf8 �����

            if (stream == null)
            {
                throw new ArgumentNullException("stream");
            }

            var headers = new List<KeyValuePair<string, byte[]>>();
            var currentMemStream = new MemoryStream();
            SmartStream.ReadLineAsyncOP readLineOP = new SmartStream.ReadLineAsyncOP(new byte[Workaround.Definitions.MaxStreamLineLength],
                                                                                     SizeExceededAction.
                                                                                         ThrowException);
            while (true)
            {
                stream.ReadLine(readLineOP, false);
                if (readLineOP.Error != null)
                {
                    throw readLineOP.Error;
                }
                // We reached end of stream.
                if (readLineOP.BytesInBuffer == 0)
                {
                    if (currentMemStream.Length > 0)
                    {
                        AddToBinaryDict(headers, currentMemStream);
                    }
                    m_IsModified = false;

                    break;
                }
                // We got blank header terminator line.
                if (readLineOP.LineBytesInBuffer == 0)
                {
                    if (currentMemStream.Length > 0)
                    {
                        AddToBinaryDict(headers, currentMemStream);
                    }
                    m_IsModified = false;

                    break;
                }

                string line = Encoding.UTF8.GetString(readLineOP.Buffer, 0, readLineOP.BytesInBuffer);
                var realBuffer = new List<byte>();

                if ((line.StartsWith("From: \"") || line.StartsWith("To: \"")) && !line.EndsWith(">\r\n"))
                {
                    var tmpArr = new byte[readLineOP.BytesInBuffer];
                    Array.Copy(readLineOP.Buffer, 0, tmpArr, 0, readLineOP.BytesInBuffer);
                    realBuffer.AddRange(tmpArr);
                    do
                    {
                        stream.ReadLine(readLineOP, false);

                        if (readLineOP.LineBytesInBuffer == 0)
                            break;

                        line = Encoding.UTF8.GetString(readLineOP.Buffer, 0, readLineOP.BytesInBuffer);

                        tmpArr = new byte[readLineOP.BytesInBuffer];
                        Array.Copy(readLineOP.Buffer, 0, tmpArr, 0, readLineOP.BytesInBuffer);
                        realBuffer.AddRange(tmpArr);

                    } while (!line.EndsWith(">\r\n"));

                    if (realBuffer.Count > 0)
                    {
                        line = Encoding.UTF8.GetString(realBuffer.ToArray());
                    }
                }
                

                // New header field starts.
                if (currentMemStream.Length == 0)
                {
                    currentMemStream.Write(readLineOP.Buffer, 0, readLineOP.BytesInBuffer);
                }
                // Header field continues.
                else if (char.IsWhiteSpace(line[0]))
                {
                    currentMemStream.Write(readLineOP.Buffer, 0, readLineOP.BytesInBuffer);
                }
                // Current header field closed, new starts.
                else
                {
                    AddToBinaryDict(headers, currentMemStream);

                    currentMemStream = new MemoryStream();
                    if (realBuffer.Count > 0)
                        currentMemStream.Write(realBuffer.ToArray(), 0, realBuffer.Count);
                    else
                        currentMemStream.Write(readLineOP.Buffer, 0, readLineOP.BytesInBuffer);
                }
            }
            //Process dictionary
            //Find content type
            var contentTypeHeader = 
                headers
                    .Where(x => x.Value != null)
                    .Where(x => "content-type".Equals(x.Key, StringComparison.OrdinalIgnoreCase))
                    .Select(x => Encoding.UTF8.GetString(x.Value))
                    .SingleOrDefault();
            var encoding = Encoding.UTF8;
            if (contentTypeHeader != null)
            {
                var mime = MIME_h_ContentType.Parse(contentTypeHeader);
                if (!string.IsNullOrEmpty(mime.Param_Charset))
                {
                    encoding = EncodingTools.GetEncodingByCodepageName(mime.Param_Charset) ?? Encoding.UTF8;
                }
                else
                {
                    //Join headers
                    var subjectRaw =
                        headers
                            .Where(x => x.Value != null)
                            .Where(x => "subject".Equals(x.Key, StringComparison.OrdinalIgnoreCase))
                            .Select(x => x.Value)
                            .SingleOrDefault();
                    //Try to detect hueristic
                    encoding = subjectRaw != null ? EncodingTools.DetectInputCodepage(subjectRaw) : Encoding.UTF8;
                }
            }

            foreach (var keyValuePair in headers)
            {
                Add(encoding.GetString(keyValuePair.Value));
            }
        }