/// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="onwer">Owner collection.</param>
 /// <param name="id">Message ID.</param>
 /// <param name="uid">Message IMAP UID value.</param>
 /// <param name="internalDate">Message store date.</param>
 /// <param name="size">Message size in bytes.</param>
 /// <param name="flags">Message flags.</param>
 internal IMAP_Message(IMAP_MessageCollection onwer,string id,long uid,DateTime internalDate,long size,IMAP_MessageFlags flags)
 {
     m_pOwner       = onwer;
     m_ID           = id;
     m_UID          = uid;
     m_InternalDate = internalDate;
     m_Size         = size;
     m_Flags        = flags;
 }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="onwer">Owner collection.</param>
 /// <param name="id">Message ID.</param>
 /// <param name="uid">Message IMAP UID value.</param>
 /// <param name="internalDate">Message store date.</param>
 /// <param name="size">Message size in bytes.</param>
 /// <param name="flags">Message flags.</param>
 internal IMAP_Message(IMAP_MessageCollection onwer, string id, long uid, DateTime internalDate, long size, IMAP_MessageFlags flags)
 {
     m_pOwner       = onwer;
     m_ID           = id;
     m_UID          = uid;
     m_InternalDate = internalDate;
     m_Size         = size;
     m_Flags        = flags;
 }
        /// <summary>
        /// Deletes IMAP folder.
        /// </summary>
        /// <param name="accessingUser">User who accesses this method. 
        /// User needs r permission to call this method or Exception is thrown. 
        /// There is special user 'system' for which permission check is skipped.</param>
        /// <param name="folderOwnerUser">User who's folder it is.</param>
        /// <param name="folder">Folder what to delete. For example: Inbox,Public Folders/Documnets .</param>
        public void DeleteFolder(string accessingUser,string folderOwnerUser,string folder)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that user exists.
                *) Normalize folder. Remove '/' from folder start and end, ... .
                *) Do Shared Folders mapping.
                *) Don't allow to delete shared folders root folder.
                   For BoundedUser root don't allow root folder only,
                   for UsersShared root don't allow root + user name.
                *) Ensure that folder exists. Throw Exception if don't.
                *) See if user has sufficient permissions. User requires 'c' permission.
                   There is builtin user system, skip ACL for it.
                *) Create folder.
            */

            // Don't allow to delete inbox
            if(folder.ToLower() == "inbox"){
                throw new Exception("Can't delete inbox");
            }

            //--- Validate values -------------------//
            ArgsValidator.ValidateUserName(folderOwnerUser);
            ArgsValidator.ValidateFolder(folder);
            //---------------------------------------//

            // Ensure that user exists.
            if(!UserExists(folderOwnerUser)){
                throw new Exception("User '" + folderOwnerUser + "' doesn't exist !");
            }

            // Normalize folder. Remove '/' from folder start and end.
            folder = API_Utlis.NormalizeFolder(folder);

            // Do Shared Folders mapping.
            string originalFolder = folder;
            SharedFolderMapInfo mappedFolder = MapSharedFolder(originalFolder);
            if(mappedFolder.IsSharedFolder){
                folderOwnerUser = mappedFolder.FolderOnwer;
                folder = mappedFolder.Folder;

                /* Don't allow to delete shared folders root folder.
                   For BoundedUser root don't allow root folder only,
                   for UsersShared root don't allow root + user name.
                */

                // Main shared folder root.
                if(mappedFolder.SharedRootName.ToLower() == originalFolder.ToLower()){
                    throw new ArgumentException("Can't delete shared root folder '" + originalFolder + "' !");
                }
                // Users shared folder: root/username -> no folder
                if(folder == ""){
                    throw new ArgumentException("Can't delete shared root folder '" + originalFolder + "' !");
                }

                if(folderOwnerUser == "" || folder == ""){
                    throw new ArgumentException("Specified root folder '" + originalFolder + "' isn't accessible !");
                }
            }

            // Ensure that folder doesn't exists. Throw Exception if don't.
            if(!FolderExists(folderOwnerUser + "/" + folder)){
                throw new Exception("Folder '" + folder + "' doesn't exist !");
            }

            // See if user has sufficient permissions. User requires 'c' permission.
            //  There is builtin user system, skip ACL for it.
            if(accessingUser.ToLower() != "system"){
                IMAP_ACL_Flags acl = GetUserACL(folderOwnerUser,folder,accessingUser);
                if((acl & IMAP_ACL_Flags.c) == 0){
                    throw new InsufficientPermissionsException("Insufficient permissions for folder '" + accessingUser + "/" + folder + "' !");
                }
            }

            //--- Recycle bin handling ----------------------------------------------------------------------//
            if(Convert.ToBoolean(GetRecycleBinSettings().Rows[0]["DeleteToRecycleBin"])){
                IMAP_MessageCollection messages = new IMAP_MessageCollection();
                GetMessagesInfo("system",folderOwnerUser,folder,messages);
                foreach(IMAP_Message message in messages){
                    EmailMessageItems msgItems = new EmailMessageItems(message.ID,IMAP_MessageItems_enum.Message);
                    GetMessageItems("system",folderOwnerUser,folder,msgItems);
                    if(msgItems.MessageExists){
                        string subject = "<none>";
                        try{
                            subject = MimeUtils.ParseHeaderField("Subject:",msgItems.MessageStream);
                            subject = subject.Replace("\r","");
                            subject = subject.Replace("\n","");
                        }
                        catch{
                        }
                        msgItems.MessageStream.Position = 0;
                        byte[] data = new byte[msgItems.MessageStream.Length];
                        msgItems.MessageStream.Read(data,0,data.Length);

                        using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_StoreRecycleBinMessage")){
                            sqlCmd.AddParameter("_messageID" ,NpgsqlDbType.Varchar,Guid.NewGuid().ToString());
                            sqlCmd.AddParameter("_user"      ,NpgsqlDbType.Varchar,folderOwnerUser);
                            sqlCmd.AddParameter("_folder"    ,NpgsqlDbType.Varchar,folder);
                            sqlCmd.AddParameter("_subject"   ,NpgsqlDbType.Varchar,subject);
                            sqlCmd.AddParameter("_data"      ,NpgsqlDbType.Bytea  ,data);

                            DataSet ds = sqlCmd.Execute();
                        }
                    }
                }
            }
            //----------------------------------------------------------------------------------------------//

            //--- Delete folder
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteFolder")){
                sqlCmd.AddParameter("_folderOwnerUser",NpgsqlDbType.Varchar,folderOwnerUser);
                sqlCmd.AddParameter("_folderName"     ,NpgsqlDbType.Varchar,folder);

                DataSet ds = sqlCmd.Execute();
            }
        }
        /// <summary>
        /// Gets specified IMAP folder messages info. 
        /// </summary>
        /// <param name="accessingUser">User who accesses this method. 
        /// User needs r permission to call this method or Exception is thrown. 
        /// There is special user 'system' for which permission check is skipped.</param>
        /// <param name="folderOwnerUser">User who's folder it is.</param>
        /// <param name="folder">Folder what messages info to get. For example: Inbox,Public Folders/Documnets .</param>
        /// <param name="messages">IMAP_Messages collection where to store folder messages info.</param>
        public void GetMessagesInfo(string accessingUser,string folderOwnerUser,string folder,IMAP_MessageCollection messages)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that user exists.
                *) Normalize folder. Remove '/' from folder start and end, ... .
                *) Do Shared Folders mapping.
                *) Ensure that folder exists. Throw Exception if don't.
                *) See if user has sufficient permissions. User requires 'r' permission.
                    There is builtin user system, skip ACL for it.
                *) Fill messages info.
            */

            //--- Validate values -------------------//
            ArgsValidator.ValidateUserName(folderOwnerUser);
            ArgsValidator.ValidateFolder(folder);
            ArgsValidator.ValidateNotNull(messages);
            //---------------------------------------//

            // Ensure that user exists.
            if(!UserExists(folderOwnerUser)){
                throw new Exception("User '" + folderOwnerUser + "' doesn't exist !");
            }

            // Normalize folder. Remove '/' from folder start and end.
            folder = API_Utlis.NormalizeFolder(folder);

            // Do Shared Folders mapping.
            string originalFolder = folder;
            SharedFolderMapInfo mappedFolder = MapSharedFolder(originalFolder);
            if(mappedFolder.IsSharedFolder){
                folderOwnerUser = mappedFolder.FolderOnwer;
                folder = mappedFolder.Folder;

                if(folderOwnerUser == "" || folder == ""){
                    throw new ArgumentException("Specified root folder '" + originalFolder + "' isn't accessible !");
                }
            }

            // Ensure that folder exists. Throw Exception if don't.
            if(!FolderExists(folderOwnerUser + "/" + folder)){
                throw new Exception("Folder '" + folder + "' doesn't exist !");
            }

            // See if user has sufficient permissions. User requires 'r' permission.
            //  There is builtin user system, skip ACL for it.
            if(accessingUser.ToLower() != "system"){
                IMAP_ACL_Flags acl = GetUserACL(folderOwnerUser,folder,accessingUser);
                if((acl & IMAP_ACL_Flags.r) == 0){
                    throw new InsufficientPermissionsException("Insufficient permissions for folder '" + accessingUser + "/" + folder + "' !");
                }
            }

            //--- Fill messages info
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetMessagesInfo")){
                sqlCmd.AddParameter("_userName",NpgsqlDbType.Varchar,folderOwnerUser);
                sqlCmd.AddParameter("_folder"  ,NpgsqlDbType.Varchar,folder);

                DataSet ds = sqlCmd.Execute();
                ds.Tables[0].TableName = "lsMailStore";

                foreach(DataRow dr in ds.Tables["lsMailStore"].Rows){
                    string   messageID = dr["MessageID"].ToString();
                    int      size      = Convert.ToInt32(dr["Size"]);
                    DateTime date      = Convert.ToDateTime(dr["Date"]);
                    int      flags     = Convert.ToInt32(dr["MessageFlags"]);
                    int      uid       = Convert.ToInt32(dr["UID"]);

                    messages.Add(
                        messageID,
                        uid,
                        date,
                        size,
                        (IMAP_MessageFlags)flags
                    );
                }
            }
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="folder">Folder name.</param>
 internal IMAP_SelectedFolder(string folder)
 {
     m_Folder    = folder;
     m_pMessages = new IMAP_MessageCollection();
 }
        /// <summary>
        /// Gets specified IMAP folder messages info. 
        /// </summary>
        /// <param name="accessingUser">User who accesses this method. 
        /// User needs r permission to call this method or Exception is thrown. 
        /// There is special user 'system' for which permission check is skipped.</param>
        /// <param name="folderOwnerUser">User who's folder it is.</param>
        /// <param name="folder">Folder what messages info to get. For example: Inbox,Public Folders/Documnets .</param>
        /// <param name="messages">IMAP_Messages collection where to store folder messages info.</param>
        public void GetMessagesInfo(string accessingUser,string folderOwnerUser,string folder,IMAP_MessageCollection messages)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that user exists.
                *) Normalize folder. Remove '/' from folder start and end, ... .
                *) Do Shared Folders mapping.
                *) Ensure that folder exists. Throw Exception if don't.
                *) See if user has sufficient permissions. User requires 'r' permission.
                    There is builtin user system, skip ACL for it.
                *) Fill messages info.
            */

            //--- Validate values -------------------//
            ArgsValidator.ValidateUserName(folderOwnerUser);
            ArgsValidator.ValidateFolder(folder);
            ArgsValidator.ValidateNotNull(messages);
            //---------------------------------------//

            // Ensure that user exists.
            if(!UserExists(folderOwnerUser)){
                throw new Exception("User '" + folderOwnerUser + "' doesn't exist !");
            }

            // Normalize folder. Remove '/' from folder start and end.
            folder = API_Utlis.NormalizeFolder(folder);

            // Do Shared Folders mapping.
            string originalFolder = folder;
            SharedFolderMapInfo mappedFolder = MapSharedFolder(originalFolder);
            if(mappedFolder.IsSharedFolder){
                folderOwnerUser = mappedFolder.FolderOnwer;
                folder = mappedFolder.Folder;

                if(folderOwnerUser == "" || folder == ""){
                    throw new ArgumentException("Specified root folder '" + originalFolder + "' isn't accessible !");
                }
            }

            // Ensure that folder exists. Throw Exception if don't.
            if(!FolderExists(folderOwnerUser + "/" + folder)){
                throw new Exception("Folder '" + folder + "' doesn't exist !");
            }

            // See if user has sufficient permissions. User requires 'r' permission.
            //  There is builtin user system, skip ACL for it.
            if(accessingUser.ToLower() != "system"){
                IMAP_ACL_Flags acl = GetUserACL(folderOwnerUser,folder,accessingUser);
                if((acl & IMAP_ACL_Flags.r) == 0){
                    throw new InsufficientPermissionsException("Insufficient permissions for folder '" + accessingUser + "/" + folder + "' !");
                }
            }

            //--- Get message info from messages info db file ----------------------------------------------------//
            string userID         = GetUserID(folderOwnerUser);
            string folderFullPath = API_Utlis.PathFix(API_Utlis.DirectoryExists(API_Utlis.PathFix(m_MailStorePath + "Mailboxes\\" + folderOwnerUser + "\\" + Core.Encode_IMAP_UTF7_String(folder))) + "\\");

            List<FolderMessageInfo> messagesInfo = FolderMessagesInfoManager.GetMessagesInfo(folderFullPath);
            Dictionary<int,int>     flagsDB      = FolderMessageFlagsManager.GetFlags(userID,folderFullPath);
            foreach(FolderMessageInfo messageInfo in messagesInfo){
                IMAP_MessageFlags flags = IMAP_MessageFlags.Recent;
                if(flagsDB.ContainsKey(messageInfo.UID)){
                    flags = (IMAP_MessageFlags)flagsDB[messageInfo.UID];
                }
                messages.Add(
                    messageInfo.InternalDate.ToString("yyyyMMddHHmmss") + "_" + messageInfo.UID.ToString("D10"),
                    messageInfo.UID,
                    messageInfo.InternalDate.ToLocalTime(),
                    messageInfo.Size,
                    flags
                );
            }
            //--------------------------------------------------------------------------------------------------//
        }
        private void GetUserFolderMessagesInfo(string argsText)
        {
            try{
                string[] args = TextUtils.SplitQuotedString(argsText,' ',true);
                if(args.Length != 3){
                    this.Socket.WriteLine("-ERR Invalid arguments. Syntax: GetUserFolderMessagesInfo <virtualServerID> \"<user>\" \"<folder>\"");
                    return;
                }

                foreach(VirtualServer virtualServer in m_pServer.MailServer.VirtualServers){
                    if(virtualServer.ID.ToLower() == args[0].ToLower()){
                        // TODO: handle no existent user
                        // TODO: handle no existent folder

                        DataSet ds = new DataSet();
                        ds.Tables.Add("MessagesInfo");
                        ds.Tables["MessagesInfo"].Columns.Add("ID");
                        ds.Tables["MessagesInfo"].Columns.Add("UID");  // REMOVE ME: Now needed for delete message.
                        ds.Tables["MessagesInfo"].Columns.Add("Size",typeof(long));
                        ds.Tables["MessagesInfo"].Columns.Add("Flags",typeof(long));
                        ds.Tables["MessagesInfo"].Columns.Add("Envelope");

                        IMAP_MessageCollection messages = new IMAP_MessageCollection();
                        virtualServer.API.GetMessagesInfo("system",args[1],args[2],messages);
                        foreach(IMAP_Message message in messages){
                            try{
                                DataRow dr = ds.Tables["MessagesInfo"].NewRow();
                                dr["ID"]    = message.ID;
                                dr["UID"]   = message.UID;
                                dr["Size"]  = message.Size;
                                dr["Flags"] = message.Flags;

                                EmailMessageItems msgItems = new EmailMessageItems(message.ID,IMAP_MessageItems_enum.Envelope);
                                virtualServer.API.GetMessageItems("system",args[1],args[2],msgItems);
                                dr["Envelope"] = msgItems.Envelope;
                                ds.Tables["MessagesInfo"].Rows.Add(dr);
                            }
                            catch{
                            }
                        }

                        // Compress data
                        byte[] dsZipped = CompressDataSet(ds);

                        this.Socket.WriteLine("+OK " + dsZipped.Length);
                        this.Socket.Write(dsZipped);
                        return;
                    }
                }

                this.Socket.WriteLine("-ERR Specified virtual server with ID '" + args[0] + "' doesn't exist !");
            }
            catch(Exception x){
                this.Socket.WriteLine("-ERR " + x.Message);
            }
        }
        private void GetUserFolderInfo(string argsText)
        {
            /* GetUserFolderInfo <virtualServerID> "<folderOwnerUser>" "<folder>"
                  Responses:
                    +OK "<creationDate>" <numberOfMessages> <sizeUsed>
                    -ERR <errorText>
            */

            try{
                string[] args = TextUtils.SplitQuotedString(argsText,' ');
                if(args.Length != 3){
                    this.Socket.WriteLine("-ERR Invalid arguments. Syntax: GetUserFolderInfo <virtualServerID> \"<folderOwnerUser>\" \"<folder>\"");
                    return;
                }

                foreach(VirtualServer virtualServer in m_pServer.MailServer.VirtualServers){
                    if(virtualServer.ID.ToLower() == args[0].ToLower()){
                        // TODO: handle no existent user

                        IMAP_MessageCollection messages = new IMAP_MessageCollection();
                        virtualServer.API.GetMessagesInfo(
                            "system",
                            TextUtils.UnQuoteString(args[1]),
                            TextUtils.UnQuoteString(args[2]),
                            messages
                        );

                        // Calculate size used
                        long sizeUsed = 0;
                        foreach(IMAP_Message message in messages){
                            sizeUsed += message.Size;
                        }

                        DateTime creationTime = virtualServer.API.FolderCreationTime(TextUtils.UnQuoteString(args[1]),TextUtils.UnQuoteString(args[2]));

                        this.Socket.WriteLine("+OK \"" + creationTime.ToString("yyyyMMdd HH:mm:ss") + "\" " + messages.Count + " " + sizeUsed + "");
                        return;
                    }
                }

                this.Socket.WriteLine("-ERR Specified virtual server with ID '" + args[0] + "' doesn't exist !");
            }
            catch(Exception x){
                this.Socket.WriteLine("-ERR " + x.Message);
            }
        }
        private void pop3_Server_GetMessgesList(object sender,LumiSoft.Net.POP3.Server.GetMessagesInfo_EventArgs e)
        {
            try{
                string userName = e.UserName;

                IMAP_MessageCollection messages = new IMAP_MessageCollection();
                m_pApi.GetMessagesInfo(userName,userName,"Inbox",messages);
                for(int i=0;i<messages.Count;i++){
                    IMAP_Message msg = messages[i];
                    e.Messages.Add(msg.ID,msg.UID.ToString(),msg.Size);
                }
            }
            catch(Exception x){
                Error.DumpError(this.Name,x,new System.Diagnostics.StackTrace());
            }
        }
 /// <summary>
 /// Default constructor.
 /// </summary>
 /// <param name="folder">Folder name.</param>
 internal IMAP_SelectedFolder(string folder)
 {
     m_Folder    = folder;
     m_pMessages = new IMAP_MessageCollection();
 }