Click here to Skip to main content
15,885,760 members
Articles / Programming Languages / C#

LumiSoft MailServer

Rate me:
Please Sign up or sign in to vote.
3.79/5 (22 votes)
17 Nov 2006CPOL1 min read 321.7K   4.9K   74  
Full featured SMTP/POP3/IMAP server
using System;
using System.IO;
using System.Data;
using System.Data.SqlClient;
using System.Collections;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Text;
using System.Net;

using LumiSoft.Net;
using LumiSoft.Net.AUTH;
using LumiSoft.Net.IMAP.Server;
using LumiSoft.Net.IMAP;
using LumiSoft.Net.Mime;

namespace LumiSoft.MailServer
{
	/// <summary>
	/// Lumisoft Mail Server MSSQL API.
	/// </summary>
	public class mssql_API : IMailServerApi
	{
		private string m_ConStr = "";

		/// <summary>
		/// Default constructor.
		/// </summary>
		/// <param name="intitString"></param>
		public mssql_API(string intitString)
		{
			// connectionstring=
			string[] parameters = intitString.Replace("\r\n","\n").Split('\n');
			foreach(string param in parameters){
				if(param.ToLower().IndexOf("connectionstring=") > -1){
					m_ConStr = param.Substring(17);
				}
			}
          
            SqlConnectionStringBuilder b = new SqlConnectionStringBuilder(m_ConStr);
            string database = b.InitialCatalog;
            b.InitialCatalog = "";                
            using(SqlConnection con = new SqlConnection(b.ToString())){                    
                con.Open();

                // See if database exists
                try{
                    con.ChangeDatabase(database);
                }
                catch{
                    // Database don't exist, try to create it

                    try{
                        SqlCommand cmd = new SqlCommand();
                        cmd.Connection = con;
                        cmd.CommandType = CommandType.Text;
                        cmd.CommandText = "create database \"" + database + "\"";
                        cmd.ExecuteNonQuery();
                        con.ChangeDatabase(database);

                        // Create tables
                        cmd.CommandText = ResManager.GetText("tables.sql",System.Text.Encoding.Default);
                        cmd.ExecuteNonQuery();

                        // Create procedures                            
                        using(System.IO.StringReader r = new System.IO.StringReader(ResManager.GetText("procedures.sql",System.Text.Encoding.UTF8))){
                            string line = r.ReadLine().Trim();

                            StringBuilder command = new StringBuilder();
					        while(line != null){
						        line = line.TrimEnd();

							    if(line.ToLower() == "go"){
                                    cmd.CommandText = command.ToString();
								    cmd.ExecuteNonQuery();

								    command = new StringBuilder();
							    }
							    else{
							        command.Append(line + "\r\n");
							    }
        										
							    line = r.ReadLine();							    
						    }

                            // No ending GO, exec last statement block
                            if(command.ToString().Trim() != ""){
                                cmd.CommandText = command.ToString();
								cmd.ExecuteNonQuery();
                            }
                        }
                    }
                    catch(Exception x){                       
                        throw new Exception("Database '" + database + "' doesn''t exist ! Create failed, specified user doesn't have enough permisssions to create database ! Create database manually.");
                    }                    
                }
            }
		}


		#region Domain related

		#region method GetDomains

		/// <summary>
		/// Gets domain list.
		/// </summary>
		/// <returns></returns>
		public DataView GetDomains()
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetDomains")){
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Domains";

