/*
 *              /// <summary>
 *              /// Transfers specified message to specified socket. This is good for example transfering message from remote POP3 server to POP3 client.
 *              /// </summary>
 *              /// <param name="nr">Message number.</param>
 *              /// <param name="socket">Socket where to store message.</param>
 *              public void GetMessage(int nr,Socket socket)
 *              {
 *                      if(!m_Connected){
 *                              throw new Exception("You must connect first !");
 *                      }
 *
 *                      if(!m_Authenticated){
 *                              throw new Exception("You must authenticate first !");
 *                      }
 *
 *                      Core.SendLine(m_pSocket,"RETR " + nr.ToString());
 *
 *                      // Read first line of reply, check if it's ok
 *                      string line = Core.ReadLine(m_pSocket);
 *                      if(line.StartsWith("+OK")){
 *                              NetworkStream readStream  = new NetworkStream(m_pSocket);
 *                              NetworkStream storeStream = new NetworkStream(socket);
 *
 *                              byte[] crlf = new byte[]{(byte)'\r',(byte)'\n'};
 *                              StreamLineReader reader = new StreamLineReader(readStream);
 *                              byte[] lineData = reader.ReadLine();
 *                              while(lineData != null){
 *                                      // End of message reached
 *                                      if(lineData.Length == 1 && lineData[0] == '.'){
 *                                              return;
 *                                      }
 *
 *                                      storeStream.Write(lineData,0,lineData.Length);
 *                                      storeStream.Write(crlf,0,crlf.Length);
 *                                      lineData = reader.ReadLine();
 *                              }
 *                      }
 *                      else{
 *                              throw new Exception("Server returned:" + line);
 *                      }
 *              }
 */
        /// <summary>
        /// Gets specified message.
        /// </summary>
        /// <param name="nr">Message number.</param>
        public byte[] GetMessage(int nr)
        {
            if (!m_Connected)
            {
                throw new Exception("You must connect first !");
            }

            if (!m_Authenticated)
            {
                throw new Exception("You must authenticate first !");
            }

            m_pSocket.SendLine("RETR " + nr.ToString());

            // Read first line of reply, check if it's ok
            string line = m_pSocket.ReadLine();

            if (line.StartsWith("+OK"))
            {
                MemoryStream  strm = new MemoryStream();
                ReadReplyCode code = m_pSocket.ReadData(strm, 100000000, "\r\n.\r\n", ".\r\n");
                if (code != ReadReplyCode.Ok)
                {
                    throw new Exception("Error:" + code.ToString());
                }
                return(Core.DoPeriodHandling(strm, false).ToArray());
            }
            else
            {
                throw new Exception("Server returned:" + line);
            }
        }
        /// <summary>
        /// Fetches messages headers or full messages data.
        /// </summary>
        /// <param name="startMsgNo">Start message number.</param>
        /// <param name="endMsgNo">End message number. -1 = last.</param>
        /// <param name="uidFetch">Specifies if startMsgNo and endMsgNo is message UIDs.</param>
        /// <param name="headersOnly">If true message headers are retrieved, otherwise full message retrieved.</param>
        /// <param name="setSeenFlag">If true message seen flag is setted.</param>
        /// <returns></returns>
        public IMAP_FetchItem[] FetchMessages(int startMsgNo, int endMsgNo, bool uidFetch, bool headersOnly, bool setSeenFlag)
        {
            if (!m_Connected)
            {
                throw new Exception("You must connect first !");
            }
            if (!m_Authenticated)
            {
                throw new Exception("You must authenticate first !");
            }
            if (m_SelectedFolder.Length == 0)
            {
                throw new Exception("You must select folder first !");
            }

            ArrayList fetchItems = new ArrayList();

            string endMsg = endMsgNo.ToString();

            if (endMsgNo < 1)
            {
                endMsg = "*";
            }
            string headers = "";

            if (headersOnly)
            {
                headers = "HEADER";
            }
            string uidF = "";

            if (uidFetch)
            {
                uidF = "UID ";
            }
            string seenFl = "";

            if (!setSeenFlag)
            {
                seenFl = ".PEEK";
            }

            m_pSocket.SendLine("a1 " + uidF + "FETCH " + startMsgNo + ":" + endMsg + " (UID RFC822.SIZE FLAGS BODY" + seenFl + "[" + headers + "])");

            // Must get lines with * and cmdTag + OK or cmdTag BAD/NO
            string reply = m_pSocket.ReadLine();

            if (reply.StartsWith("*"))
            {
                // Read multiline response
                while (reply.StartsWith("*"))
                {
                    // Fetch may return status response there, skip them
                    if (IsStatusResponse(reply))
                    {
                        continue;
                    }

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

                    int    uid        = 0;
                    int    size       = 0;
                    byte[] data       = null;
                    bool   isNewMsg   = true;
                    bool   isAnswered = false;

                    // Loop fetch result fields
                    for (int i = 0; i < 4; i++)
                    {
                        // UID field
                        if (reply.ToUpper().StartsWith("UID"))
                        {
                            reply = reply.Substring(3).Trim();                             // Remove UID word from reply
                            if (reply.IndexOf(" ") > -1)
                            {
                                uid   = Convert.ToInt32(reply.Substring(0, reply.IndexOf(" ")));
                                reply = reply.Substring(reply.IndexOf(" ")).Trim(); // Remove UID value from reply
                            }
                            else                                                    // Last param, no ending ' '
                            {
                                uid   = Convert.ToInt32(reply.Substring(0));
                                reply = "";
                            }
                        }
                        // RFC822.SIZE field
                        else if (reply.ToUpper().StartsWith("RFC822.SIZE"))
                        {
                            reply = reply.Substring(11).Trim();                             // Remove RFC822.SIZE word from reply
                            if (reply.IndexOf(" ") > -1)
                            {
                                size  = Convert.ToInt32(reply.Substring(0, reply.IndexOf(" ")));
                                reply = reply.Substring(reply.IndexOf(" ")).Trim();                                 // Remove RFC822.SIZE value from reply
                            }
                            else
                            {
                                // Last param, no ending ' '
                                size  = Convert.ToInt32(reply.Substring(0));
                                reply = "";
                            }
                        }
                        // BODY.PEEK field
                        else if (reply.ToUpper().StartsWith("BODY"))
                        {
                            // Get data. Find {dataLen}
                            int dataLen = Convert.ToInt32(reply.Substring(reply.IndexOf("{") + 1, reply.IndexOf("}") - reply.IndexOf("{") - 1));

                            MemoryStream  storeStrm = new MemoryStream(dataLen);
                            ReadReplyCode code      = m_pSocket.ReadData(storeStrm, dataLen, true);
                            if (code != ReadReplyCode.Ok)
                            {
                                throw new Exception("Read data:" + code.ToString());
                            }

                            data = storeStrm.ToArray();

                            // Read last fetch line, can be ')' or some params')'.
                            reply = m_pSocket.ReadLine().Trim();
                            if (!reply.EndsWith(")"))
                            {
                                throw new Exception("UnExpected fetch end ! value:" + reply);
                            }
                            else
                            {
                                reply = reply.Substring(0, reply.Length - 1).Trim();                                // Remove ')' from reply
                            }
                        }
                        // FLAGS field
                        else if (reply.ToUpper().StartsWith("FLAGS"))
                        {
                            if (reply.ToUpper().IndexOf("\\SEEN") > -1)
                            {
                                isNewMsg = false;
                            }
                            if (reply.ToUpper().IndexOf("\\ANSWERED") > -1)
                            {
                                isAnswered = true;
                            }

                            reply = reply.Substring(reply.IndexOf(")") + 1).Trim();                             // Remove FLAGS value from reply
                        }
                    }

                    fetchItems.Add(new IMAP_FetchItem(uid, size, data, headersOnly, isNewMsg, isAnswered));

                    reply = m_pSocket.ReadLine();
                }
            }

            reply = reply.Substring(reply.IndexOf(" ")).Trim();             // Remove Cmd tag

            if (!reply.ToUpper().StartsWith("OK"))
            {
                if (!reply.ToUpper().StartsWith("NO"))
                {
                    throw new Exception("Server returned:" + reply);
                }
            }

            IMAP_FetchItem[] retVal = new IMAP_FetchItem[fetchItems.Count];
            fetchItems.CopyTo(retVal);

            return(retVal);
        }