				return ds.Tables["Domains"].DefaultView;
			}
		}

		#endregion


		#region method AddDomain

		/// <summary>
		/// Adds new domain.
		/// </summary>
		/// <param name="domainID">Domain ID. Suggested value is Guid.NewGuid() .</param>
		/// <param name="domainName">Domain name. Eg. yourDomain.com .</param>
		/// <param name="description">Domain description.</param>
		/// <remarks>Throws exception if specified domain already exists.</remarks>
		public void AddDomain(string domainID,string domainName,string description)
		{
			if(domainID.Length == 0){
				throw new Exception("You must specify domainID");
			}
			ArgsValidator.ValidateDomainName(domainName);

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddDomain")){
				sqlCmd.AddParameter("@DomainID"   ,SqlDbType.NVarChar,domainID);
				sqlCmd.AddParameter("@DomainName" ,SqlDbType.NVarChar,domainName);
				sqlCmd.AddParameter("@Description",SqlDbType.NVarChar,description);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Domains";

				if(ds.Tables["Domains"].Rows.Count > 0 && ds.Tables["Domains"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Domains"].Rows[0]["ErrorText"].ToString());
				}
			}
		}

		#endregion

		#region function DeleteDomain

		/// <summary>
		/// Deletes specified domain.
		/// </summary>
		/// <param name="domainID">Domain ID. Use <see cref="IMailServerApi.GetDomains">GetDomains()</see> to get valid values.</param>
		/// <remarks>Deletes specified domain and all domain related data (users,mailing lists,routes).</remarks>
		public void DeleteDomain(string domainID)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteDomain")){
				sqlCmd.AddParameter("@DomainID" ,SqlDbType.NVarChar,domainID);
							
				DataSet ds = sqlCmd.Execute();
			}
		}

		#endregion

        #region method UpdateDomain

        /// <summary>
        /// Updates specified domain data.
        /// </summary>
        /// <param name="domainID">Domain ID which to update.</param>
        /// <param name="domainName">Domain name.</param>
        /// <param name="description">Domain description.</param>
        public void UpdateDomain(string domainID,string domainName,string description)
        {
            if(domainID.Length == 0){
				throw new Exception("You must specify domainID");
			}
            ArgsValidator.ValidateDomainName(domainName);

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateDomain")){
				sqlCmd.AddParameter("@DomainID"   ,SqlDbType.NVarChar,domainID);
				sqlCmd.AddParameter("@DomainName" ,SqlDbType.NVarChar,domainName);
				sqlCmd.AddParameter("@Description",SqlDbType.NVarChar,description);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Domains";

				if(ds.Tables["Domains"].Rows.Count > 0 && ds.Tables["Domains"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Domains"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion

		#region function DomainExists

		/// <summary>
		/// Checks if specified domain exists.
		/// </summary>
		/// <param name="source">Domain name or email address.</param>
		/// <returns>Returns true if domain exists.</returns>
		public bool DomainExists(string source)
		{
			// Source is Emails
			if(source.IndexOf("@") > -1){
				source = source.Substring(source.IndexOf("@") + 1);
			}

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DomainExists")){
				sqlCmd.AddParameter("@DomainName",SqlDbType.NVarChar,source);

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

				if(ds.Tables["Domains"].Rows.Count > 0){
					return true;
				}
			}

			return false;
		}

		#endregion

		#endregion


		#region User and Groups related

		#region function GetUsers

		/// <summary>
		/// Gets user list in specified domain.
		/// </summary>
		/// <param name="domainName">Domain which user list to retrieve.To get all use value 'ALL'.</param>
		/// <returns></returns>
		public DataView GetUsers(string domainName)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetUsers")){
				if(domainName != "ALL"){
					sqlCmd.AddParameter("@DomainName",SqlDbType.NVarChar,domainName);
				}

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

				return ds.Tables["Users"].DefaultView;
			}
		}

		#endregion


        #region method GetUserID

        /// <summary>
        /// Gets user ID from user name. Returns null if user doesn't exist.
        /// </summary>
        /// <param name="userName">User name.</param>
        /// <returns>Returns user ID or null if user doesn't exist.</returns>
        public string GetUserID(string userName)
        {   
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"select UserID from lsUsers where (UserName = '" + userName + "')")){				
                sqlCmd.CommandType = CommandType.Text;
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Users";

                if(ds.Tables["Users"].Rows.Count > 0){
                    return ds.Tables["Users"].Rows[0]["UserID"].ToString();
                }
			}
     
            return null;
        }

        #endregion

		#region function AddUser

		/// <summary>
		/// Adds new user to specified domain.
		/// </summary>
		/// <param name="userID">User ID. Suggested value is Guid.NewGuid() .</param>
		/// <param name="userName">User login name.</param>
		/// <param name="fullName">User full name.</param> 
		/// <param name="password">User login password.</param>
		/// <param name="description">User description.</param>
		/// <param name="domainName">Domain where to add user. Use <see cref="IMailServerApi.GetDomains">GetDomains()</see> to get valid values.</param>
		/// <param name="mailboxSize">Maximum mailbox size.</param>
		/// <param name="enabled">Sepcifies if user is enabled.</param>
		/// <param name="permissions">Specifies user permissions.</param>
		/// <remarks>Throws exception if specified user already exists.</remarks>
		public void AddUser(string userID,string userName,string fullName,string password,string description,string domainName,int mailboxSize,bool enabled,UserPermissions_enum permissions)
		{
			if(userID.Length == 0){
				throw new Exception("You must specify userID");
			}
			if(userName.Length == 0){
				throw new Exception("You must specify userName");
			}

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddUser")){
				sqlCmd.AddParameter("@UserID"      ,SqlDbType.NVarChar,userID);
				sqlCmd.AddParameter("@FullName"    ,SqlDbType.NVarChar,fullName);
				sqlCmd.AddParameter("@UserName"    ,SqlDbType.NVarChar,userName);
				sqlCmd.AddParameter("@Password"    ,SqlDbType.NVarChar,password);
				sqlCmd.AddParameter("@Description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@DomainName"  ,SqlDbType.NVarChar,domainName);
				sqlCmd.AddParameter("@MailboxSize" ,SqlDbType.NVarChar,mailboxSize);
				sqlCmd.AddParameter("@Enabled"     ,SqlDbType.Bit,enabled);
				sqlCmd.AddParameter("@permissions" ,SqlDbType.Int,permissions);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Users";

				if(ds.Tables["Users"].Rows.Count > 0 && ds.Tables["Users"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Users"].Rows[0]["ErrorText"].ToString());
				}						
			}			
		}
	
		#endregion

		#region function DeleteUser

		/// <summary>
		/// Deletes user.
		/// </summary>
		/// <param name="userID">User id of the user which to delete. Use <see cref="IMailServerApi.GetUsers">>GetUsers()</see> to get valid values.</param>
		public void DeleteUser(string userID)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteUser")){
				sqlCmd.AddParameter("@UserID" ,SqlDbType.NVarChar,userID);
							
				DataSet ds = sqlCmd.Execute();
			}
		}
		
		#endregion

		#region function UpdateUser

		/// <summary>
		/// Updates new user to specified domain.
		/// </summary>
		/// <param name="userID">User id of the user which to update. Use <see cref="IMailServerApi.GetUsers">>GetUsers()</see> to get valid values.</param>
		/// <param name="userName">User login name.</param>
		/// <param name="fullName">User full name.</param>
		/// <param name="password">User login password.</param>
		/// <param name="description">User description.</param>
		/// <param name="domainName">Domain where to add user. Use <see cref="IMailServerApi.GetDomains">>GetDomains()</see> to get valid values.</param>
		/// <param name="mailboxSize">Maximum mailbox size.</param>
		/// <param name="enabled">Sepcifies if user is enabled.</param>
		/// <param name="permissions">Specifies user permissions.</param>
		public void UpdateUser(string userID,string userName,string fullName,string password,string description,string domainName,int mailboxSize,bool enabled,UserPermissions_enum permissions)
		{
			if(userName.Length == 0){
				throw new Exception("You must specify userName");
			}

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateUser")){
				sqlCmd.AddParameter("@UserID"      ,SqlDbType.NVarChar,userID);
				sqlCmd.AddParameter("@UserName"    ,SqlDbType.NVarChar,userName);
				sqlCmd.AddParameter("@FullName"    ,SqlDbType.NVarChar,fullName);
				sqlCmd.AddParameter("@Password"    ,SqlDbType.NVarChar,password);
				sqlCmd.AddParameter("@Description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@DomainName"  ,SqlDbType.NVarChar,domainName);
				sqlCmd.AddParameter("@MailboxSize" ,SqlDbType.NVarChar,mailboxSize);
				sqlCmd.AddParameter("@Enabled"     ,SqlDbType.Bit,enabled);
				sqlCmd.AddParameter("@permissions" ,SqlDbType.Int,permissions);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Users";

				if(ds.Tables["Users"].Rows.Count > 0 && ds.Tables["Users"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Users"].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion

		#region function AddUserAddress

		/// <summary>
		/// Add new email address to user.
		/// </summary>
		/// <param name="userName">User name. Use <see cref="IMailServerApi.GetUsers">>GetUsers()</see> to get valid values.</param>
		/// <param name="emailAddress">Email address to add.</param>
		/// <remarks>Throws exception if specified user email address exists.</remarks>
		public void AddUserAddress(string userName,string emailAddress)
		{
			if(userName.Length == 0){
				throw new Exception("You must specify userName");
			}
			if(emailAddress.Length == 0){
				throw new Exception("You must specify address");
			}

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddUserAddress")){
				sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);
				sqlCmd.AddParameter("@Address" ,SqlDbType.NVarChar,emailAddress);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "UserAddresses";

				if(ds.Tables["UserAddresses"].Rows.Count > 0 && ds.Tables["UserAddresses"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["UserAddresses"].Rows[0]["ErrorText"].ToString());
				}
			}
		}

		#endregion

		#region function DeleteUserAddress

		/// <summary>
		/// Deletes specified email address from user. 
		/// </summary>
		/// <param name="emailAddress">Email address to add.</param>
		public void DeleteUserAddress(string emailAddress)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteUserAddress")){
				sqlCmd.AddParameter("@emailAddress" ,SqlDbType.VarChar,emailAddress);
							
				DataSet ds = sqlCmd.Execute();
			}
		}

		#endregion

		#region function GetUserAddresses

		/// <summary>
		/// Gets user email addresses.
		/// </summary>
		/// <param name="userName"> Use <see cref="IMailServerApi.GetUsers">GetUsers()</see> to get valid values.</param>
		public DataView GetUserAddresses(string userName)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetUserAddresses")){
				if(userName.Length > 0){
					sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);
				}

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

				return ds.Tables["UserAddresses"].DefaultView;
			}
		}

		#endregion

		#region function UserExists

		/// <summary>
		/// Checks if user exists.
		/// </summary>
		/// <param name="userName">User name.</param>
		/// <returns>Returns true if user exists.</returns>
		public bool UserExists(string userName)
		{			
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetUserProperties")){
				sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);

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

				if(ds.Tables["Users"].Rows.Count > 0){
					return true;
				}
			}

			return false;
		}
		
		#endregion

		#region function MapUser

		/// <summary>
		/// Maps email address to mailbox.
		/// </summary>
		/// <param name="emailAddress"></param>
		/// <returns>Returns mailbox or null if such email address won't exist.</returns>
		public string MapUser(string emailAddress)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_MapUser")){
				sqlCmd.AddParameter("@EmailAddress",SqlDbType.NVarChar,emailAddress);

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

				if(ds.Tables["UserAddresses"].Rows.Count > 0){
					return ds.Tables["UserAddresses"].Rows[0]["UserName"].ToString();
				}
			}

			return null;
		}
		
		#endregion

		#region function ValidateMailboxSize

		/// <summary>
		/// Checks if specified mailbox size is exceeded.
		/// </summary>
		/// <param name="userName">User name. Use <see cref="IMailServerApi.GetUsers">GetUsers()</see> to get valid values.</param>
		/// <returns>Returns true if exceeded.</returns>
		public bool ValidateMailboxSize(string userName)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_ValidateMailboxSize")){
				sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);

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

				if(ds.Tables["Detail"].Rows.Count > 0){
					return !Convert.ToBoolean(ds.Tables["Detail"].Rows[0]["Validated"]);
				}
			}
			
			return false;
		}
		
		#endregion

        #region method GetUserPermissions

        /// <summary>
        /// Gets specified user permissions.
        /// </summary>
        /// <param name="userName">User name.</param>
        /// <returns></returns>
        public UserPermissions_enum GetUserPermissions(string userName)
        {
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetUserProperties")){
				sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);

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

				if(ds.Tables["Users"].Rows.Count > 0){
					return (UserPermissions_enum)Convert.ToInt32(ds.Tables["Users"].Rows[0]["Permissions"]);
				}
			}

            return UserPermissions_enum.None;
        }

        #endregion

        #region method GetUserLastLoginTime

        /// <summary>
        /// Gets user last login time.
        /// </summary>
        /// <param name="userName">User name who's last login time to get.</param>
        /// <returns>User last login time.</returns>
        public DateTime GetUserLastLoginTime(string userName)
        {
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetUserProperties")){
				sqlCmd.AddParameter("@userName",SqlDbType.NVarChar,userName);

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

				if(ds.Tables["Users"].Rows.Count > 0){
					return Convert.ToDateTime(ds.Tables["Users"].Rows[0]["LastLoginTime"]);
				}

                return DateTime.Now;
			}
        }

        #endregion

        #region method UpdateUserLastLoginTime

        /// <summary>
        /// Updates user last login time.
        /// </summary>
        /// <param name="userName">User name who's last login time to update.</param>
        public void UpdateUserLastLoginTime(string userName)
        {
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateUserLastLoginTime")){
				sqlCmd.AddParameter("@userName",SqlDbType.NVarChar,userName);

				DataSet ds = sqlCmd.Execute();
			}
        }

        #endregion


        #region function GetUserRemoteServers

		/// <summary>
		/// Gets user pop3 remote accounts.
		/// </summary>
		/// <param name="userName">User name. Use <see cref="IMailServerApi.GetUsers">GetUsers()</see> to get valid values.</param>
		/// <returns></returns>
		public DataView GetUserRemoteServers(string userName)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetUserRemoteServers")){
				if(userName.Length > 0){
					sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);
				}

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

				return ds.Tables["UserRemoteServers"].DefaultView;
			}
		}

		#endregion

		#region function AddUserRemoteServer

		/// <summary>
		/// AAdds new remote pop3 server to user.
		/// </summary>
		/// <param name="serverID">Server ID. Suggested value is Guid.NewGuid() .</param>
		/// <param name="userName">User name. Use <see cref="IMailServerApi.GetUsers">GetUsers()</see> to get valid values.</param>
		/// <param name="description">Remote server description.</param>
		/// <param name="remoteServer">Remote server name.</param>
		/// <param name="remotePort">Remote server port.</param>
		/// <param name="remoteUser">Remote server user name.</param>
		/// <param name="remotePassword">Remote server password.</param>
		/// <param name="useSSL">Specifies if SSL must be used to connect to remote server.</param>
		/// <param name="enabled">Specifies if remote server is enabled.</param>
		/// <remarks>Throws exception if specified user remote server already exists.</remarks>
		public void AddUserRemoteServer(string serverID,string userName,string description,string remoteServer,int remotePort,string remoteUser,string remotePassword,bool useSSL,bool enabled)
		{
			if(serverID.Length == 0){
				throw new Exception("You must specify serverID");
			}
			if(userName.Length == 0){
				throw new Exception("You must specify userName");
			}

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddUserRemoteServer")){
				sqlCmd.AddParameter("@ServerID"       ,SqlDbType.NVarChar,serverID);
				sqlCmd.AddParameter("@UserName"       ,SqlDbType.NVarChar,userName);
                sqlCmd.AddParameter("@Description"    ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@RemoteServer"   ,SqlDbType.NVarChar,remoteServer);
				sqlCmd.AddParameter("@RemotePort"     ,SqlDbType.Int     ,remotePort);
				sqlCmd.AddParameter("@RemoteUserName" ,SqlDbType.NVarChar,remoteUser);
				sqlCmd.AddParameter("@RemotePassword" ,SqlDbType.NVarChar,remotePassword);
                sqlCmd.AddParameter("@UseSSL"         ,SqlDbType.Bit     ,useSSL);
                sqlCmd.AddParameter("@Enabled"        ,SqlDbType.Bit     ,enabled);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Result";

				if(ds.Tables["Result"].Rows.Count > 0 && ds.Tables["Result"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Result"].Rows[0]["ErrorText"].ToString());
				}
			}
		}

		#endregion

		#region function DeleteUserRemoteServer

		/// <summary>
		/// Deletes specified pop3 remote account from user.
		/// </summary>
		/// <param name="serverID">Remote server ID. Use <see cref="IMailServerApi.GetUserRemoteServers">GetUserRemoteServers()</see> to get valid values.</param>
		public void DeleteUserRemoteServer(string serverID)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteUserRemoteServer")){
				sqlCmd.AddParameter("@ServerID" ,SqlDbType.UniqueIdentifier,serverID);
				
				DataSet ds = sqlCmd.Execute();
			}
		}

		#endregion

        #region mehtod UpdateUserRemoteServer

        /// <summary>
		/// Updates user remote pop3 server.
		/// </summary>
		/// <param name="serverID">Server ID. Suggested value is Guid.NewGuid() .</param>
		/// <param name="userName">User name. Use <see cref="IMailServerApi.GetUsers">GetUsers()</see> to get valid values.</param>
		/// <param name="description">Remote server description.</param>
		/// <param name="remoteServer">Remote server name.</param>
		/// <param name="remotePort">Remote server port.</param>
		/// <param name="remoteUser">Remote server user name.</param>
		/// <param name="remotePassword">Remote server password.</param>
		/// <param name="useSSL">Specifies if SSL must be used to connect to remote server.</param>
		/// <param name="enabled">Specifies if remote server is enabled.</param>
		/// <remarks>Throws exception if specified user remote server already exists.</remarks>
		public void UpdateUserRemoteServer(string serverID,string userName,string description,string remoteServer,int remotePort,string remoteUser,string remotePassword,bool useSSL,bool enabled)
        {
            if(serverID.Length == 0){
				throw new Exception("You must specify serverID");
			}
			if(userName.Length == 0){
				throw new Exception("You must specify userName");
			}

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateUserRemoteServer")){
				sqlCmd.AddParameter("@ServerID"       ,SqlDbType.NVarChar,serverID);
				sqlCmd.AddParameter("@UserName"       ,SqlDbType.NVarChar,userName);
                sqlCmd.AddParameter("@Description"    ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@RemoteServer"   ,SqlDbType.NVarChar,remoteServer);
				sqlCmd.AddParameter("@RemotePort"     ,SqlDbType.Int     ,remotePort);
				sqlCmd.AddParameter("@RemoteUserName" ,SqlDbType.NVarChar,remoteUser);
				sqlCmd.AddParameter("@RemotePassword" ,SqlDbType.NVarChar,remotePassword);
                sqlCmd.AddParameter("@UseSSL"         ,SqlDbType.Bit     ,useSSL);
                sqlCmd.AddParameter("@Enabled"        ,SqlDbType.Bit     ,enabled);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Result";

				if(ds.Tables["Result"].Rows.Count > 0 && ds.Tables["Result"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Result"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion

		
		#region method GetUserMessageRules

		/// <summary>
		/// Gets user message  rules.
		/// </summary>
		/// <param name="userName">User name.</param>
		/// <returns></returns>
		public DataView GetUserMessageRules(string userName)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetUserMessageRules")){
				if(userName.Length > 0){
					sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);
				}

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

                // We need to convert MatchExpression to string. 
                // MSSQL won't allow strings bigger than 400 chars, because of it we store it as Image.
                ds.Tables[0].Columns["MatchExpression"].ColumnName = "MatchExpressionOld";
                ds.Tables[0].Columns.Add("MatchExpression",typeof(string));
                foreach(DataRow dr in ds.Tables[0].Rows){
                    dr["MatchExpression"] = System.Text.Encoding.Default.GetString((byte[])dr["MatchExpressionOld"]);
                }
                ds.Tables[0].Columns.Remove("MatchExpressionOld");

				return ds.Tables["UserMessageRules"].DefaultView;
			}
		}

		#endregion

		#region method AddUserMessageRule

		/// <summary>
        /// Adds new user message rule.
        /// </summary>
        /// <param name="userID">User who owns specified rule.</param>
        /// <param name="ruleID">Rule ID. Guid.NewID().ToString() is suggested.</param>
        /// <param name="cost">Cost specifies in what order rules are processed. Costs with lower values are processed first.</param>
        /// <param name="enabled">Specifies if rule is enabled.</param>
        /// <param name="checkNextRule">Specifies when next rule is checked.</param>
        /// <param name="description">Rule description.</param>
        /// <param name="matchExpression">Rule match expression.</param>
        public void AddUserMessageRule(string userID,string ruleID,long cost,bool enabled,GlobalMessageRule_CheckNextRule_enum checkNextRule,string description,string matchExpression)
        {
            if(userID == null || userID == ""){
                throw new Exception("Invalid userID value, userID can't be '' or null !");
            }
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddUserMessageRule")){
                sqlCmd.AddParameter("@userID"          ,SqlDbType.NVarChar,userID);
				sqlCmd.AddParameter("@ruleID"          ,SqlDbType.NVarChar,ruleID);
				sqlCmd.AddParameter("@cost"            ,SqlDbType.BigInt  ,cost);
				sqlCmd.AddParameter("@enabled"         ,SqlDbType.Bit     ,enabled);
				sqlCmd.AddParameter("@checkNextRule"   ,SqlDbType.Int     ,checkNextRule);
				sqlCmd.AddParameter("@description"     ,SqlDbType.NVarChar,description);
                sqlCmd.AddParameter("@matchExpression" ,SqlDbType.Image   ,System.Text.Encoding.Default.GetBytes(matchExpression));
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "UserMessageRules";

				if(ds.Tables["UserMessageRules"].Rows.Count > 0 && ds.Tables["UserMessageRules"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["UserMessageRules"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

		#endregion

		#region method DeleteUserMessageRule

		/// <summary>
		/// Deletes specified user message rule.
		/// </summary>
        /// <param name="userID">User who owns specified rule.</param>
		/// <param name="ruleID">Rule ID.</param>
		public void DeleteUserMessageRule(string userID,string ruleID)
        {
            if(userID == null || userID == ""){
                throw new Exception("Invalid userID value, userID can't be '' or null !");
            }
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteUserMessageRule")){
                sqlCmd.AddParameter("@userID" ,SqlDbType.NVarChar,userID);
				sqlCmd.AddParameter("@ruleID" ,SqlDbType.NVarChar,ruleID);
							
				DataSet ds = sqlCmd.Execute();
			}
        }

		#endregion

		#region method UpdateUserMessageRule

		/// <summary>
        /// Updates specified user message rule.
        /// </summary>
        /// <param name="userID">User who owns specified rule.</param>
        /// <param name="ruleID">Rule ID.</param>
        /// <param name="cost">Cost specifies in what order rules are processed. Costs with lower values are processed first.</param>
        /// <param name="enabled">Specifies if rule is enabled.</param>
        /// <param name="checkNextRule">Specifies when next rule is checked.</param>
        /// <param name="description">Rule description.</param>
        /// <param name="matchExpression">Rule match expression.</param>
        public void UpdateUserMessageRule(string userID,string ruleID,long cost,bool enabled,GlobalMessageRule_CheckNextRule_enum checkNextRule,string description,string matchExpression)
        {
            if(userID == null || userID == ""){
                throw new Exception("Invalid userID value, userID can't be '' or null !");
            }
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateUserMessageRule")){
                sqlCmd.AddParameter("@userID"          ,SqlDbType.NVarChar,userID);
				sqlCmd.AddParameter("@ruleID"          ,SqlDbType.NVarChar,ruleID);
				sqlCmd.AddParameter("@cost"            ,SqlDbType.BigInt  ,cost);
				sqlCmd.AddParameter("@enabled"         ,SqlDbType.Bit     ,enabled);
				sqlCmd.AddParameter("@checkNextRule"   ,SqlDbType.Int     ,checkNextRule);
				sqlCmd.AddParameter("@description"     ,SqlDbType.NVarChar,description);
                sqlCmd.AddParameter("@matchExpression" ,SqlDbType.Image   ,System.Text.Encoding.Default.GetBytes(matchExpression));
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "UserMessageRules";

				if(ds.Tables["UserMessageRules"].Rows.Count > 0 && ds.Tables["UserMessageRules"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["UserMessageRules"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

		#endregion

        #region method GetUserMessageRuleActions

        /// <summary>
        /// Gets specified user message rule actions.
        /// </summary>
        /// <param name="userID">User who owns specified rule.</param>
        /// <param name="ruleID">Rule ID of rule which actions to get.</param>
        public DataView GetUserMessageRuleActions(string userID,string ruleID)
        {
            if(userID == null || userID == ""){
                throw new Exception("Invalid userID value, userID can't be '' or null !");
            }
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetUserMessageRuleActions")){
                sqlCmd.AddParameter("@userID",SqlDbType.NVarChar,userID);
			    sqlCmd.AddParameter("@ruleID",SqlDbType.NVarChar,ruleID);

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

				return ds.Tables["UserMessageRuleActions"].DefaultView;
			}
        }

        #endregion

        #region method AddUserMessageRuleAction

        /// <summary>
        /// Adds action to specified user message rule.
        /// </summary>
        /// <param name="userID">User who owns specified rule.</param>
        /// <param name="ruleID">Rule ID to which to add this action.</param>
        /// <param name="actionID">Action ID. Guid.NewID().ToString() is suggested.</param>
        /// <param name="description">Action description.</param>
        /// <param name="actionType">Action type.</param>
        /// <param name="actionData">Action data. Data structure depends on action type.</param>
        public void AddUserMessageRuleAction(string userID,string ruleID,string actionID,string description,GlobalMessageRuleAction_enum actionType,byte[] actionData)
        {
            if(userID == null || userID == ""){
                throw new Exception("Invalid userID value, userID can't be '' or null !");
            }
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }
            if(actionID == null || actionID == ""){
                throw new Exception("Invalid actionID value, actionID can't be '' or null !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddUserMessageRuleAction")){
                sqlCmd.AddParameter("@userID"      ,SqlDbType.NVarChar,userID);
				sqlCmd.AddParameter("@ruleID"      ,SqlDbType.NVarChar,ruleID);
				sqlCmd.AddParameter("@actionID"    ,SqlDbType.NVarChar,actionID);
				sqlCmd.AddParameter("@description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@actionType"  ,SqlDbType.Int     ,(int)actionType);
                sqlCmd.AddParameter("@actionData"  ,SqlDbType.Image   ,actionData);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "UserMessageRuleActions";

				if(ds.Tables["UserMessageRuleActions"].Rows.Count > 0 && ds.Tables["UserMessageRuleActions"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["UserMessageRuleActions"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion

        #region method DeleteUserMessageRuleAction

        /// <summary>
        /// Deletes specified action from specified user message rule.
        /// </summary>
        /// <param name="userID">User who owns specified rule.</param>
        /// <param name="ruleID">Rule ID which action to delete.</param>
        /// <param name="actionID">Action ID of action which to delete.</param>
        public void DeleteUserMessageRuleAction(string userID,string ruleID,string actionID)
        {
            if(userID == null || userID == ""){
                throw new Exception("Invalid userID value, userID can't be '' or null !");
            }
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }
            if(actionID == null || actionID == ""){
                throw new Exception("Invalid actionID value, actionID can't be '' or null !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteUserMessageRuleAction")){
                sqlCmd.AddParameter("@userID"   ,SqlDbType.NVarChar,userID);
				sqlCmd.AddParameter("@ruleID"   ,SqlDbType.NVarChar,ruleID);
                sqlCmd.AddParameter("@actionID" ,SqlDbType.NVarChar,actionID);
							
				DataSet ds = sqlCmd.Execute();
			}
        }

        #endregion

        #region method UpdateUserMessageRuleAction

        /// <summary>
        /// Updates specified rule action.
        /// </summary>
        /// <param name="userID">User who owns specified rule.</param>
        /// <param name="ruleID">Rule ID which action to update.</param>
        /// <param name="actionID">Action ID of action which to update.</param>
        /// <param name="description">Action description.</param>
        /// <param name="actionType">Action type.</param>
        /// <param name="actionData">Action data. Data structure depends on action type.</param>
        public void UpdateUserMessageRuleAction(string userID,string ruleID,string actionID,string description,GlobalMessageRuleAction_enum actionType,byte[] actionData)
        {
            if(userID == null || userID == ""){
                throw new Exception("Invalid userID value, userID can't be '' or null !");
            }
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }
            if(actionID == null || actionID == ""){
                throw new Exception("Invalid actionID value, actionID can't be '' or null !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateUserMessageRuleAction")){
                sqlCmd.AddParameter("@userID"      ,SqlDbType.NVarChar,userID);
				sqlCmd.AddParameter("@ruleID"      ,SqlDbType.NVarChar,ruleID);
				sqlCmd.AddParameter("@actionID"    ,SqlDbType.NVarChar,actionID);
				sqlCmd.AddParameter("@description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@actionType"  ,SqlDbType.Int     ,(int)actionType);
                sqlCmd.AddParameter("@actionData"  ,SqlDbType.Image   ,actionData);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "UserMessageRuleActions";

				if(ds.Tables["UserMessageRuleActions"].Rows.Count > 0 && ds.Tables["UserMessageRuleActions"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["UserMessageRuleActions"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion


		#region function AuthUser

		/// <summary>
		/// Authenticates user.
		/// </summary>
		/// <param name="userName">User name.</param>
		/// <param name="passwData">Password data.</param>
		/// <param name="authData">Authentication specific data(as tag).</param>
		/// <param name="authType">Authentication type.</param>
		/// <returns></returns>
		public DataSet AuthUser(string userName,string passwData,string authData,AuthType authType)
		{
			DataSet retVal = new DataSet();
			DataTable dt = retVal.Tables.Add("Result");
			dt.Columns.Add("Result");
			dt.Columns.Add("ReturnData");
			DataRow drx = dt.NewRow();
			drx["Result"] = "false";
			drx["ReturnData"] = "";
			dt.Rows.Add(drx);

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetUserProperties")){
				sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);
						
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Users";

				if(ds.Tables["Users"].Rows.Count > 0){
					string password = ds.Tables["Users"].Rows[0]["PASSWORD"].ToString().ToLower();

					switch(authType)
					{
						case AuthType.APOP:
							if(AuthHelper.Apop(password,authData) == passwData){
								drx["Result"] = "true";
								return retVal;
							}
							break;

						case AuthType.CRAM_MD5:	
							if(AuthHelper.Cram_Md5(password,authData) == passwData){
								drx["Result"] = "true";
								return retVal;
							}
							break;

						case AuthType.DIGEST_MD5:	
							string realm      = "";
							string nonce      = "";
							string cnonce     = "";
							string digest_uri = "";
							foreach(string clntRespParam in authData.Split(',')){
								if(clntRespParam.StartsWith("realm=")){
									realm = clntRespParam.Split(new char[]{'='},2)[1].Replace("\"","");
								}
								else if(clntRespParam.StartsWith("nonce=")){
									nonce = clntRespParam.Split(new char[]{'='},2)[1].Replace("\"","");
								}									
								else if(clntRespParam.StartsWith("cnonce=")){
									cnonce = clntRespParam.Split(new char[]{'='},2)[1].Replace("\"","");
								}									
								else if(clntRespParam.StartsWith("digest-uri=")){
									digest_uri = clntRespParam.Split(new char[]{'='},2)[1].Replace("\"","");
								}
							}

							if(passwData == AuthHelper.Digest_Md5(true,realm,userName,password,nonce,cnonce,digest_uri)){
								string returnData = AuthHelper.Digest_Md5(false,realm,userName,password,nonce,cnonce,digest_uri);
									
								drx["Result"] = "true";
								drx["ReturnData"] = returnData;
								return retVal;
							}

							break;

						case AuthType.Plain:
							if(password == passwData.ToLower()){
								drx["Result"] = "true";
								return retVal;
							}
							break;						
					}
				}
			}			

			return retVal;
		}
		
		#endregion


        #region method GroupExists

        /// <summary>
        /// Gets if specified user group exists.
        /// </summary>
        /// <param name="groupName">Group name.</param>
        /// <returns>Returns true, if user group exists.</returns>
        public bool GroupExists(string groupName)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Check if group exists.
            */

            //--- Validate values --------------------//
            ArgsValidator.ValidateUserName(groupName);
            //----------------------------------------//

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GroupExists")){
				sqlCmd.AddParameter("@groupName",SqlDbType.NVarChar,groupName);

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

				if(ds.Tables["Result"].Rows.Count > 0){
					return true;
				}
			}

			return false;
        }

        #endregion

        #region method GetGroups

        /// <summary>
        /// Gets user groups.
        /// </summary>
        /// <returns></returns>
        public DataView GetGroups()
        {
            /* Implementation notes:
                *) Get groups.
            */

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetGroups")){
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Groups";

				return ds.Tables["Groups"].DefaultView;
			}
        }

        #endregion

        #region method AddGroup

        /// <summary>
        /// Adds new user group.
        /// </summary>
        /// <param name="groupID">Group ID. Guid.NewGuid().ToString() is suggested.</param>
        /// <param name="groupName">Group name.</param>
        /// <param name="description">Group description.</param>
        /// <param name="enabled">Specifies if group is enabled.</param>
        public void AddGroup(string groupID,string groupName,string description,bool enabled)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that group ID won't exist already. Throw Exception if does.
                *) Ensure that group or user with specified name doesn't exist. Throw Exception if does.
                *) Add group.
            */

            //--- Validate values --------------------//
            if(groupID == null || groupID == ""){
                throw new Exception("Invalid groupID value, groupID can't be '' or null !");
            }
            ArgsValidator.ValidateUserName(groupName);
            ArgsValidator.ValidateNotNull(description);
            //----------------------------------------//

            /* We handle these is SQL, sql returns these errors in ErrorText
             
                *) Ensure that group ID won't exist already. Throw Exception if does.
                *) Ensure that group or user with specified name doesn't exist. Throw Exception if does.
             
            */

            // Insert group
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddGroup")){
				sqlCmd.AddParameter("@groupID"     ,SqlDbType.NVarChar,groupID);
				sqlCmd.AddParameter("@groupName"   ,SqlDbType.NVarChar,groupName);
				sqlCmd.AddParameter("@description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@enabled"     ,SqlDbType.Bit     ,enabled);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Groups";

                // Proccess adding errors
				if(ds.Tables["Groups"].Rows.Count > 0 && ds.Tables["Groups"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Groups"].Rows[0]["ErrorText"].ToString());
				}
			}
        }
       
        #endregion

        #region method DeleteGroup

        /// <summary>
        /// Deletes specified user group.
        /// </summary>
        /// <param name="groupID">Group ID.</param>
        public void DeleteGroup(string groupID)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that group does exist.  Throw Exception if doesn't.
                *) Delete group members.
                *) Delete group.
            */

            //--- Validate values --------------------//
            if(groupID == null || groupID == ""){
                throw new Exception("Invalid groupID value, groupID can't be '' or null !");
            }
            //----------------------------------------//

            /* We handle these is SQL, sql returns these errors in ErrorText
             
                *) Ensure that group does exist.
                *) Delete group members.
             
            */

            // Delete group and it's emebres
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteGroup")){
				sqlCmd.AddParameter("@groupID" ,SqlDbType.NVarChar,groupID);
							
				DataSet ds = sqlCmd.Execute();
                ds.Tables[0].TableName = "Groups";

                // Proccess errors
				if(ds.Tables["Groups"].Rows.Count > 0 && ds.Tables["Groups"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Groups"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion

        #region method UpdateGroup

        /// <summary>
        /// Updates user group info.
        /// </summary>
        /// <param name="groupID">Group ID.</param>
        /// <param name="groupName">Group name.</param>
        /// <param name="description">Group description.</param>
        /// <param name="enabled">Specifies if group is enabled.</param>
        public void UpdateGroup(string groupID,string groupName,string description,bool enabled)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that group with specified ID does exist.  Throw Exception if doesn't.
                *) If group name is changed, ensure that new group name won't conflict 
                   any other group or user name. Throw Exception if does.                    
                *) Udpate group.
            */

            //--- Validate values --------------------//
            if(groupID == null || groupID == ""){
                throw new Exception("Invalid groupID value, groupID can't be '' or null !");
            }
            ArgsValidator.ValidateUserName(groupName);
            ArgsValidator.ValidateNotNull(description);
            //----------------------------------------//

            /* We handle these is SQL, sql returns these errors in ErrorText
             
            *) Ensure that group with specified ID does exist.  Throw Exception if doesn't.
            *) If group name is changed, ensure that new group name won't conflict 
               any other group or user name. Throw Exception if does.
            */

            // Update group
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateGroup")){
				sqlCmd.AddParameter("@groupID"     ,SqlDbType.NVarChar,groupID);
				sqlCmd.AddParameter("@groupName"   ,SqlDbType.NVarChar,groupName);
				sqlCmd.AddParameter("@description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@enabled"     ,SqlDbType.Bit     ,enabled);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Groups";

                // Proccess adding errors
				if(ds.Tables["Groups"].Rows.Count > 0 && ds.Tables["Groups"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Groups"].Rows[0]["ErrorText"].ToString());
				}
			}
        }
        
        #endregion

        #region method GroupMemberExists

        /// <summary>
        /// Gets if specified group member exists in specified user group members list.
        /// </summary>
        /// <param name="groupName">Group name</param>
        /// <param name="userOrGroup">User or group.</param>
        /// <returns></returns>
        public bool GroupMemberExists(string groupName,string userOrGroup)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that group exists. Throw Exception if doesn't.
                *) Check if group member exists.
            */

            //--- Validate values --------------------//
            ArgsValidator.ValidateUserName(groupName);
            ArgsValidator.ValidateUserName(userOrGroup);
            //----------------------------------------//

            // Ensure that group exists. Throw Exception if doesn't.
            if(!GroupExists(groupName)){
                throw new Exception("Invalid group name, specified group '" + groupName + "' doesn't exist !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GroupMemberExists")){
				sqlCmd.AddParameter("@groupName"  ,SqlDbType.NVarChar,groupName);
                sqlCmd.AddParameter("@userOrGroup",SqlDbType.NVarChar,userOrGroup);

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

				if(ds.Tables["Result"].Rows.Count > 0){
					return true;
				}
			}

            return false;
        }

        #endregion

        #region method GetGroupMembers

        /// <summary>
        /// Gets useer group members who belong to specified group.
        /// </summary>
        /// <param name="groupName">Group name.</param>
        /// <returns></returns>
        public string[] GetGroupMembers(string groupName)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that group exists. Throw Exception if doesn't.
                *) Get members.
            */

            //--- Validate values --------------------//
            if(groupName == null || groupName == ""){
                throw new Exception("Invalid groupName value, groupName can't be '' or null !");
            }
            //----------------------------------------//

            // Ensure that group exists.
            if(!GroupExists(groupName)){
                throw new Exception("Invalid group name, specified group name '" + groupName + "' doesn't exist !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetGroupMembers")){
                sqlCmd.AddParameter("@groupName" ,SqlDbType.NVarChar,groupName);

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

                List<string> members = new List<string>();
                foreach(DataRow dr in ds.Tables["Groups"].Rows){
                    members.Add(dr["UserOrGroup"].ToString());
                }
				return members.ToArray();
			}
        }

        #endregion

        #region method AddGroupMember

        /// <summary>
        /// Add specified user or group to specified goup members list.
        /// </summary>
        /// <param name="groupName">Group name.</param>
        /// <param name="userOrGroup">User or group.</param>
        public void AddGroupMember(string groupName,string userOrGroup)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that group exists. Throw Exception if doesn't.
                *) Don't allow to add same group as group member.
                *) Ensure that group member doesn't exist. Throw Exception if does.
                *) Add group member.
            */

            //--- Validate values --------------------//
            ArgsValidator.ValidateUserName(groupName);
            ArgsValidator.ValidateUserName(userOrGroup);
            //----------------------------------------//

            /* We handle these is SQL, sql returns these errors in ErrorText
            
                *) Ensure that group exists. Throw Exception if doesn't.
                *) Don't allow to add same group as group member.
                *) Ensure that group member doesn't exist. Throw Exception if does.
            */

            // Add group member
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddGroupMember")){
				sqlCmd.AddParameter("@groupName"   ,SqlDbType.NVarChar,groupName);
				sqlCmd.AddParameter("@userOrGroup" ,SqlDbType.NVarChar,userOrGroup);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Result";

                // Proccess errors
				if(ds.Tables["Result"].Rows.Count > 0 && ds.Tables["Result"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Result"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion

        #region method DeleteGroupMember

        /// <summary>
        /// Deletes specified user or group from specified group members list.
        /// </summary>
        /// <param name="groupName">Group name.</param>
        /// <param name="userOrGroup">User or group.</param>
        public void DeleteGroupMember(string groupName,string userOrGroup)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that group exists. Throw Exception if doesn't.
                *) Ensure that group member does exist. Throw Exception if doesn't.
                *) Delete group member.
            */

            //--- Validate values --------------------//
            ArgsValidator.ValidateUserName(groupName);
            ArgsValidator.ValidateUserName(userOrGroup);
            //----------------------------------------//

            /* We handle these is SQL, sql returns these errors in ErrorText
            
                *) Ensure that group exists. Throw Exception if doesn't.
                *) Ensure that group member does exist. Throw Exception if doesn't.
              
            */            

            // Delete group and it's emebres
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteGroupMember")){
				sqlCmd.AddParameter("@groupName"   ,SqlDbType.NVarChar,groupName);
                sqlCmd.AddParameter("@userOrGroup" ,SqlDbType.NVarChar,userOrGroup);
							
				DataSet ds = sqlCmd.Execute();
                ds.Tables[0].TableName = "Result";

                // Proccess errors
				if(ds.Tables["Result"].Rows.Count > 0 && ds.Tables["Result"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Result"].Rows[0]["ErrorText"].ToString());
				}
			}                       
        }

        #endregion

        #region method GetGroupUsers

        /// <summary>
        /// Gets specified group users. All nested group members are replaced by actual users.
        /// </summary>
        /// <param name="groupName">Group name.</param>
        /// <returns></returns>
        public string[] GetGroupUsers(string groupName)
        {
            List<string> users = new List<string>();
            List<string> proccessedGroups = new List<string>();
            Queue<string> membersQueue = new Queue<string>();
            string[] members = GetGroupMembers(groupName);
            foreach(string member in members){
                membersQueue.Enqueue(member);
            }

            while(membersQueue.Count > 0){
                string member = membersQueue.Dequeue();
                // Nested group
                DataRow drGroup = GetGroup(member);
                if(drGroup != null){
                    // Don't proccess poroccessed groups any more, causes infinite loop
                    if(!proccessedGroups.Contains(member.ToLower())){
                        // Skip disabled groups
                        if(Convert.ToBoolean(drGroup["Enabled"])){
                            members = GetGroupMembers(member);
                            foreach(string m in members){
                                membersQueue.Enqueue(m);
                            }  
                        }

                        proccessedGroups.Add(member.ToLower());
                    }
                }
                // User
                else{
                    if(!users.Contains(member)){
                        users.Add(member);
                    }
                }
            }

            return users.ToArray();
        }

        #endregion

		#endregion


		#region MailingList related
		
		#region function GetMailingLists

		/// <summary>
		/// Gets mailing lists.
		/// </summary>
		/// <param name="domainName">Domain name. Use <see cref="IMailServerApi.GetDomains">GetDomains()</see> to get valid values.</param>
		/// <returns></returns>
		public DataView GetMailingLists(string domainName)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetMailingLists")){
				if(domainName != "ALL"){
					sqlCmd.AddParameter("@DomainName",SqlDbType.NVarChar,domainName);
				}

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

				return ds.Tables["MailingLists"].DefaultView;
			}
		}
		
		#endregion
		

		#region function AddMailingList

		/// <summary>
		/// Adds new mailing list.
		/// </summary>
		/// <param name="mailingListID">Mailing list ID. Suggested value is Guid.NewGuid() .</param>
		/// <param name="mailingListName">Mailing list name name. Eg. all@lumisoft.ee .</param>
		/// <param name="description">Mailing list description.</param>
		/// <param name="domainName">Domain name. Use <see cref="IMailServerApi.GetDomains">GetDomains()</see> to get valid values.</param>
		/// <param name="enabled">Specifies if mailing list is enabled.</param>
		/// <remarks>Throws exception if specified mailing list already exists.</remarks>
		public void AddMailingList(string mailingListID,string mailingListName,string description,string domainName,bool enabled)
		{
			if(mailingListID.Length == 0){
				throw new Exception("You must specify mailingListID");
			}
			if(mailingListName.Length == 0){
				throw new Exception("You must specify mailingListName");
			}

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddMailingList")){
				sqlCmd.AddParameter("@MailingListID"   ,SqlDbType.NVarChar,mailingListID);
				sqlCmd.AddParameter("@MailingListName" ,SqlDbType.NVarChar,mailingListName);
				sqlCmd.AddParameter("@Description"     ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@DomainName"      ,SqlDbType.NVarChar,domainName);
				sqlCmd.AddParameter("@enabled"         ,SqlDbType.Bit     ,enabled);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "MailingLists";

				if(ds.Tables["MailingLists"].Rows.Count > 0 && ds.Tables["MailingLists"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["MailingLists"].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion

		#region function DeleteMailingList

		/// <summary>
		/// Deletes specified mailing list.
		/// </summary>
		/// <param name="mailingListID"> Use <see cref="IMailServerApi.GetMailingLists">GetMailingLists()</see> to get valid values.</param>
		/// <returns></returns>
		public void DeleteMailingList(string mailingListID)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteMailingList")){
				sqlCmd.AddParameter("@MailingListID" ,SqlDbType.NVarChar,mailingListID);
							
				DataSet ds = sqlCmd.Execute();
			}
		}
		
		#endregion

		#region function UpdateMailingList

		/// <summary>
		/// Updates specified mailing list.
		/// </summary>
		/// <param name="mailingListID">Mailing list ID.</param>
		/// <param name="mailingListName">Mailing list name name. Use <see cref="IMailServerApi.GetMailingLists">GetMailingLists()</see> to get valid values.</param>
		/// <param name="description">Mailing list description.</param>
		/// <param name="domainName">Domain name. Use <see cref="IMailServerApi.GetDomains">>GetUsers()</see> to get valid values.</param>
		/// <param name="enabled">Specifies if mailing list is enabled.</param>
		public void UpdateMailingList(string mailingListID,string mailingListName,string description,string domainName,bool enabled)
		{
			if(mailingListName.Length == 0){
				throw new Exception("You must specify mailingListName");
			}

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateMailingList")){
				sqlCmd.AddParameter("@MailingListID"   ,SqlDbType.NVarChar,mailingListID);
				sqlCmd.AddParameter("@MailingListName" ,SqlDbType.NVarChar,mailingListName);
				sqlCmd.AddParameter("@Description"     ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@DomainName"      ,SqlDbType.NVarChar,domainName);
				sqlCmd.AddParameter("@enabled"         ,SqlDbType.Bit     ,enabled);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "MailingLists";

				if(ds.Tables["MailingLists"].Rows.Count > 0 && ds.Tables["MailingLists"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["MailingLists"].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion

		#region function AddMailingListAddress

		/// <summary>
		/// Add new email address to specified mailing list.
		/// </summary>
		/// <param name="addressID">Address ID. Suggested value is Guid.NewGuid() .</param>
		/// <param name="mailingListName">Mailing list name name. Use <see cref="IMailServerApi.GetMailingLists">GetMailingLists()</see> to get valid values.</param>
		/// <param name="address">Mailing list member address.</param>
		/// <remarks>Throws exception if specified mailing list member already exists.</remarks>
		public void AddMailingListAddress(string addressID,string mailingListName,string address)
		{
			if(addressID.Length == 0){
				throw new Exception("You must specify addressID");
			}
			if(mailingListName.Length == 0){
				throw new Exception("You must specify mailingListName");
			}
			if(address.Length == 0){
				throw new Exception("You must specify address");
			}

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddMailingListAddress")){
				sqlCmd.AddParameter("@AddressID"       ,SqlDbType.NVarChar,addressID);
				sqlCmd.AddParameter("@MailingListName" ,SqlDbType.NVarChar,mailingListName);
				sqlCmd.AddParameter("@Address"         ,SqlDbType.NVarChar,address);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "MailingListAddresses";

				if(ds.Tables["MailingListAddresses"].Rows.Count > 0 && ds.Tables["MailingListAddresses"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["MailingListAddresses"].Rows[0]["ErrorText"].ToString());
				}
			}
		}

		#endregion

		#region function DeleteMailingListAddress

		/// <summary>
		/// Deletes specified email address from mailing list. 
		/// </summary>
		/// <param name="addressID">Mailing list member address ID. Use <see cref="IMailServerApi.GetMailingListAddresses">GetMailingListMembers()</see> to get valid values.</param>
		public void DeleteMailingListAddress(string addressID)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteMailingListAddress")){
				sqlCmd.AddParameter("@AddressID" ,SqlDbType.UniqueIdentifier,addressID);
							
				DataSet ds = sqlCmd.Execute();
			}
		}

		#endregion

		#region function GetMailingListAddresses

		/// <summary>
		/// Gets mailing list members.
		/// </summary>
		/// <param name="mailingListName">Mailing list name name. Use <see cref="IMailServerApi.GetMailingLists">GetMailingLists()</see> to get valid values.</param>
		public DataView GetMailingListAddresses(string mailingListName)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetMailingListAddresses")){
				if(mailingListName.Length > 0){
					sqlCmd.AddParameter("@MailingListName",SqlDbType.NVarChar,mailingListName);
				}

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

				return ds.Tables["MailingListAddresses"].DefaultView;
			}
		}

		#endregion


        #region method GetMailingListACL

        /// <summary>
        /// Gets mailing list ACL list.
        /// </summary>
        /// <param name="mailingListName">Mailing list name.</param>
        public DataView GetMailingListACL(string mailingListName)
        {
            /* Implementation notes:
                *) Get mailing list ACL list.
            */

            // Get mailing list ACL list
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetMailingListACL")){
                sqlCmd.AddParameter("@MailingListName" ,SqlDbType.NVarChar,mailingListName);

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

				return ds.Tables["ACL"].DefaultView;
			}
        }

        #endregion

        #region method AddMailingListACL

        /// <summary>
        /// Adds specified user or group to mailing list ACL list (specified user can send messages to the specified mailing list).
        /// </summary>
        /// <param name="mailingListName">Mailing list name.</param>
        /// <param name="userOrGroup">User or group name.</param>
        public void AddMailingListACL(string mailingListName,string userOrGroup)
        {
            /* Implementation notes:
                *) Ensure that mailing list exists.
                *) Ensure that user or group already doesn't exist in list.
                *) Add ACL entry.
            */

            /* We handle these is SQL, sql returns these errors in ErrorText
             
                *) Ensure that mailing list exists.
                *) Ensure that user or group already doesn't exist in list.
             
            */

            // Add ACL entry
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddMailingListACL")){
				sqlCmd.AddParameter("@mailingListName" ,SqlDbType.NVarChar,mailingListName);
                sqlCmd.AddParameter("@userOrGroup"     ,SqlDbType.NVarChar,userOrGroup);
							
				DataSet ds = sqlCmd.Execute();
                ds.Tables[0].TableName = "Result";

                // Proccess errors
				if(ds.Tables["Result"].Rows.Count > 0 && ds.Tables["Result"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Result"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion

        #region method DeleteMailingListACL

        /// <summary>
        /// Deletes specified user or group from mailing list ACL list.
        /// </summary>
        /// <param name="mailingListName">Mailing list name.</param>
        /// <param name="userOrGroup">User or group name.</param>
        public void DeleteMailingListACL(string mailingListName,string userOrGroup)
        {
            /* Implementation notes:
                *) Ensure that mailing list exists.
                *) Delete ACL entry.
            */

            /* We handle these is SQL, sql returns these errors in ErrorText
             
                *) Ensure that mailing list exists.
             
            */

            // Delete group and it's emebres
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteMailingListACL")){
				sqlCmd.AddParameter("@mailingListName" ,SqlDbType.NVarChar,mailingListName);
                sqlCmd.AddParameter("@userOrGroup"     ,SqlDbType.NVarChar,userOrGroup);
							
				DataSet ds = sqlCmd.Execute();
                ds.Tables[0].TableName = "Result";

                // Proccess errors
				if(ds.Tables["Result"].Rows.Count > 0 && ds.Tables["Result"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Result"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion

        #region method CanAccessMailingList

        /// <summary>
        /// Checks if specified user can access specified mailing list.
        /// There is one built-in user anyone, that represent all users (including anonymous).
        /// </summary>
        /// <param name="mailingListName">Mailing list name.</param>
        /// <param name="user">User name.</param>
        /// <returns></returns>
        public bool CanAccessMailingList(string mailingListName,string user)
        {
            /* Implementation notes:
                *) Ensure that mailing list exists.
                *) Check access.
            */

            // Ensure that mailing list exists
            // Get mailing list ID
            string mailingListID = null;
            foreach(DataRowView drV in GetMailingLists("ALL")){
                if(drV["MailingListName"].ToString().ToLower() == mailingListName.ToLower()){
                    mailingListID = drV["MailingListID"].ToString();
                    break;
                }
            }                
            if(mailingListID == null){
                throw new Exception("Invalid mailing list name, specified mailing list '" + mailingListName + "' doesn't exist !");
            }

            // Check access
            WSqlCommand cmd = new WSqlCommand(m_ConStr,"select * from lsMailingListACL");
            cmd.CommandType = CommandType.Text;
            DataSet dsMailingListACL = cmd.Execute();
            dsMailingListACL.Tables[0].TableName = "ACL";

            foreach(DataRow dr in dsMailingListACL.Tables["ACL"].Rows){
                if(dr["MailingListID"].ToString() == mailingListID){
                    // Built-in anyone
                    if(dr["UserOrGroup"].ToString().ToLower() == "anyone"){
                        return true;
                    }
                    // Built-in "authenticated users"
                    else if(dr["UserOrGroup"].ToString().ToLower() == "authenticated users"){
                        return UserExists(user);
                    }
                    // User or group
                    else{                            
                        if(GroupExists(dr["UserOrGroup"].ToString())){
                            return IsUserGroupMember(dr["UserOrGroup"].ToString(),user);
                        }                    
                        else{
                            return UserExists(user);
                        }
                    }
                }
            }

            return false;
        }

        #endregion


        #region function MailingListExists

        /// <summary>
		/// Checks if user exists.
		/// </summary>
		/// <param name="mailingListName">Mailing list name.</param>
		/// <returns>Returns true if mailing list exists.</returns>
		public bool MailingListExists(string mailingListName)
		{		
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetMailingListProperties")){
				sqlCmd.AddParameter("@MailingListName",SqlDbType.NVarChar,mailingListName);

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

				if(ds.Tables["MailingLists"].Rows.Count > 0){
					return true;
				}
			}

			return false;
		}
		
		#endregion

		#endregion


        #region Rules

        #region method GetGlobalMessageRules

        /// <summary>
        /// Gets global message rules.
        /// </summary>
        /// <returns></returns>
        public DataView GetGlobalMessageRules()
        {
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetGlobalMessageRules")){				
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "GlobalMessageRules";

                // We need to convert MatchExpression to string. 
                // MSSQL won't allow strings bigger than 400 chars, because of it we store it as Image.
                ds.Tables[0].Columns["MatchExpression"].ColumnName = "MatchExpressionOld";
                ds.Tables[0].Columns.Add("MatchExpression",typeof(string));
                foreach(DataRow dr in ds.Tables[0].Rows){
                    dr["MatchExpression"] = System.Text.Encoding.Default.GetString((byte[])dr["MatchExpressionOld"]);
                }
                ds.Tables[0].Columns.Remove("MatchExpressionOld");
                

				return ds.Tables["GlobalMessageRules"].DefaultView;
			}
        }

        #endregion

        #region method AddGlobalMessageRule

        /// <summary>
        /// Adds new global message rule.
        /// </summary>
        /// <param name="ruleID">Rule ID. Guid.NewID().ToString() is suggested.</param>
        /// <param name="cost">Cost specifies in what order rules are processed. Costs with lower values are processed first.</param>
        /// <param name="enabled">Specifies if rule is enabled.</param>
        /// <param name="checkNextRule">Specifies when next rule is checked.</param>
        /// <param name="description">Rule description.</param>
        /// <param name="matchExpression">Rule match expression.</param>
        public void AddGlobalMessageRule(string ruleID,long cost,bool enabled,GlobalMessageRule_CheckNextRule_enum checkNextRule,string description,string matchExpression)
        {
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddGlobalMessageRule")){
				sqlCmd.AddParameter("@ruleID"          ,SqlDbType.NVarChar,ruleID);
				sqlCmd.AddParameter("@cost"            ,SqlDbType.BigInt  ,cost);
				sqlCmd.AddParameter("@enabled"         ,SqlDbType.Bit     ,enabled);
				sqlCmd.AddParameter("@checkNextRule"   ,SqlDbType.Int     ,checkNextRule);
				sqlCmd.AddParameter("@description"     ,SqlDbType.NVarChar,description);
                sqlCmd.AddParameter("@matchExpression" ,SqlDbType.Image   ,System.Text.Encoding.Default.GetBytes(matchExpression));
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "GlobalMessageRules";

				if(ds.Tables["GlobalMessageRules"].Rows.Count > 0 && ds.Tables["GlobalMessageRules"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["GlobalMessageRules"].Rows[0]["ErrorText"].ToString());
				}
			}
        }
        
        #endregion

        #region method DeleteGlobalMessageRule

        /// <summary>
        /// Deletes specified global message rule.
        /// </summary>
        /// <param name="ruleID">Rule ID of rule which to delete.</param>
        public void DeleteGlobalMessageRule(string ruleID)
        {
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteGlobalMessageRule")){
				sqlCmd.AddParameter("@ruleID" ,SqlDbType.NVarChar,ruleID);
							
				DataSet ds = sqlCmd.Execute();
			}
        }
       
        #endregion

        #region method UpdateGlobalMessageRule

        /// <summary>
        /// Updates specified global message rule.
        /// </summary>
        /// <param name="ruleID">Rule ID.</param>
        /// <param name="cost">Cost specifies in what order rules are processed. Costs with lower values are processed first.</param>
        /// <param name="enabled">Specifies if rule is enabled.</param>
        /// <param name="checkNextRule">Specifies when next rule is checked.</param>
        /// <param name="description">Rule description.</param>
        /// <param name="matchExpression">Rule match expression.</param>
        public void UpdateGlobalMessageRule(string ruleID,long cost,bool enabled,GlobalMessageRule_CheckNextRule_enum checkNextRule,string description,string matchExpression)
        {
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateGlobalMessageRule")){
				sqlCmd.AddParameter("@ruleID"          ,SqlDbType.NVarChar,ruleID);
				sqlCmd.AddParameter("@cost"            ,SqlDbType.BigInt  ,cost);
				sqlCmd.AddParameter("@enabled"         ,SqlDbType.Bit     ,enabled);
				sqlCmd.AddParameter("@checkNextRule"   ,SqlDbType.Int     ,checkNextRule);
				sqlCmd.AddParameter("@description"     ,SqlDbType.NVarChar,description);
                sqlCmd.AddParameter("@matchExpression" ,SqlDbType.Image   ,System.Text.Encoding.Default.GetBytes(matchExpression));
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "GlobalMessageRules";

				if(ds.Tables["GlobalMessageRules"].Rows.Count > 0 && ds.Tables["GlobalMessageRules"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["GlobalMessageRules"].Rows[0]["ErrorText"].ToString());
				}
			}
        }
        
        #endregion

        #region method GetGlobalMessageRuleActions

        /// <summary>
        /// Gets specified global message rule actions.
        /// </summary>
        /// <param name="ruleID">Rule ID of rule which actions to get.</param>
        public DataView GetGlobalMessageRuleActions(string ruleID)
        {
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetGlobalMessageRuleActions")){
			    sqlCmd.AddParameter("@ruleID",SqlDbType.NVarChar,ruleID);

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

				return ds.Tables["GlobalMessageRuleActions"].DefaultView;
			}
        }

        #endregion

        #region method AddGlobalMessageRuleAction

        /// <summary>
        /// Adds action to specified global message rule.
        /// </summary>
        /// <param name="ruleID">Rule ID to which to add this action.</param>
        /// <param name="actionID">Action ID. Guid.NewID().ToString() is suggested.</param>
        /// <param name="description">Action description.</param>
        /// <param name="actionType">Action type.</param>
        /// <param name="actionData">Action data. Data structure depends on action type.</param>
        public void AddGlobalMessageRuleAction(string ruleID,string actionID,string description,GlobalMessageRuleAction_enum actionType,byte[] actionData)
        {
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }
            if(actionID == null || actionID == ""){
                throw new Exception("Invalid actionID value, actionID can't be '' or null !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddGlobalMessageRuleAction")){
				sqlCmd.AddParameter("@ruleID"      ,SqlDbType.NVarChar,ruleID);
				sqlCmd.AddParameter("@actionID"    ,SqlDbType.NVarChar,actionID);
				sqlCmd.AddParameter("@description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@actionType"  ,SqlDbType.Int     ,(int)actionType);
                sqlCmd.AddParameter("@actionData"  ,SqlDbType.Image   ,actionData);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "GlobalMessageRuleActions";

				if(ds.Tables["GlobalMessageRuleActions"].Rows.Count > 0 && ds.Tables["GlobalMessageRuleActions"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["GlobalMessageRuleActions"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion

        #region method DeleteGlobalMessageRuleAction

        /// <summary>
        /// Deletes specified action from specified global message rule.
        /// </summary>
        /// <param name="ruleID">Rule ID which action to delete.</param>
        /// <param name="actionID">Action ID of action which to delete.</param>
        public void DeleteGlobalMessageRuleAction(string ruleID,string actionID)
        {
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }
            if(actionID == null || actionID == ""){
                throw new Exception("Invalid actionID value, actionID can't be '' or null !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteGlobalMessageRuleAction")){
				sqlCmd.AddParameter("@ruleID"   ,SqlDbType.NVarChar,ruleID);
                sqlCmd.AddParameter("@actionID" ,SqlDbType.NVarChar,actionID);
							
				DataSet ds = sqlCmd.Execute();
			}
        }

        #endregion

        #region method UpdateGlobalMessageRuleAction

        /// <summary>
        /// Updates specified rule action.
        /// </summary>
        /// <param name="ruleID">Rule ID which action to update.</param>
        /// <param name="actionID">Action ID of action which to update.</param>
        /// <param name="description">Action description.</param>
        /// <param name="actionType">Action type.</param>
        /// <param name="actionData">Action data. Data structure depends on action type.</param>
        public void UpdateGlobalMessageRuleAction(string ruleID,string actionID,string description,GlobalMessageRuleAction_enum actionType,byte[] actionData)
        {
            if(ruleID == null || ruleID == ""){
                throw new Exception("Invalid ruleID value, ruleID can't be '' or null !");
            }
            if(actionID == null || actionID == ""){
                throw new Exception("Invalid actionID value, actionID can't be '' or null !");
            }

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateGlobalMessageRuleAction")){
				sqlCmd.AddParameter("@ruleID"      ,SqlDbType.NVarChar,ruleID);
				sqlCmd.AddParameter("@actionID"    ,SqlDbType.NVarChar,actionID);
				sqlCmd.AddParameter("@description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@actionType"  ,SqlDbType.Int     ,(int)actionType);
                sqlCmd.AddParameter("@actionData"  ,SqlDbType.Image   ,actionData);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "GlobalMessageRuleActions";

				if(ds.Tables["GlobalMessageRuleActions"].Rows.Count > 0 && ds.Tables["GlobalMessageRuleActions"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["GlobalMessageRuleActions"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion

        #endregion


		#region Routing related

		#region function GetRoutes

		/// <summary>
		/// Gets email address routes.
		/// </summary>
		/// <returns></returns>
		public DataView GetRoutes()
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetRoutes")){
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Routing";

				return ds.Tables["Routing"].DefaultView;
			}
		}
		
		#endregion


		#region function AddRoute

		/// <summary>
		/// Adds new route.
		/// </summary>
		/// <param name="routeID">Route ID.</param>
        /// <param name="cost">Cost specifies in what order roues are processed. Costs with lower values are processed first.</param>
		/// <param name="enabled">Specifies if route is enabled.</param>
		/// <param name="description">Route description text.</param>
		/// <param name="pattern">Match pattern. For example: *,*@domain.com,*sales@domain.com.</param>
		/// <param name="action">Specifies route action.</param>
		/// <param name="actionData">Route action data.</param>
		public void AddRoute(string routeID,long cost,bool enabled,string description,string pattern,RouteAction_enum action,byte[] actionData)
		{
			if(routeID.Length == 0){
				throw new Exception("You must specify routeID");
			}
			if(pattern.Length == 0){
				throw new Exception("You must specify pattern");
			}
            
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddRoute")){
				sqlCmd.AddParameter("@routeID"     ,SqlDbType.NVarChar,routeID);
				sqlCmd.AddParameter("@cost"        ,SqlDbType.BigInt  ,cost);
				sqlCmd.AddParameter("@enabled"     ,SqlDbType.Bit     ,enabled);
				sqlCmd.AddParameter("@description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@pattern"     ,SqlDbType.NVarChar,pattern);
                sqlCmd.AddParameter("@action"      ,SqlDbType.Int     ,action);
                sqlCmd.AddParameter("@actionData"  ,SqlDbType.Image   ,actionData);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Routing";

				if(ds.Tables["Routing"].Rows.Count > 0 && ds.Tables["Routing"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Routing"].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion

		#region function DeleteRoute

		/// <summary>
		/// Deletes route.
		/// </summary>
		/// <param name="routeID">Route ID.</param>
		public void DeleteRoute(string routeID)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteRoute")){
				sqlCmd.AddParameter("@RouteID" ,SqlDbType.NVarChar,routeID);
							
				DataSet ds = sqlCmd.Execute();
			}
		}
		
		#endregion

		#region function UpdateRoute

		/// <summary>
		/// Updates route.
		/// </summary>
		/// <param name="routeID">Route ID.</param>
        /// <param name="cost">Cost specifies in what order roues are processed. Costs with lower values are processed first.</param>
		/// <param name="enabled">Specifies if route is enabled.</param>
		/// <param name="description">Route description text.</param>
		/// <param name="pattern">Match pattern. For example: *,*@domain.com,*sales@domain.com.</param>
		/// <param name="action">Specifies route action.</param>
		/// <param name="actionData">Route action data.</param>
		public void UpdateRoute(string routeID,long cost,bool enabled,string description,string pattern,RouteAction_enum action,byte[] actionData)
		{
			if(pattern.Length == 0){
				throw new Exception("You must specify pattern");
			}
            
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateRoute")){
				sqlCmd.AddParameter("@routeID"     ,SqlDbType.NVarChar,routeID);
				sqlCmd.AddParameter("@cost"        ,SqlDbType.BigInt  ,cost);
				sqlCmd.AddParameter("@enabled"     ,SqlDbType.Bit     ,enabled);
				sqlCmd.AddParameter("@description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@pattern"     ,SqlDbType.NVarChar,pattern);
                sqlCmd.AddParameter("@action"      ,SqlDbType.Int     ,action);
                sqlCmd.AddParameter("@actionData"  ,SqlDbType.Image   ,actionData);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Routing";

				if(ds.Tables["Routing"].Rows.Count > 0 && ds.Tables["Routing"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Routing"].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion

		#endregion


		#region MailStore related

		#region function GetMessagesInfo

		/// <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_GetMessageInfo")){
				sqlCmd.AddParameter("@Mailbox",SqlDbType.NVarChar,folderOwnerUser);
				sqlCmd.AddParameter("@Folder" ,SqlDbType.NVarChar,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
                    );
				}
			}
		}
		
		#endregion
                

		#region function StoreMessage

		/// <summary>
		/// Stores message to specified 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 where to store message. For example: Inbox,Public Folders/Documnets .</param>
		/// <param name="msgStream">Stream where message has stored. Stream position must be at the beginning of the message.</param>
		/// <param name="date">Recieve date.</param>
		/// <param name="flags">Message flags.</param>
		public void StoreMessage(string accessingUser,string folderOwnerUser,string folder,Stream msgStream,DateTime date,IMAP_MessageFlags flags)
		{
            /* 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 'p' or 'i' permission.
                    There is builtin user system, skip ACL for it.
                *) Store message.
            */

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

            // 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 'p' or 'i' 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.p) == 0 && (acl & IMAP_ACL_Flags.i) == 0){
                    throw new InsufficientPermissionsException("Insufficient permissions for folder '" + accessingUser + "/" + folder + "' !");
                }
            }

            //--- Store message
			byte[] topLines = GetTopLines(msgStream,50);	
            msgStream.Position = 0;
	        byte[] messageData = new byte[msgStream.Length];
            msgStream.Read(messageData,0,messageData.Length);
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_StoreMessage")){
				sqlCmd.AddParameter("@MailBox"      ,SqlDbType.NVarChar ,folderOwnerUser);
				sqlCmd.AddParameter("@Folder"       ,SqlDbType.NVarChar ,folder);
				sqlCmd.AddParameter("@Data"         ,SqlDbType.Image    ,messageData);
				sqlCmd.AddParameter("@Size"         ,SqlDbType.BigInt   ,msgStream.Length);
				sqlCmd.AddParameter("@TopLines"     ,SqlDbType.Image    ,topLines);
				sqlCmd.AddParameter("@Date"         ,SqlDbType.DateTime ,date);
				sqlCmd.AddParameter("@MessageFlags" ,SqlDbType.Int      ,(int)flags);

				DataSet ds = sqlCmd.Execute();

				if(ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0){
					throw new Exception(ds.Tables[0].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion

		#region function StoreMessageFlags

		/// <summary>
		/// Stores IMAP message flags (\seen,\draft, ...).
		/// </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 which message flags to store. For example: Inbox,Public Folders/Documnets .</param>
		/// <param name="message">Fix ME: ???</param>
		/// <param name="msgFlags">Message flags to store.</param>
		public void StoreMessageFlags(string accessingUser,string folderOwnerUser,string folder,LumiSoft.Net.IMAP.Server.IMAP_Message message,IMAP_MessageFlags msgFlags)
		{
           /* 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.
                *) Remove all message flags which permissions user doesn't have.
                *) Store message.
            */

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

            // 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 !");
            }

            // Remove all message flags which permissions user doesn't have.
            if(accessingUser != "system"){
			    IMAP_ACL_Flags userACL = GetUserACL(folderOwnerUser,folder,accessingUser);					
			    if((userACL & IMAP_ACL_Flags.s) == 0){
    				msgFlags &= ~IMAP_MessageFlags.Seen;
			    }
			    else if((userACL & IMAP_ACL_Flags.d) == 0){
				    msgFlags &= ~IMAP_MessageFlags.Deleted;
			    }				
			    else if((userACL & IMAP_ACL_Flags.s) == 0){
				    msgFlags &= (~IMAP_MessageFlags.Answered | ~IMAP_MessageFlags.Draft | ~IMAP_MessageFlags.Flagged);
			    }
            }

            //--- Store message flags
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_StoreMessageFlags")){
				sqlCmd.AddParameter("@MessageID"    ,SqlDbType.NVarChar,message.ID);
				sqlCmd.AddParameter("@Mailbox"      ,SqlDbType.NVarChar,folderOwnerUser);
				sqlCmd.AddParameter("@Folder"       ,SqlDbType.NVarChar,folder);
				sqlCmd.AddParameter("@MessageFalgs" ,SqlDbType.Int     ,(int)message.Flags);

				DataSet ds = sqlCmd.Execute();
			}
		}
		
		#endregion

		#region method DeleteMessage

		/// <summary>
		/// Deletes message from mailbox.
		/// </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 message to delete. For example: Inbox,Public Folders/Documnets .</param>
		/// <param name="messageID">Message ID.</param>
		/// <param name="uid">Message UID value.</param>
		public void DeleteMessage(string accessingUser,string folderOwnerUser,string folder,string messageID,int uid)
		{
            /* 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 'd' permission.
                    There is builtin user system, skip ACL for it.
                *) Fill messages info.
            */

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

            // 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 'd' 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.d) == 0){
                    throw new InsufficientPermissionsException("Insufficient permissions for folder '" + accessingUser + "/" + folder + "' !");
                }
            }

            //--- Recycle bin handling ----------------------------------------------------------------------//
            if(Convert.ToBoolean(GetRecycleBinSettings().Rows[0]["DeleteToRecycleBin"])){
                EmailMessageItems msgItems = new EmailMessageItems(messageID,IMAP_MessageItems_enum.Message | IMAP_MessageItems_enum.Envelope);
                GetMessageItems("system",folderOwnerUser,folder,msgItems);
                if(msgItems.MessageExists){
                    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" ,SqlDbType.NVarChar,Guid.NewGuid().ToString());
                        sqlCmd.AddParameter("@user"      ,SqlDbType.NVarChar,folderOwnerUser);
                        sqlCmd.AddParameter("@folder"    ,SqlDbType.NVarChar,folder);
                        sqlCmd.AddParameter("@size"      ,SqlDbType.BigInt  ,data.Length);
                        sqlCmd.AddParameter("@envelope"  ,SqlDbType.NVarChar,msgItems.Envelope);
                        sqlCmd.AddParameter("@data"      ,SqlDbType.Image   ,data);				        

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

            //--- Delete message
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteMessage")){
				sqlCmd.AddParameter("@MessageID" ,SqlDbType.NVarChar,messageID);
				sqlCmd.AddParameter("@Mailbox"   ,SqlDbType.NVarChar,folderOwnerUser);
				sqlCmd.AddParameter("@Folder"    ,SqlDbType.NVarChar,folder);

				DataSet ds = sqlCmd.Execute();
			}
		}
		
		#endregion

        #region method GetMessageItems

        /// <summary>
        /// Gets specified message specified items.
        /// </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 message to delete. For example: Inbox,Public Folders/Documnets .</param>
        /// <param name="e">MessageItems info.</param>
        public void GetMessageItems(string accessingUser,string folderOwnerUser,string folder,EmailMessageItems e)
        {
            /* 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.
                *) Store message.
            */

            //--- Validate values -------------------//
            ArgsValidator.ValidateUserName(folderOwnerUser);
            ArgsValidator.ValidateFolder(folder);
            ArgsValidator.ValidateNotNull(e);
            //---------------------------------------//
            
            // 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
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetMessage")){
				sqlCmd.AddParameter("@MessageID" ,SqlDbType.NVarChar,e.MessageID);
				sqlCmd.AddParameter("@Mailbox"   ,SqlDbType.NVarChar,folderOwnerUser);
				sqlCmd.AddParameter("@Folder"    ,SqlDbType.NVarChar,folder);

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

                byte[] data = (byte[])ds.Tables["lsMailStore"].Rows[0]["Data"];

                Mime m = null;
                if((e.MessageItems & IMAP_MessageItems_enum.BodyStructure) != 0 || (e.MessageItems & IMAP_MessageItems_enum.Envelope) != 0){
                    try{
                        m = Mime.Parse(data);
                    }
                    catch(Exception x){
                        m = LumiSoft.Net.Mime.Mime.CreateSimple(new AddressList(),new AddressList(),"[BAD MESSAGE] Bad message, message parsing failed !","NOTE: Bad message, message parsing failed !\r\n\r\n" + x.Message,"");
                    }
                }

                if((e.MessageItems & IMAP_MessageItems_enum.BodyStructure) != 0){
                    e.BodyStructure = IMAP_BODY.ConstructBodyStructure(m,false).Substring(5);
                }
                if((e.MessageItems & IMAP_MessageItems_enum.Envelope) != 0){
                    e.Envelope = IMAP_Envelope.ConstructEnvelope(m.MainEntity);
                }
                if((e.MessageItems & IMAP_MessageItems_enum.Header) != 0){
                    e.Header = GetTopLines(new MemoryStream(data),0);
                }
                if((e.MessageItems & IMAP_MessageItems_enum.Message) != 0){                
                    e.MessageStream = new MemoryStream(data);
                }
			}
        }

        #endregion

		#region function GetMessageTopLines

		/// <summary>
		/// Gets message header + number of specified lines.
		/// </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 message top lines to get. For example: Inbox,Public Folders/Documnets .</param>
		/// <param name="msgID">MessageID.</param>
		/// <param name="nrLines">Number of lines to retrieve. NOTE: line counting starts at the end of header.</param>
		/// <returns>Returns message header + number of specified lines.</returns>
		public byte[] GetMessageTopLines(string accessingUser,string folderOwnerUser,string folder,string msgID,int nrLines)
		{
            /* 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.
                *) Get message top lines.
            */

            //--- Validate values -------------------//
            ArgsValidator.ValidateUserName(folderOwnerUser);
            ArgsValidator.ValidateFolder(folder);
            ArgsValidator.ValidateNotNull(msgID);
            //---------------------------------------//
            
            // 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 top lines
			if(nrLines < 50){
				using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetMessageTopLines")){
					sqlCmd.AddParameter("@MessageID" ,SqlDbType.NVarChar,msgID);
					sqlCmd.AddParameter("@Mailbox"   ,SqlDbType.NVarChar,folderOwnerUser);
					sqlCmd.AddParameter("@Folder"    ,SqlDbType.NVarChar,folder);

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

					return GetTopLines(new MemoryStream((byte[])ds.Tables["lsMailStore"].Rows[0]["TopLines"]),nrLines);
				}
			}
			else{
				EmailMessageItems msgItems = new EmailMessageItems(msgID,IMAP_MessageItems_enum.Message);            
                GetMessageItems(accessingUser,folderOwnerUser,folder,msgItems);
                byte[] topLines = GetTopLines(msgItems.MessageStream,nrLines);
                msgItems.MessageStream.Dispose();
				return topLines;
			}
		}
		
		#endregion

		#region function CopyMessage

		/// <summary>
		/// Creates copy of message to destination 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 contains message to copy. For example: Inbox,Public Folders/Documnets .</param>
		/// <param name="destFolderUser">Destination IMAP folder owner user name.</param>
		/// <param name="destFolder">Destination IMAP folder name.</param>
		/// <param name="message">IMAP message which to copy.</param>
		public void CopyMessage(string accessingUser,string folderOwnerUser,string folder,string destFolderUser,string destFolder,LumiSoft.Net.IMAP.Server.IMAP_Message message)
		{
           /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) We don't need to map shared folder, check security, it done by GetMessage and StoreMessage methods.
                *) Copy message.               
            */

            //--- Validate values -------------------//
            ArgsValidator.ValidateUserName(folderOwnerUser);
            ArgsValidator.ValidateFolder(folder);
            ArgsValidator.ValidateUserName(destFolderUser);
            ArgsValidator.ValidateFolder(destFolder);
            ArgsValidator.ValidateNotNull(message);
            //---------------------------------------//
            
            //--- Copy message
            EmailMessageItems msgItems = new EmailMessageItems(message.ID,IMAP_MessageItems_enum.Message);            
            GetMessageItems(accessingUser,folderOwnerUser,folder,msgItems);
            StoreMessage("system",destFolderUser,destFolder,msgItems.MessageStream,message.InternalDate,message.Flags);
            msgItems.MessageStream.Dispose();
		}
		
		#endregion


		#region function GetFolders
        		
        /// <summary>
		/// Gets all available IMAP folders.
		/// </summary>
		/// <param name="userName">User name who's folders to get.</param>
        /// <param name="includeSharedFolders">If true, shared folders are included.</param>
		public string[] GetFolders(string userName,bool includeSharedFolders)
		{
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that user exists. Throw Exception if don't.
                *) See if user has sufficient permissions. User requires 'l' permission.                             
                *) Append all visible(Forlders on what user has 'r' right) user mailbox folders.
                *) Append all visible(Forlders on what user has 'r' right) public folders to folders list !
            */

            //--- Validate values -------------------//
            ArgsValidator.ValidateUserName(userName);
            //---------------------------------------//

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

            DataSet dsFolders = null;
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetFolders")){
				sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);

				dsFolders = sqlCmd.Execute();
				dsFolders.Tables[0].TableName = "Folders";
			}

            // Append all visible(Forlders on what user has 'r' right) user mailbox folders.
            List<string> userFolders = new List<string>();
			for(int i=0;i<dsFolders.Tables["Folders"].Rows.Count;i++){
                string folder = dsFolders.Tables["Folders"].Rows[i]["FolderName"].ToString();
                // Show folders what user has 'r' right
                if((GetUserACL(userName,folder,userName) & IMAP_ACL_Flags.r) != 0){
                    userFolders.Add(folder);
                }
			}
            
            // Append all visible(Forlders on what user has 'r' right) shared folders to folders list
            if(includeSharedFolders){
                SharedFolderRoot[] sharedFolderRoots = GetSharedFolderRoots();
                foreach(SharedFolderRoot sharedFolderRoot in sharedFolderRoots){
                    // Skip disabled roots                    
                    if(!sharedFolderRoot.Enabled){
                        continue;
                    }

                    // Root is bounded folder
                    if(sharedFolderRoot.RootType == SharedFolderRootType_enum.BoundedRootFolder){
                        // Get root child folders
                        string[] boundedFolders = GetFolders(sharedFolderRoot.BoundedUser,false);
                        foreach(string boundedFolder in boundedFolders){
                            // We want folders what will be BoundedFolder child folders, others just skip
                            if(boundedFolder.ToLower().StartsWith(sharedFolderRoot.BoundedFolder.ToLower())){
                                // Show only folder what user has 'lr' right
                                if((GetUserACL(sharedFolderRoot.BoundedUser,boundedFolder,userName) & IMAP_ACL_Flags.r) != 0){
                                    // We must cut of boundedFolder part. For example root name = 'Public'and 
                                    // boundedFolder = 'inbox', then it must be visible as Public/inboxChildFolders,
                                    // not Public/inbox/inboxChildFolders.
                                    userFolders.Add(sharedFolderRoot.FolderName + boundedFolder.Substring(sharedFolderRoot.BoundedFolder.Length));
                                }
                            }
                        }
                    }
                    // Root is Users Shared Folders namespace
                    else{
                        // Get shared user on which accessing user has access rights
                        List<string> sharingUsers = new List<string>();
                        foreach(DataRow dr in GetACL().Tables["ACL"].Rows){
                            string userOrGroup = dr["User"].ToString().ToLower();
                            string sharingUserName = dr["Folder"].ToString().Split(new char[]{'/'},2)[0];

                            // anyone access, so we have access to that folder
                            if(userOrGroup == "anyone"){
                                if(!sharingUsers.Contains(sharingUserName)){
                                    sharingUsers.Add(sharingUserName);
                                }
                            }
                            // accessing user has access to that folder
                            else if(userOrGroup == userName.ToLower()){
                                if(!sharingUsers.Contains(sharingUserName)){
                                    sharingUsers.Add(sharingUserName);
                                }
                            }
                            else{
                                // If group ACL set, see if accessing user is member of that group
                                DataRow drGroup = GetGroup(userOrGroup);
                                if(drGroup != null && IsUserGroupMember(drGroup["GroupName"].ToString(),userName)){
                                    if(!sharingUsers.Contains(sharingUserName)){
                                        sharingUsers.Add(sharingUserName);
                                    }
                                }                                
                            }
                        }

                        //--- Show folders on what user has enough ACL
                        foreach(string sharingUser in sharingUsers){
                            string[] sharingUserFolders = GetFolders(sharingUser,false);
                            foreach(string sharingUserFolder in sharingUserFolders){
                                // Show only folder what user has 'lr' right
                                if((GetUserACL(sharingUser,sharingUserFolder,userName) & IMAP_ACL_Flags.r) != 0){                                
                                    userFolders.Add(sharedFolderRoot.FolderName + "/" + sharingUser + "/" + sharingUserFolder);
                                }
                            }
                        }
                    }                   
                }
            }

			return userFolders.ToArray();
        }
		
		#endregion

		#region function GetSubscribedFolders

		/// <summary>
		/// Gets subscribed IMAP folders.
		/// </summary>
		/// <param name="userName"></param>
		public string[] GetSubscribedFolders(string userName)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetSubscribedFolders")){
				sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);

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

				string[] retVal = new string[ds.Tables["Folders"].Rows.Count];
				int i = 0;
				foreach(DataRow dr in ds.Tables["Folders"].Rows){
					retVal[i] = dr["FolderName"].ToString();
					i++;
				}

				return retVal;
			}
		}
		
		#endregion

		#region function SubscribeFolder

		/// <summary>
		/// Subscribes new IMAP folder.
		/// </summary>
		/// <param name="userName"></param>
		/// <param name="folder"></param>
		public void SubscribeFolder(string userName,string folder)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_SubscribeFolder")){
				sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);
				sqlCmd.AddParameter("@Folder"  ,SqlDbType.NVarChar,folder);

				DataSet ds = sqlCmd.Execute();
			}
		}
		
		#endregion

		#region function UnSubscribeFolder

		/// <summary>
		/// UnSubscribes IMAP folder.
		/// </summary>
		/// <param name="userName"></param>
		/// <param name="folder"></param>
		public void UnSubscribeFolder(string userName,string folder)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UnSubscribeFolder")){
				sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);
				sqlCmd.AddParameter("@Folder"  ,SqlDbType.NVarChar,folder);

				DataSet ds = sqlCmd.Execute();
			}
		}
		
		#endregion

		#region function CreateFolder

		/// <summary>
		/// Creates new 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 contains message to copy. For example: Inbox,Public Folders/Documnets .</param>
		public void CreateFolder(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.
                *) Ensure that folder doesn't 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.
            */

            //--- 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;

                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 + "' already 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 + "' !");
                }
            }

            //--- Create folder
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_CreateFolder")){
				sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,folderOwnerUser);
				sqlCmd.AddParameter("@Folder"  ,SqlDbType.NVarChar,folder);

				DataSet ds = sqlCmd.Execute();

				if(ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0){
					throw new Exception(ds.Tables[0].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion

		#region method DeleteFolder

		/// <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" ,SqlDbType.NVarChar,Guid.NewGuid().ToString());
                            sqlCmd.AddParameter("@user"      ,SqlDbType.NVarChar,folderOwnerUser);
                            sqlCmd.AddParameter("@folder"    ,SqlDbType.NVarChar,folder);
                            sqlCmd.AddParameter("@subject"   ,SqlDbType.NVarChar,subject);
                            sqlCmd.AddParameter("@data"      ,SqlDbType.Image   ,data);				        

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

            //--- Delete folder
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteFolder")){
				sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,folderOwnerUser);
				sqlCmd.AddParameter("@Folder"  ,SqlDbType.NVarChar,folder);

				DataSet ds = sqlCmd.Execute();

				if(ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0){
					throw new Exception(ds.Tables[0].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion

		#region function RenameFolder

		/// <summary>
		/// Renames 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: Trash,Public Folders/Documnets .</param>
		/// <param name="newFolder">New folder name.</param>
		public void RenameFolder(string accessingUser,string folderOwnerUser,string folder,string newFolder)
		{
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that user exists.
                *) Normalize folders. Remove '/' from folder start and end, ... .
                *) Do Shared Folders mapping.
                *) Don't allow to rename shared folders root folder.
                   For BoundedUser root don't allow root folder only,
                   for UsersShared root don't allow root + user name.
                *) Ensure that source folder exists. Throw Exception if don't.
                *) Ensure that destinaton folder doesn't exists. Throw Exception if does.
                *) See if user has sufficient permissions. User requires 'c' permission.
                   There is builtin user system, skip ACL for it.
                *) Rename folder.
            */

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

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

            // 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(originalFolder.ToLower() == mappedFolder.SharedRootName.ToLower() || folderOwnerUser == "" || folder == ""){
                    throw new Exception("Can't rename shared root folder '" + originalFolder + "' !");
                }
            }

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

            // Do Shared Folders mapping.
            string originalNewFolder = newFolder;
            string destinationFolderOwner = folderOwnerUser;
            SharedFolderMapInfo mappedNewFolder = MapSharedFolder(originalNewFolder);
            if(mappedNewFolder.IsSharedFolder){
                destinationFolderOwner = mappedNewFolder.FolderOnwer;
                newFolder = mappedNewFolder.Folder;

                if(originalNewFolder.ToLower() == mappedNewFolder.SharedRootName.ToLower() || destinationFolderOwner == "" || newFolder == ""){
                    throw new Exception("Invalid destination folder value, folder '" + originalNewFolder + "' alreay exists !");
                }
            }

            // Don't allow shared folder to change root folder
            // For example: "Public Folder/aaa" can't be renamed to "Public Folder New/aaa"
            if(mappedFolder.SharedRootName.ToLower() != mappedNewFolder.SharedRootName.ToLower() || mappedFolder.FolderOnwer.ToLower() != mappedNewFolder.FolderOnwer.ToLower()){
                throw new ArgumentException("Shared folder can't change root folder !");
            }
                                
            // Ensure that folder does exist. Throw Exception if don't.
            if(!FolderExists(folderOwnerUser + "/" + folder)){
                throw new Exception("Folder '" + folder + "' doesn't exist !");
            }

            // Ensure that folder doesn't exists. Throw Exception if does.
            if(FolderExists(folderOwnerUser + "/" + newFolder)){
                throw new Exception("Folder '" + newFolder + "' 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 + "' !");
                }
            }

            //--- Rename folder
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_RenameFolder")){
				sqlCmd.AddParameter("@UserName"  ,SqlDbType.NVarChar,folderOwnerUser);
				sqlCmd.AddParameter("@Folder"    ,SqlDbType.NVarChar,folder);
				sqlCmd.AddParameter("@NewFolder" ,SqlDbType.NVarChar,newFolder);

				DataSet ds = sqlCmd.Execute();

				if(ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0){
					throw new Exception(ds.Tables[0].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion
//**
		#region method FolderExists

		/// <summary>
		/// Gets if specified folder exists.
		/// </summary>
		/// <param name="folderName">Folder name which to check. Eg. UserName/Inbox,UserName/Inbox/subfolder.</param>
		/// <returns>Returns true if folder exists, otherwise false.</returns>
		public bool FolderExists(string folderName)
		{
			string[] user_folder = folderName.Split(new char[]{'/'},2);
			string   userName    = user_folder[0];
			if(user_folder.Length == 2){
				folderName = user_folder[1];
			}

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_FolderExists")){
				sqlCmd.AddParameter("@FolderName",SqlDbType.NVarChar,folderName);
				sqlCmd.AddParameter("@UserName"  ,SqlDbType.NVarChar,userName);

				DataSet ds = sqlCmd.Execute();
				if(ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0){
					return true;
				}
			}

			return false;
		}

		#endregion

        #region method FolderCreationTime

        /// <summary>
		/// Gets time when specified folder was created.
		/// </summary>
        /// <param name="folderOwnerUser">User who's folder it is.</param>
        /// <param name="folder">Folder what creation time to get. For example: Inbox,Public Folders/Documnets .</param>
		public DateTime FolderCreationTime(string folderOwnerUser,string folder)
        {
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_FolderExists")){                
				sqlCmd.AddParameter("@userName"  ,SqlDbType.NVarChar,folderOwnerUser);
				sqlCmd.AddParameter("@folderName",SqlDbType.NVarChar,folder);

				DataSet ds = sqlCmd.Execute();
				if(ds.Tables.Count > 0 && ds.Tables[0].Rows.Count > 0){
					return (DateTime)ds.Tables[0].Rows[0]["CreationTime"];
				}
                else{
                    throw new Exception("Folder '" + folderOwnerUser + "/" + folder + "' doesn't exist !");
                }
			}
        }

		#endregion


        #region method GetSharedFolderRoots

        /// <summary>
        /// Gets shared folder root folders.
        /// </summary>
        /// <returns></returns>
        public SharedFolderRoot[] GetSharedFolderRoots()
        {
            /* Implementation notes:
                *) Get shared folder roots.
            */

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetSharedFolderRoots")){
				DataSet dsSharedFolderRoots = sqlCmd.Execute();
				dsSharedFolderRoots.Tables[0].TableName = "SharedFoldersRoots";

                List<SharedFolderRoot> roots = new List<SharedFolderRoot>();
			    foreach(DataRow dr in dsSharedFolderRoots.Tables["SharedFoldersRoots"].Rows){
                    roots.Add(new SharedFolderRoot(
                        dr["RootID"].ToString(),
                        Convert.ToBoolean(dr["Enabled"]),
                        dr["Folder"].ToString(),
                        dr["Description"].ToString(),
                        (SharedFolderRootType_enum)Convert.ToInt32(dr["RootType"]),
                        dr["BoundedUser"].ToString(),
                        dr["BoundedFolder"].ToString()
                    ));
                }

                return roots.ToArray();
			}
        }

        #endregion

        #region method AddSharedFolderRoot

        /// <summary>
        /// Add shared folder root.
        /// </summary>
        /// <param name="rootID">Root folder ID. Guid.NewID().ToString() is suggested.</param>
        /// <param name="enabled">Specifies if root folder is enabled.</param>
        /// <param name="folder">Folder name which will be visible to public.</param>
        /// <param name="description">Description text.</param>
        /// <param name="rootType">Specifies what type root folder is.</param>
        /// <param name="boundedUser">User which to bound root folder.</param>
        /// <param name="boundedFolder">Folder which to bound to public folder.</param>
        public void AddSharedFolderRoot(string rootID,bool enabled,string folder,string description,SharedFolderRootType_enum rootType,string boundedUser,string boundedFolder)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that root ID doesn't exists.
                *) Ensure that root doesn't exists.
                *) Add root folder.
            */

            if(rootID == null || rootID == ""){
                throw new Exception("Invalid rootID value, rootID can't be '' or null !");
            }

            //--- Validate values -------------------------------------//
            ArgsValidator.ValidateNotNull(rootID);
            ArgsValidator.ValidateSharedFolderRoot(folder);
            ArgsValidator.ValidateNotNull(description);
            if(rootType == SharedFolderRootType_enum.BoundedRootFolder){
                ArgsValidator.ValidateUserName(boundedUser);
                ArgsValidator.ValidateFolder(boundedFolder);
            }
            //---------------------------------------------------------//

            /* We handle these is SQL, sql returns these errors in ErrorText
             
                *) Ensure that root ID doesn't exists.
                *) Ensure that root doesn't exists.             
            */

            // Insert group
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddSharedFolderRoot")){
				sqlCmd.AddParameter("@rootID"        ,SqlDbType.NVarChar ,rootID);
				sqlCmd.AddParameter("@enabled"       ,SqlDbType.Bit      ,enabled);
				sqlCmd.AddParameter("@folder"        ,SqlDbType.NVarChar ,folder);
				sqlCmd.AddParameter("@description"   ,SqlDbType.NVarChar ,description);
                sqlCmd.AddParameter("@rootType"      ,SqlDbType.Int      ,rootType);
                sqlCmd.AddParameter("@boundedUser"   ,SqlDbType.NVarChar ,boundedUser);
                sqlCmd.AddParameter("@boundedFolder" ,SqlDbType.NVarChar ,boundedFolder);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Result";

                // Proccess adding errors
				if(ds.Tables["Result"].Rows.Count > 0 && ds.Tables["Result"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Result"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion

        #region method DeleteSharedFolderRoot

        /// <summary>
        /// Deletes shard folders root folder.
        /// </summary>
        /// <param name="rootID">Root folder ID which to delete.</param>
        public void DeleteSharedFolderRoot(string rootID)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that root ID exists.
                *) Delete root folder.
            */

            //--- Validate values -------------------------------------//
            if(rootID == null || rootID == ""){
                throw new Exception("Invalid rootID value, rootID can't be '' or null !");
            }
            //---------------------------------------------------------//

            /* We handle these is SQL, sql returns these errors in ErrorText
             
                *) Ensure that root ID exists.
                *) Delete root folder.
             
            */

            // Delete group and it's emebres
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteSharedFolderRoot")){
				sqlCmd.AddParameter("@rootID" ,SqlDbType.NVarChar,rootID);
							
				DataSet ds = sqlCmd.Execute();
                ds.Tables[0].TableName = "Result";

                // Proccess errors
				if(ds.Tables["Result"].Rows.Count > 0 && ds.Tables["Result"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Result"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion

        #region method UpdateSharedFolderRoot

        /// <summary>
        /// Updates shared folder root.
        /// </summary>
        /// <param name="rootID">Root Folder IF which to update.</param>
        /// <param name="enabled">Specifies if root folder is enabled.</param>
        /// <param name="folder">Folder name which will be visible to public.</param>
        /// <param name="description">Description text.</param>
        /// <param name="rootType">Specifies what type root folder is.</param>
        /// <param name="boundedUser">User which to bound root folder.</param>
        /// <param name="boundedFolder">Folder which to bound to public folder.</param>
        public void UpdateSharedFolderRoot(string rootID,bool enabled,string folder,string description,SharedFolderRootType_enum rootType,string boundedUser,string boundedFolder)
        {
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that root ID exists.
                *) If root folder name is changed, ensure that new root folder won't conflict 
                   any other root folder name. Throw Exception if does.     
                *) Update root folder.
            */

            if(rootID == null || rootID == ""){
                throw new Exception("Invalid rootID value, rootID can't be '' or null !");
            }

            //--- Validate values -------------------------------------//
            ArgsValidator.ValidateNotNull(rootID);
            ArgsValidator.ValidateSharedFolderRoot(folder);
            ArgsValidator.ValidateNotNull(description);
            if(rootType == SharedFolderRootType_enum.BoundedRootFolder){
                ArgsValidator.ValidateUserName(boundedUser);
                ArgsValidator.ValidateFolder(boundedFolder);
            }
            //---------------------------------------------------------//

            /* We handle these is SQL, sql returns these errors in ErrorText
             
            *) Ensure that root ID exists.
                *) If root folder name is changed, ensure that new root folder won't conflict 
                   any other root folder name. Throw Exception if does. 
            */

            // Update root folder
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateSharedFolderRoot")){
				sqlCmd.AddParameter("@rootID"        ,SqlDbType.NVarChar ,rootID);
				sqlCmd.AddParameter("@enabled"       ,SqlDbType.Bit      ,enabled);
				sqlCmd.AddParameter("@folder"        ,SqlDbType.NVarChar ,folder);
				sqlCmd.AddParameter("@description"   ,SqlDbType.NVarChar ,description);
                sqlCmd.AddParameter("@rootType"      ,SqlDbType.Int      ,rootType);
                sqlCmd.AddParameter("@boundedUser"   ,SqlDbType.NVarChar ,boundedUser);
                sqlCmd.AddParameter("@boundedFolder" ,SqlDbType.NVarChar ,boundedFolder);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Result";

                // Proccess adding errors
				if(ds.Tables["Result"].Rows.Count > 0 && ds.Tables["Result"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Result"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion


		#region method GetFolderACL

		/// <summary>
		/// Gets specified folder ACL.
		/// </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 ACL to get. For example: Inbox,Public Folders/Documnets .</param>
		/// <returns></returns>
		public DataView GetFolderACL(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.
                *) Ensure that folder exists. Throw Exception if don't.
                *) See if user has sufficient permissions. User requires 'a' permission.
                   There is builtin user system, skip ACL for it.
                *) Get folder ACL.
            */

            //--- 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;

                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 'a' 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.a) == 0){
                    throw new InsufficientPermissionsException("Insufficient permissions for folder '" + accessingUser + "/" + folder + "' !");
                }
            }

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetFolderACL")){
				if(folder.Length > 0){
					sqlCmd.AddParameter("@FolderName",SqlDbType.NVarChar,folderOwnerUser + "/" + folder);
				}

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

				return ds.Tables["ACL"].DefaultView;
			}
		}

		#endregion

		#region method DeleteFolderACL

		/// <summary>
		/// Deletes specified folder ACL for specified user.
		/// </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 ACL to delete. For example: Inbox,Public Folders/Documnets .</param>
        /// <param name="userOrGroup">User or user group which ACL on specified folder to delete.</param>
		public void DeleteFolderACL(string accessingUser,string folderOwnerUser,string folder,string userOrGroup)
		{
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that folder owner user exists.
                *) Ensure that user or user group 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 'a' permission.
                   There is builtin user system, skip ACL for it.
                *) Delete folder.
            */

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

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

            // Ensure that user or user group exists. Skip check for anyone.
            if(userOrGroup.ToLower() != "anyone" && !GroupExists(userOrGroup)){
                if(!UserExists(userOrGroup)){
                    throw new Exception("Invalid userOrGroup value, there is no such user or group '" + userOrGroup + "' !");
                }
            }

            // 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 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 'a' 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.a) == 0){
                    throw new InsufficientPermissionsException("Insufficient permissions for folder '" + accessingUser + "/" + folder + "' !");
                }
            }

            //--- Delete folder ACL
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteFolderACL")){
				sqlCmd.AddParameter("@FolderName" ,SqlDbType.UniqueIdentifier,folderOwnerUser + "/" + folder);
				sqlCmd.AddParameter("@UserName"   ,SqlDbType.UniqueIdentifier,userOrGroup);
				
				DataSet ds = sqlCmd.Execute();
			}
		}

		#endregion

		#region method SetFolderACL

		/// <summary>
		/// Sets specified folder ACL for specified user.
		/// </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 ACL to set. For example: Inbox,Public Folders/Documnets .</param>
        /// <param name="userOrGroup">>User or user which group ACL set to specified folder.</param>
		/// <param name="setType">Specifies how ACL flags must be stored (ADD,REMOVE,REPLACE).</param>
		/// <param name="aclFlags">ACL flags.</param>
		public void SetFolderACL(string accessingUser,string folderOwnerUser,string folder,string userOrGroup,IMAP_Flags_SetType setType,IMAP_ACL_Flags aclFlags)
		{
            /* Implementation notes:
                *) Validate values. Throw ArgumnetExcetion if invalid values.
                *) Ensure that folder owner user exists.
                *) Ensure that user or user group 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 'a' permission.
                   There is builtin user system, skip ACL for it.
                *) Set folder ACL folder.
            */

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

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

            // Ensure that user or user group exists. Skip check for anyone.
            if(userOrGroup.ToLower() != "anyone" && !GroupExists(userOrGroup)){
                if(!UserExists(userOrGroup)){
                   throw new Exception("Invalid userOrGroup value, there is no such user or group '" + userOrGroup + "' !");
               }
            }

            // 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 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 'a' 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.a) == 0){
                   throw new InsufficientPermissionsException("Insufficient permissions for folder '" + accessingUser + "/" + folder + "' !");
               }
            }

            //--- Set folder ACL
			IMAP_ACL_Flags currentACL_Flags = GetUserACL(folderOwnerUser,folder,folderOwnerUser);
			if(setType == IMAP_Flags_SetType.Replace){
				currentACL_Flags = aclFlags;
			}
			else if(setType == IMAP_Flags_SetType.Add){
				currentACL_Flags |= aclFlags;
			}
			else if(setType == IMAP_Flags_SetType.Remove){
				currentACL_Flags &= ~aclFlags;
			}
	
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_SetFolderACL")){
				sqlCmd.AddParameter("@FolderName"  ,SqlDbType.UniqueIdentifier,folderOwnerUser + "/" + folder);
				sqlCmd.AddParameter("@UserName"    ,SqlDbType.UniqueIdentifier,userOrGroup);
				sqlCmd.AddParameter("@Permissions" ,SqlDbType.UniqueIdentifier,IMAP_Utils.ACL_to_String(currentACL_Flags));
				
				DataSet ds = sqlCmd.Execute();
			}
		}

		#endregion

		#region method GetUserACL

		/// <summary>
		/// Gets what permissions specified user has to specified folder.
		/// </summary>
        /// <param name="folderOwnerUser">User who's folder it is.</param>
		/// <param name="folder">Folder which ACL to get. For example Inbox,Public Folders.</param>
		/// <param name="user">User name which ACL to get.</param>
		/// <returns></returns>
		public IMAP_ACL_Flags GetUserACL(string folderOwnerUser,string folder,string user)
		{
            //--- Validate values -------------------//
            ArgsValidator.ValidateUserName(folderOwnerUser);
            ArgsValidator.ValidateFolder(folder);
            ArgsValidator.ValidateUserName(user);
            //---------------------------------------//

            /*
                *) Ensure that folder owner user exists.
                *) 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.
                 
                   If folder owner is user, and no permissions explicity set, then user have ALL permissions.
                   If user isn't folder owner:
                     *) Try to get User ACl. Also look user groups.
                        If doesn't exist, try to anyone ACL.                           
                        If anyone ACL doesn't exist, try to inherit ACL from parent folders.
                        
                      NOTE: Get maximum ACL user has. For example if user has explicity ACL set and
                            and has Group ACL set, then ACL = max combination of ACL falgs.
            */

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

            // Ensure that folder user exists.
            if(!UserExists(user)){
                throw new Exception("User '" + user + "' 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 !");
                }
            }

            IMAP_ACL_Flags userACL = IMAP_ACL_Flags.None;
                
            // See if ACL is set to this folder, if not show inhereted ACL
            DataView dv = null;
            try{
                dv = GetFolderACL("system",folderOwnerUser,folder);
            }
            // Folder doesnt exist, just skip it
            catch{
            }                
            if(dv != null && dv.Count > 0){
                bool aclSetToUser = false;
                foreach(DataRowView drV in dv){
                    // This is group, user is member of that group
                    if(GroupExists(drV["User"].ToString()) && IsUserGroupMember(drV["User"].ToString(),user)){
                        userACL |= IMAP_Utils.ACL_From_String(drV["Permissions"].ToString());
                    } 
                    // ACL is explicity set to user
                    else if(drV["User"].ToString().ToLower() == user.ToLower()){
			            userACL = IMAP_Utils.ACL_From_String(drV["Permissions"].ToString());
                        aclSetToUser = true;
				    }
                    // There is ANYONE access
					else if(drV["User"].ToString().ToLower() == "anyone"){
					    userACL = IMAP_Utils.ACL_From_String(drV["Permissions"].ToString());
				    }
                }

                // ACL isn't explicity set to folder owner user,give all permissions to folder owner user.
                if(!aclSetToUser && user.ToLower() == folderOwnerUser.ToLower()){
                    userACL = IMAP_ACL_Flags.All;
                }
            }
            else{
                // ACL isn't set and user owner, give full rights
                if(user.ToLower() == folderOwnerUser.ToLower()){
                    userACL = IMAP_ACL_Flags.All;
                }
                else{
                    // Try to inherit ACL from parent folder(s)
                    // Move right to left in path.
                    while(folder.LastIndexOf('/') > -1){
                        // Move 1 level to right in path
                        folder = folder.Substring(0,folder.LastIndexOf('/'));

                        dv = GetFolderACL("system",folderOwnerUser,folder);
                        if(dv.Count > 0){                                
                            foreach(DataRowView drV in dv){
                                string userOrGroup = drV["User"].ToString();
                                // This is group, user is member of that group
                                if(GroupExists(userOrGroup) && IsUserGroupMember(drV["User"].ToString(),user)){
                                    userACL |= IMAP_Utils.ACL_From_String(drV["Permissions"].ToString());
                                }
                                // ACL is explicity set to user
                                else if(drV["User"].ToString().ToLower() == user.ToLower()){
			    		            userACL |= IMAP_Utils.ACL_From_String(drV["Permissions"].ToString());
				                }
                                // There is ANYONE access
					            else if(drV["User"].ToString().ToLower() == "anyone"){
						            userACL |= IMAP_Utils.ACL_From_String(drV["Permissions"].ToString());
						        }
                            }

                            // We inhereted all permission, don't look other parent folders anymore
                            break;
                        }
                    }
                }
            }

			return userACL;
		}

		#endregion


        #region method CreateUserDefaultFolders

        /// <summary>
        /// Creates specified user default folders, if they don't exist already.
        /// </summary>
        /// <param name="userName">User name to who's default folders to create.</param>
        public void CreateUserDefaultFolders(string userName)
        {
            foreach(DataRowView drV in GetUsersDefaultFolders()){
                if(!FolderExists(userName + "/" + drV["FolderName"].ToString())){
                    CreateFolder("system",userName,drV["FolderName"].ToString());
                    SubscribeFolder(userName,drV["FolderName"].ToString());
                }
            }
        }
        
        #endregion

        #region method GetUsersDefaultFolders

        /// <summary>
        /// Gets users default folders.
        /// </summary>
        /// <returns></returns>
        public DataView GetUsersDefaultFolders()
        {
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetUsersDefaultFolders")){
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "UsersDefaultFolders";

                // Force Inbox to be created
                if(ds.Tables["UsersDefaultFolders"].Rows.Count == 0){
                    DataRow dr = ds.Tables["UsersDefaultFolders"].NewRow();
				    dr["FolderName"] = "Inbox";
				    dr["Permanent"]  = true;									
				    ds.Tables["UsersDefaultFolders"].Rows.Add(dr);
                }

				return ds.Tables["UsersDefaultFolders"].DefaultView;
			}
        }

        #endregion

        #region method AddUsersDefaultFolder

        /// <summary>
        /// Adds users default folder.
        /// </summary>
        /// <param name="folderName">Folder name.</param>
        /// <param name="permanent">Spcifies if folder is permanent, user can't delete it.</param>
        public void AddUsersDefaultFolder(string folderName,bool permanent)
        {
            ArgsValidator.ValidateFolder(folderName);

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddUsersDefaultFolder")){
				sqlCmd.AddParameter("@folderName" ,SqlDbType.NVarChar,folderName);
				sqlCmd.AddParameter("@permanent  ",SqlDbType.Bit     ,permanent);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "UsersDefaultFolders";

				if(ds.Tables["UsersDefaultFolders"].Rows.Count > 0 && ds.Tables["UsersDefaultFolders"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["UsersDefaultFolders"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion

        #region method DeleteUsersDefaultFolder

        /// <summary>
        /// Deletes specified users default folder.
        /// </summary>
        /// <param name="folderName">Folder name.</param>
        public void DeleteUsersDefaultFolder(string folderName)
        {
            ArgsValidator.ValidateFolder(folderName);

            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteUsersDefaultFolder")){
				sqlCmd.AddParameter("@folderName" ,SqlDbType.NVarChar,folderName);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "UsersDefaultFolders";

				if(ds.Tables["UsersDefaultFolders"].Rows.Count > 0 && ds.Tables["UsersDefaultFolders"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["UsersDefaultFolders"].Rows[0]["ErrorText"].ToString());
				}
			}
        }

        #endregion


		#region method GetMailboxSize

		/// <summary>
		/// Gets specified user mailbox size.
		/// </summary>
		/// <param name="userName">User name.</param>
		/// <returns>Returns mailbox size.</returns>
		public long GetMailboxSize(string userName)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetMailboxSize")){
				sqlCmd.AddParameter("@UserName",SqlDbType.NVarChar,userName);

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

				if(ds.Tables["Detail"].Rows.Count > 0){
					return Convert.ToInt64(ds.Tables["Detail"].Rows[0]["MailboxSize"]);
				}
				else{
					return 0;
				}
			}
		}

		#endregion


        #region method GetRecycleBinSettings

        /// <summary>
        /// Gets recycle bin settings.
        /// </summary>
        /// <returns></returns>
        public DataTable GetRecycleBinSettings()
        {
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetRecycleBinSettings")){
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "RecycleBinSettings";

                if(ds.Tables["RecycleBinSettings"].Rows.Count == 0){
                    DataRow dr = ds.Tables["RecycleBinSettings"].NewRow();
                    dr["DeleteToRecycleBin"]  = false;
                    dr["DeleteMessagesAfter"] = 1;
                    ds.Tables["RecycleBinSettings"].Rows.Add(dr);
                }

				return ds.Tables["RecycleBinSettings"];
			}
        }

        #endregion

        #region method UpdateRecycleBinSettings

        /// <summary>
        /// Updates recycle bin settings.
        /// </summary>
        /// <param name="deleteToRecycleBin">Specifies if deleted messages are store to recycle bin.</param>
        /// <param name="deleteMessagesAfter">Specifies how old messages will be deleted.</param>
        public void UpdateRecycleBinSettings(bool deleteToRecycleBin,int deleteMessagesAfter)
        {
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateRecycleBinSettings")){
                sqlCmd.AddParameter("@deleteToRecycleBin" ,SqlDbType.Bit,deleteToRecycleBin);
                sqlCmd.AddParameter("@deleteMessagesAfter",SqlDbType.Int,deleteMessagesAfter);
				DataSet ds = sqlCmd.Execute();
			}
        }

        #endregion

        #region method GetRecycleBinMessagesInfo

		/// <summary>
        /// Gets recycle bin messages info. 
        /// </summary>
        /// <param name="user">User who's recyclebin messages to get or null if all users messages.</param>
        /// <param name="startDate">Messages from specified date. Pass DateTime.MinValue if not used.</param>
        /// <param name="endDate">Messages to specified date. Pass DateTime.MinValue if not used.</param>
        /// <returns></returns>
        public DataView GetRecycleBinMessagesInfo(string user,DateTime startDate,DateTime endDate)
        {
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetRecycleBinMessagesInfo")){
                if(user != null && user != ""){
                    sqlCmd.AddParameter("@userName",SqlDbType.NVarChar,user);
                }
                else{
                    sqlCmd.AddParameter("@userName",SqlDbType.NVarChar,null);
                }
                if(startDate != DateTime.MinValue){
                    sqlCmd.AddParameter("@startDate",SqlDbType.DateTime,startDate);
                }
                else{
                    sqlCmd.AddParameter("@startDate",SqlDbType.DateTime,new DateTime(1900,1,1));
                }
                if(endDate != DateTime.MinValue){
                    sqlCmd.AddParameter("@endDate",SqlDbType.DateTime,endDate);
                }
                else{
                    sqlCmd.AddParameter("@endDate",SqlDbType.DateTime,DateTime.MaxValue);
                }
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "MessagesInfo";

				return ds.Tables["MessagesInfo"].DefaultView;
			}
        }
				
		#endregion

        #region method GetRecycleBinMessages

        /// <summary>
        /// Gets recycle bin message stream. NOTE: This method caller must take care of closing stream. 
        /// </summary>
        /// <param name="messageID">Message ID if of message what to get.</param>
        /// <returns></returns>
        public Stream GetRecycleBinMessage(string messageID)
        {
            // Get message and message info from recycle bin
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetRecycleBinMessage")){
                sqlCmd.AddParameter("@messageID" ,SqlDbType.VarChar,messageID);
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "RecycleBin";
                
				if(ds.Tables["RecycleBin"].Rows.Count > 0){
                    return new MemoryStream(((byte[])ds.Tables["RecycleBin"].Rows[0]["Data"]));
                }
                else{
                    throw new Exception("Specified message doesn't exist !");
                }
			}
        }
				
		#endregion

        #region method DeleteRecycleBinMessage

        /// <summary>
        /// Deletes specified recycle bin message.
        /// </summary>
        /// <param name="messageID">Message ID.</param>
        public void DeleteRecycleBinMessage(string messageID)
        {
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteRecycleBinMessage")){
                sqlCmd.AddParameter("@messageID" ,SqlDbType.NVarChar,messageID);
				DataSet ds = sqlCmd.Execute();
			}
        }

        #endregion

        #region method RestoreRecycleBinMessage

        /// <summary>
        /// Restores specified recycle bin message.
        /// </summary>
        /// <param name="messageID">Message ID.</param>
        public void RestoreRecycleBinMessage(string messageID)
        {
            // Get message and message info from recycle bin
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetRecycleBinMessage")){
                sqlCmd.AddParameter("@messageID" ,SqlDbType.VarChar,messageID);
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "RecycleBin";
                
				if(ds.Tables["RecycleBin"].Rows.Count > 0){
                    string userName = ds.Tables["RecycleBin"].Rows[0]["User"].ToString();
                    string folder   = ds.Tables["RecycleBin"].Rows[0]["Folder"].ToString();

                    // If folder doesn't exist, create it
                    if(!FolderExists(userName + "/" + folder)){
                        CreateFolder("system",userName,folder);
                    }

                    // Try to store message back
                    StoreMessage(
                        "system",
                        userName,
                        folder,
                        new MemoryStream(((byte[])ds.Tables["RecycleBin"].Rows[0]["Data"])),
                        DateTime.Now,
                        IMAP_MessageFlags.Recent
                    );
                }
			}
        }

        #endregion

		#endregion

	
		#region Security related

		#region function GetSecurityList

		/// <summary>
		/// Gets security entries list.
		/// </summary>
		public DataView GetSecurityList()
		{			
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetSecurityList")){
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "IPSecurity";

				return ds.Tables["IPSecurity"].DefaultView;
			}
		}
		
		#endregion


		#region method AddSecurityEntry

		/// <summary>
		/// Adds new IP security entry.
		/// </summary>
		/// <param name="id">IP security entry ID.</param>
		/// <param name="enabled">Specifies if IP security entry is enabled.</param>
		/// <param name="description">IP security entry description text.</param>
		/// <param name="service">Specifies service for what security entry applies.</param>
		/// <param name="action">Specifies what action done if IP matches to security entry range.</param>
		/// <param name="startIP">Range start IP.</param>
		/// <param name="endIP">Range end IP.</param>
		public void AddSecurityEntry(string id,bool enabled,string description,Service_enum service,IPSecurityAction_enum action,IPAddress startIP,IPAddress endIP)
		{
			if(id.Length == 0){
				throw new Exception("You must specify securityID");
			}
            
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddSecurityEntry")){
				sqlCmd.AddParameter("@id"          ,SqlDbType.NVarChar,id);
                sqlCmd.AddParameter("@enabled"     ,SqlDbType.BigInt,enabled);
				sqlCmd.AddParameter("@description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@service"     ,SqlDbType.Int,(int)service);
				sqlCmd.AddParameter("@Action"      ,SqlDbType.Int,(int)action);
				sqlCmd.AddParameter("@StartIP"     ,SqlDbType.NVarChar,startIP.ToString());
				sqlCmd.AddParameter("@EndIP"       ,SqlDbType.NVarChar,endIP.ToString());
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "IPSecurity";

				if(ds.Tables["IPSecurity"].Rows.Count > 0 && ds.Tables["IPSecurity"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["IPSecurity"].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion

		#region function DeleteSecurityEntry

		/// <summary>
		/// Deletes security entry.
		/// </summary>
		/// <param name="id">IP security entry ID.</param>
		public void DeleteSecurityEntry(string id)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteSecurityEntry")){
				sqlCmd.AddParameter("@SecurityID" ,SqlDbType.NVarChar,id);
							
				DataSet ds = sqlCmd.Execute();
			}
		}
		
		#endregion

		#region function UpdateSecurityEntry

		/// <summary>
		/// Updates IP security entry.
		/// </summary>
		/// <param name="id">IP security entry ID.</param>
		/// <param name="enabled">Specifies if IP security entry is enabled.</param>
		/// <param name="description">IP security entry description text.</param>
		/// <param name="service">Specifies service for what security entry applies.</param>
		/// <param name="action">Specifies what action done if IP matches to security entry range.</param>
		/// <param name="startIP">Range start IP.</param>
		/// <param name="endIP">Range end IP.</param>
		public void UpdateSecurityEntry(string id,bool enabled,string description,Service_enum service,IPSecurityAction_enum action,IPAddress startIP,IPAddress endIP)
		{
            
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateSecurityEntry")){
				sqlCmd.AddParameter("@id"          ,SqlDbType.NVarChar,id);
                sqlCmd.AddParameter("@enabled"     ,SqlDbType.BigInt,enabled);
				sqlCmd.AddParameter("@description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@service"     ,SqlDbType.Int,(int)service);
				sqlCmd.AddParameter("@Action"      ,SqlDbType.Int,(int)action);
				sqlCmd.AddParameter("@StartIP"     ,SqlDbType.NVarChar,startIP.ToString());
				sqlCmd.AddParameter("@EndIP"       ,SqlDbType.NVarChar,endIP.ToString());
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "IPSecurity";

				if(ds.Tables["IPSecurity"].Rows.Count > 0 && ds.Tables["IPSecurity"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["IPSecurity"].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion

		#endregion


		#region Filters related

		#region function GetFilters

		/// <summary>
		/// Gets filter list.
		/// </summary>
		/// <returns></returns>
		public DataView GetFilters()
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetFilters")){				
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "SmtpFilters";

				return ds.Tables["SmtpFilters"].DefaultView;
			}
		}
		
		#endregion


		#region function AddFilter

		/// <summary>
		/// Adds new filter.
		/// </summary>
		/// <param name="filterID">Filter ID. Suggested value is Guid.NewGuid() .</param>
		/// <param name="description">Filter description</param>
		/// <param name="type">Filter type. Eg. ISmtpMessageFilter.</param>
		/// <param name="assembly">Assembly with full location. Eg. C:\MailServer\Filters\filter.dll .</param>
		/// <param name="className">Filter full class name, wih namespace. Eg. LumiSoft.MailServer.Fileters.Filter1 .</param>
		/// <param name="cost">Filters are sorted by cost and proccessed with cost value. Smallest cost is proccessed first.</param>
		/// <param name="enabled">Specifies if filter is enabled.</param>
		/// <remarks>Throws exception if specified filter entry already exists.</remarks>
		public void AddFilter(string filterID,string description,string type,string assembly,string className,long cost,bool enabled)
		{
			if(filterID.Length == 0){
				throw new Exception("You must specify filterID");
			}

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_AddFilter")){
				sqlCmd.AddParameter("@FilterID"    ,SqlDbType.NVarChar,filterID);
				sqlCmd.AddParameter("@Description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@Type"        ,SqlDbType.NVarChar,type);
				sqlCmd.AddParameter("@Assembly"    ,SqlDbType.NVarChar,assembly);
				sqlCmd.AddParameter("@ClassName"   ,SqlDbType.NVarChar,className);
				sqlCmd.AddParameter("@Cost"        ,SqlDbType.BigInt   ,cost);
				sqlCmd.AddParameter("@Enabled"     ,SqlDbType.Bit      ,enabled);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Filters";

				if(ds.Tables["Filters"].Rows.Count > 0 && ds.Tables["Filters"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Filters"].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion

		#region function DeleteFilter

		/// <summary>
		/// Deletes specified filter.
		/// </summary>
		/// <param name="filterID">FilterID of the filter which to delete.</param>
		public void DeleteFilter(string filterID)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_DeleteFilter")){
				sqlCmd.AddParameter("@FilterID" ,SqlDbType.NVarChar,filterID);
							
				DataSet ds = sqlCmd.Execute();
			}
		}
		
		#endregion

		#region function UpdateFilter

		/// <summary>
		/// Updates specified filter.
		/// </summary>		
		/// <param name="filterID">FilterID which to update.</param>
		/// <param name="description">Filter description</param>
		/// <param name="type">Filter type. Eg. ISmtpMessageFilter.</param>
		/// <param name="assembly">Assembly with full location. Eg. C:\MailServer\Filters\filter.dll .</param>
		/// <param name="className">Filter full class name, wih namespace. Eg. LumiSoft.MailServer.Fileters.Filter1 .</param>
		/// <param name="cost">Filters are sorted by cost and proccessed with cost value. Smallest cost is proccessed first.</param>
		/// <param name="enabled">Specifies if filter is enabled.</param>
		/// <returns></returns>
		public void UpdateFilter(string filterID,string description,string type,string assembly,string className,long cost,bool enabled)
		{
			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateFilter")){
				sqlCmd.AddParameter("@FilterID"    ,SqlDbType.NVarChar,filterID);
				sqlCmd.AddParameter("@Description" ,SqlDbType.NVarChar,description);
				sqlCmd.AddParameter("@Type"        ,SqlDbType.NVarChar,type);
				sqlCmd.AddParameter("@Assembly"    ,SqlDbType.NVarChar,assembly);
				sqlCmd.AddParameter("@ClassName"   ,SqlDbType.NVarChar,className);
				sqlCmd.AddParameter("@Cost"        ,SqlDbType.BigInt   ,cost);
				sqlCmd.AddParameter("@Enabled"     ,SqlDbType.Bit      ,enabled);
							
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "Filters";

				if(ds.Tables["Filters"].Rows.Count > 0 && ds.Tables["Filters"].Rows[0]["ErrorText"].ToString().Length > 0){
					throw new Exception(ds.Tables["Filters"].Rows[0]["ErrorText"].ToString());
				}
			}
		}
		
		#endregion

		#endregion


		#region Settings related

		#region method GetSettings

		/// <summary>
		/// Gets server settings.
		/// </summary>
		/// <returns></returns>
		public DataRow GetSettings()
		{
			DataSet ds = new DataSet();
			API_Utlis.CreateSettingsSchema(ds);

			using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_GetSettings")){
				DataSet dsS = sqlCmd.Execute();
				dsS.Tables[0].TableName = "Settings";

				if(dsS.Tables["Settings"].Rows.Count > 0){
					byte[] data = (byte[])dsS.Tables["Settings"].Rows[0]["Settings"];

					ds.ReadXml(new MemoryStream(data));
				}		
			}

            if(ds.Tables["Settings"].Rows.Count == 0){
				ds.Tables["Settings"].Rows.Add(ds.Tables["Settings"].NewRow());
			}
			
			foreach(DataRow dr in ds.Tables["Settings"].Rows){
				foreach(DataColumn dc in ds.Tables["Settings"].Columns){
					if(dr.IsNull(dc.ColumnName)){
						dr[dc.ColumnName] = dc.DefaultValue;
					}
				}
			}

            if(ds.Tables["SMTP_Bindings"].Rows.Count == 0){
                ds.Tables["SMTP_Bindings"].Rows.Add(ds.Tables["SMTP_Bindings"].NewRow());
            }
            foreach(DataRow dr in ds.Tables["SMTP_Bindings"].Rows){
				foreach(DataColumn dc in ds.Tables["SMTP_Bindings"].Columns){
					if(dr.IsNull(dc.ColumnName)){
						dr[dc.ColumnName] = dc.DefaultValue;
					}
				}
			}

            if(ds.Tables["POP3_Bindings"].Rows.Count == 0){
                ds.Tables["POP3_Bindings"].Rows.Add(ds.Tables["POP3_Bindings"].NewRow());
            }
            foreach(DataRow dr in ds.Tables["POP3_Bindings"].Rows){
				foreach(DataColumn dc in ds.Tables["POP3_Bindings"].Columns){
					if(dr.IsNull(dc.ColumnName)){
						dr[dc.ColumnName] = dc.DefaultValue;
					}
				}
			}

            if(ds.Tables["IMAP_Bindings"].Rows.Count == 0){
                ds.Tables["IMAP_Bindings"].Rows.Add(ds.Tables["IMAP_Bindings"].NewRow());
            }
            foreach(DataRow dr in ds.Tables["IMAP_Bindings"].Rows){
				foreach(DataColumn dc in ds.Tables["IMAP_Bindings"].Columns){
					if(dr.IsNull(dc.ColumnName)){
						dr[dc.ColumnName] = dc.DefaultValue;
					}
				}
			}
            			
			return ds.Tables["Settings"].Rows[0];
		}

		#endregion

		#region method UpdateSettings

		/// <summary>
		/// Updates server settings.
		/// </summary>
		public void UpdateSettings(DataRow settings)
		{/*
			DataRow row = GetSettings();
			foreach(DataColumn dc in row.Table.Columns){
				row[dc.ColumnName] = settings[dc.ColumnName];
			}*/

			using(MemoryStream strm = new MemoryStream()){
				settings.Table.DataSet.WriteXml(strm);

				using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"lspr_UpdateSettings")){
					sqlCmd.AddParameter("@Settings" ,SqlDbType.Image,strm.ToArray());
								
					DataSet ds = sqlCmd.Execute();
				}
			}
		}

		#endregion

		#endregion



        #region method GetACL

        /// <summary>
        /// Gets all folders IMAP acl entries.
        /// </summary>
        /// <returns></returns>
        private DataSet GetACL()
        {
            using(WSqlCommand sqlCmd = new WSqlCommand(m_ConStr,"select * from lsIMAP_ACL")){
				DataSet ds = sqlCmd.Execute();
				ds.Tables[0].TableName = "ACL";

				return ds;
			}
        }

        #endregion

        #region method IsUserGroupMember

        /// <summary>
        /// Gets if specified user is specified user group member. Returns true if user is user group member.
        /// </summary>
        /// <param name="group">User group name.</param>
        /// <param name="user">User name.</param>
        /// <returns>Returns true if user is user group member.</returns>
        private bool IsUserGroupMember(string group,string user)
        {
            List<string> proccessedGroups = new List<string>();
            Queue<string> membersQueue = new Queue<string>();
            string[] members = GetGroupMembers(group);
            foreach(string member in members){
                membersQueue.Enqueue(member);
            }

            while(membersQueue.Count > 0){
                string member = membersQueue.Dequeue();
                // Nested group
                DataRow drGroup = GetGroup(member);
                if(drGroup != null){
                    // Don't proccess poroccessed groups any more, causes infinite loop
                    if(!proccessedGroups.Contains(member.ToLower())){
                        // Skip disabled groups
                        if(Convert.ToBoolean(drGroup["Enabled"])){
                            members = GetGroupMembers(member);
                            foreach(string m in members){
                                membersQueue.Enqueue(m);
                            }  
                        }

                        proccessedGroups.Add(member.ToLower());
                    }
                }
                // User
                else{
                    if(member.ToLower() == user.ToLower()){
                        return true;
                    }
                }
            }

            return false;
        }

        #endregion

        #region method MapSharedFolder

        /// <summary>
        /// This call holds shaared folder mapping info.
        /// </summary>
        private class SharedFolderMapInfo
        {
            private string m_OriginalFolder = "";
            private string m_FolderOwner    = "";
            private string m_Folder         = "";
            private string m_SharedRootName = "";

            /// <summary>
            /// Default constructor.
            /// </summary>
            /// <param name="originalFolder"></param>
            /// <param name="folderOwner"></param>
            /// <param name="folder"></param>
            /// <param name="sharedRootName"></param>
            public SharedFolderMapInfo(string originalFolder,string folderOwner,string folder,string sharedRootName)
            {
                m_OriginalFolder = originalFolder;
                m_FolderOwner    = folderOwner;
                m_Folder         = folder;
                m_SharedRootName = sharedRootName;
            }


            #region Properties Implementation

            /// <summary>
            /// Gets original folder.
            /// </summary>
            public string OriginalFolder
            {
                get{ return m_OriginalFolder; }
            }

            /// <summary>
            /// Shared folder owner. This is available only if OriginalFolder is shared folder.
            /// </summary>
            public string FolderOnwer
            {
                get{ return m_FolderOwner; }
            }

            /// <summary>
            /// Gets shared folder owner folder. This is available only if OriginalFolder is shared folder.
            /// </summary>
            public string Folder
            {
                get{ return m_Folder; }
            }

            /// <summary>
            /// Gets shared root folder name. This is available only if OriginalFolder is shared folder.
            /// </summary>
            public string SharedRootName
            {
                get{ return m_SharedRootName; }
            }

            /// <summary>
            /// Gets if OriginalFolder is shared folder.
            /// </summary>
            public bool IsSharedFolder
            {
                get{ return m_SharedRootName != ""; }
            }

            #endregion
        }

        /// <summary>
        /// If folder is Shared Folder, then maps folder to actual account.
        /// </summary>
        /// <param name="folder">Folder to map.</param>
        /// <returns></returns>
        private SharedFolderMapInfo MapSharedFolder(string folder)
        {
            string rootFolder = folder.Split(new char[]{'/'},2)[0];

            SharedFolderRoot[] roots = GetSharedFolderRoots();
            foreach(SharedFolderRoot root in roots){
                if(root.RootType == SharedFolderRootType_enum.BoundedRootFolder){
                    if(rootFolder.ToLower() == root.FolderName.ToLower()){
                    //  folder = root.BoundedUser + "/" + root.BoundedFolder + folder.Substring(root.FolderName.Length);
                        return new SharedFolderMapInfo(folder,root.BoundedUser,root.BoundedFolder,root.FolderName);
                    }
                }
                else if(root.RootType == SharedFolderRootType_enum.UsersSharedFolder){
                    if(rootFolder.ToLower() == root.FolderName.ToLower()){
                        // Cut off root folder name
                        string[] p = folder.Split(new char[]{'/'},3);
                        // root/user/folder
                        if(p.Length == 3){
                            return new SharedFolderMapInfo(folder,p[1],p[2],root.FolderName);
                        }
                        // root/user
                        else if(p.Length == 3){
                            return new SharedFolderMapInfo(folder,p[1],"",root.FolderName);
                        }
                        // root (User and folder missing)
                        else{
                            return new SharedFolderMapInfo(folder,"","",root.FolderName);
                        }
                    }
                }
            }

            return new SharedFolderMapInfo(folder,"","","");
        }

        #endregion

        #region method GetGroup

        /// <summary>
        /// Gets specified user group. If group doesn't exist, returns null;
        /// </summary>
        /// <param name="group">User group name.</param>
        /// <returns></returns>
        private DataRow GetGroup(string group)
        {
		    foreach(DataRowView drV in GetGroups()){
                if(group.ToLower() == drV["GroupName"].ToString().ToLower()){
                    return drV.Row;
                }
            }

            return null;
        }

        #endregion

		#region function GetTopLines

		private byte[] GetTopLines(Stream strm,int nrLines)
		{
			TextReader reader = (TextReader)new StreamReader(strm);
			
			strm.Position = 0;

			int  lCounter = 0;
			int  msgLine  = -1;
			bool msgLines = false;
			StringBuilder strBuilder = new StringBuilder();
			while(true){
				string line = reader.ReadLine();

				// Reached end of message
				if(line == null){
					break;
				}
				else{
					// End of header reached
					if(!msgLines && line.Length == 0){
						// Set flag, that message lines reading start.
						msgLines = true;
					}

					// Check that wanted message lines count isn't exceeded
					if(msgLines){
						if(msgLine > nrLines){
							break;
						}
						msgLine++;
					}

					strBuilder.Append(line + "\r\n");
				}

				lCounter++;
			}
	
			return System.Text.Encoding.ASCII.GetBytes(strBuilder.ToString());			
		}

		#endregion
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Estonia Estonia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions