/// <summary>
 /// Raises <b>Search</b> event.
 /// </summary>
 /// <param name="e">Event args.</param>
 private void OnSearch(IMAP_e_Search e)
 {
     if(this.Search != null){
         this.Search(this,e);
     }
 }
        private void SEARCH(bool uid,string cmdTag,string cmdText)
        {
            /* RFC 3501 6.4.4. SEARCH Command.
                Arguments:  OPTIONAL [CHARSET] specification
                            searching criteria (one or more)

                Responses:  REQUIRED untagged response: SEARCH

                Result:     OK - search completed
                            NO - search error: can't search that [CHARSET] or criteria
                            BAD - command unknown or arguments invalid

                The SEARCH command searches the mailbox for messages that match
                the given searching criteria.  Searching criteria consist of one
                or more search keys.  The untagged SEARCH response from the server
                contains a listing of message sequence numbers corresponding to
                those messages that match the searching criteria.
            
                When multiple keys are specified, the result is the intersection
                (AND function) of all the messages that match those keys.  For
                example, the criteria DELETED FROM "SMITH" SINCE 1-Feb-1994 refers
                to all deleted messages from Smith that were placed in the mailbox
                since February 1, 1994.  A search key can also be a parenthesized
                list of one or more search keys (e.g., for use with the OR and NOT
                keys).

                Server implementations MAY exclude [MIME-IMB] body parts with
                terminal content media types other than TEXT and MESSAGE from
                consideration in SEARCH matching.

                The OPTIONAL [CHARSET] specification consists of the word
                "CHARSET" followed by a registered [CHARSET].  It indicates the
                [CHARSET] of the strings that appear in the search criteria.
                [MIME-IMB] content transfer encodings, and [MIME-HDRS] strings in
                [RFC-2822]/[MIME-IMB] headers, MUST be decoded before comparing
                text in a [CHARSET] other than US-ASCII.  US-ASCII MUST be
                supported; other [CHARSET]s MAY be supported.

                If the server does not support the specified [CHARSET], it MUST
                return a tagged NO response (not a BAD).  This response SHOULD
                contain the BADCHARSET response code, which MAY list the
                [CHARSET]s supported by the server.

                In all search keys that use strings, a message matches the key if
                the string is a substring of the field.  The matching is
                case-insensitive.

                The defined search keys are as follows.  Refer to the Formal
                Syntax section for the precise syntactic definitions of the
                arguments.

                <sequence set>
                    Messages with message sequence numbers corresponding to the
                    specified message sequence number set.

                ALL
                    All messages in the mailbox; the default initial key for ANDing.

                ANSWERED
                    Messages with the \Answered flag set.

                BCC <string>
                    Messages that contain the specified string in the envelope
                    structure's BCC field.

                BEFORE <date>
                    Messages whose internal date (disregarding time and timezone)
                    is earlier than the specified date.

                BODY <string>
                    Messages that contain the specified string in the body of the
                    message.

                CC <string>
                    Messages that contain the specified string in the envelope
                    structure's CC field.

                DELETED
                    Messages with the \Deleted flag set.

                DRAFT
                    Messages with the \Draft flag set.

                FLAGGED
                    Messages with the \Flagged flag set.

                FROM <string>
                    Messages that contain the specified string in the envelope
                    structure's FROM field.

                HEADER <field-name> <string>
                    Messages that have a header with the specified field-name (as
                    defined in [RFC-2822]) and that contains the specified string
                    in the text of the header (what comes after the colon).  If the
                    string to search is zero-length, this matches all messages that
                    have a header line with the specified field-name regardless of
                    the contents.

                KEYWORD <flag>
                    Messages with the specified keyword flag set.

                LARGER <n>
                    Messages with an [RFC-2822] size larger than the specified
                    number of octets.

                NEW
                    Messages that have the \Recent flag set but not the \Seen flag.
                    This is functionally equivalent to "(RECENT UNSEEN)".

                NOT <search-key>
                    Messages that do not match the specified search key.

                OLD
                    Messages that do not have the \Recent flag set.  This is
                    functionally equivalent to "NOT RECENT" (as opposed to "NOT NEW").

                ON <date>
                    Messages whose internal date (disregarding time and timezone)
                    is within the specified date.

                OR <search-key1> <search-key2>
                    Messages that match either search key.

                RECENT
                    Messages that have the \Recent flag set.

                SEEN
                    Messages that have the \Seen flag set.

                SENTBEFORE <date>
                    Messages whose [RFC-2822] Date: header (disregarding time and
                    timezone) is earlier than the specified date.

                SENTON <date>
                    Messages whose [RFC-2822] Date: header (disregarding time and
                    timezone) is within the specified date.

                SENTSINCE <date>
                    Messages whose [RFC-2822] Date: header (disregarding time and
                    timezone) is within or later than the specified date.

                SINCE <date>
                    Messages whose internal date (disregarding time and timezone)
                    is within or later than the specified date.

                SMALLER <n>
                    Messages with an [RFC-2822] size smaller than the specified
                    number of octets.

                SUBJECT <string>
                    Messages that contain the specified string in the envelope
                    structure's SUBJECT field.

                TEXT <string>
                    Messages that contain the specified string in the header or
                    body of the message.

                TO <string>
                    Messages that contain the specified string in the envelope
                    structure's TO field.

                UID <sequence set>
                    Messages with unique identifiers corresponding to the specified
                    unique identifier set.  Sequence set ranges are permitted.

                UNANSWERED
                    Messages that do not have the \Answered flag set.

                UNDELETED
                    Messages that do not have the \Deleted flag set.

                UNDRAFT
                    Messages that do not have the \Draft flag set.

                UNFLAGGED
                    Messages that do not have the \Flagged flag set.

                UNKEYWORD <flag>
                    Messages that do not have the specified keyword flag set.

                UNSEEN
                    Messages that do not have the \Seen flag set.

                Example:    C: A282 SEARCH FLAGGED SINCE 1-Feb-1994 NOT FROM "Smith"
                            S: * SEARCH 2 84 882
                            S: A282 OK SEARCH completed
                            C: A283 SEARCH TEXT "string not in mailbox"
                            S: * SEARCH
                            S: A283 OK SEARCH completed
                            C: A284 SEARCH CHARSET UTF-8 TEXT {6}
                            C: XXXXXX
                            S: * SEARCH 43
                            S: A284 OK SEARCH completed
            */

            if(!this.IsAuthenticated){
                m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO","Authentication required."));

                return;
            }
            if(m_pSelectedFolder == null){
                m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO","Error: This command is valid only in selected state."));

                return;
            }

            // Store start time
			long startTime = DateTime.Now.Ticks;

            #region Parse arguments

            _CmdReader cmdReader = new _CmdReader(this,cmdText,Encoding.UTF8);            
            cmdReader.Start();

            StringReader r = new StringReader(cmdReader.CmdLine);
            
            // See if we have optional CHARSET argument.
            if(r.StartsWith("CHARSET",false)){
                r.ReadWord();

                string charset = r.ReadWord();
                if(!(string.Equals(charset,"US-ASCII",StringComparison.InvariantCultureIgnoreCase) || string.Equals(charset,"UTF-8",StringComparison.InvariantCultureIgnoreCase))){
                    m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"NO",new IMAP_t_orc_BadCharset(new string[]{"US-ASCII","UTF-8"}),"Not supported charset."));

                    return;
                }
            }

            #endregion

            try{
                IMAP_Search_Key_Group criteria = IMAP_Search_Key_Group.Parse(r);

                UpdateSelectedFolderAndSendChanges();
                
                List<int> matchedValues = new List<int>();

                IMAP_e_Search searchArgs = new IMAP_e_Search(criteria,new IMAP_r_ServerStatus(cmdTag,"OK","SEARCH completed in %exectime seconds."));
                searchArgs.Matched += new EventHandler<EventArgs<long>>(delegate(object s,EventArgs<long> e){
                    if(uid){
                        matchedValues.Add((int)e.Value);
                    }
                    else{
                        // Search sequence-number for that message.
                        int seqNo = m_pSelectedFolder.GetSeqNo(e.Value);
                        if(seqNo != -1){
                            matchedValues.Add((int)e.Value);
                        }
                    }                    
                });
                OnSearch(searchArgs);

                m_pResponseSender.SendResponseAsync(new IMAP_r_u_Search(matchedValues.ToArray()));
                m_pResponseSender.SendResponseAsync(IMAP_r_ServerStatus.Parse(searchArgs.Response.ToString().TrimEnd().Replace("%exectime",((DateTime.Now.Ticks - startTime) / (decimal)10000000).ToString("f2"))));
            }
            catch{
                m_pResponseSender.SendResponseAsync(new IMAP_r_ServerStatus(cmdTag,"BAD","Error in arguments."));
            }            
        }
Example #3
0
        /// <summary>
        /// Searhes specified folder messages which match to search criteria.
        /// </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="e">IMAP search event data.</param>
        public void Search(string accessingUser,string folderOwnerUser,string folder,IMAP_e_Search 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.
                *) Search messages.
            */

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

            using(SQLiteConnection sqlCon = GetMessagesInfoSqlCon(folderOwnerUser,folder)){
                // -------- Create sequence-number to UI map table. -------------------------
                Dictionary<long,long> seqNo_to_UID = new Dictionary<long,long>();
                using(SQLiteCommand cmd = sqlCon.CreateCommand()){
                    cmd.CommandText = "select UID from MessagesInfo ORDER by UID ASC;";
                                
                    using(SQLiteDataAdapter ad = new SQLiteDataAdapter(cmd)){
                        DataSet dsUids = new DataSet();
                        ad.Fill(dsUids); 

                        for(int i=0;i<dsUids.Tables[0].Rows.Count;i++){
                            seqNo_to_UID.Add(i + 1,Convert.ToInt64(dsUids.Tables[0].Rows[i]["UID"]));
                        }
                    }                
                    //----------------------------------------------------------------------------

                    using(SQLiteCommand cmd2 = sqlCon.CreateCommand()){
                        cmd2.CommandText = "select UID from MessagesInfo where " + SearchCriteriaToSql(e.Criteria,seqNo_to_UID) + ";";

                        DataSet ds = new DataSet("dsMessagesInfo");                
                        using(SQLiteDataAdapter ad = new SQLiteDataAdapter(cmd2)){
                            ad.Fill(ds); 
                        }
                                
                        foreach(DataRow dr in ds.Tables[0].Rows){
                            e.AddMessage(Convert.ToInt64(dr["UID"]));
                        }
                    }
                }
            }
        }
Example #4
0
        public void Search(string accessingUser, string folderOwnerUser, string folder, IMAP_e_Search e)
        {
            ArgsValidator.ValidateUserName(folderOwnerUser);
            ArgsValidator.ValidateFolder(folder);
            ArgsValidator.ValidateNotNull(e);
            if (!this.UserExists(folderOwnerUser))
            {
                throw new Exception("User '" + folderOwnerUser + "' doesn't exist !");
            }
            folder = API_Utlis.NormalizeFolder(folder);
            string text = folder;
            xml_API.SharedFolderMapInfo sharedFolderMapInfo = this.MapSharedFolder(text);
            if (sharedFolderMapInfo.IsSharedFolder)
            {
                folderOwnerUser = sharedFolderMapInfo.FolderOnwer;
                folder = sharedFolderMapInfo.Folder;
                if (folderOwnerUser == "" || folder == "")
                {
                    throw new ArgumentException("Specified root folder '" + text + "' isn't accessible !");
                }
            }
            if (!this.FolderExists(folderOwnerUser + "/" + folder))
            {
                throw new Exception("Folder '" + folder + "' doesn't exist !");
            }
            if (accessingUser.ToLower() != "system")
            {
                IMAP_ACL_Flags userACL = this.GetUserACL(folderOwnerUser, folder, accessingUser);
                if ((userACL & IMAP_ACL_Flags.r) == IMAP_ACL_Flags.None)
                {
                    throw new InsufficientPermissionsException(string.Concat(new string[]
					{
						"Insufficient permissions for folder '",
						accessingUser,
						"/",
						folder,
						"' !"
					}));
                }
            }
            using (SQLiteConnection messagesInfoSqlCon = this.GetMessagesInfoSqlCon(folderOwnerUser, folder))
            {
                Dictionary<long, long> dictionary = new Dictionary<long, long>();
                using (SQLiteCommand sQLiteCommand = messagesInfoSqlCon.CreateCommand())
                {
                    sQLiteCommand.CommandText = "select UID from MessagesInfo ORDER by UID ASC;";
                    using (SQLiteDataAdapter sQLiteDataAdapter = new SQLiteDataAdapter(sQLiteCommand))
                    {
                        DataSet dataSet = new DataSet();
                        sQLiteDataAdapter.Fill(dataSet);
                        for (int i = 0; i < dataSet.Tables[0].Rows.Count; i++)
                        {
                            dictionary.Add((long)(i + 1), Convert.ToInt64(dataSet.Tables[0].Rows[i]["UID"]));
                        }
                    }
                    using (SQLiteCommand sQLiteCommand2 = messagesInfoSqlCon.CreateCommand())
                    {
                        sQLiteCommand2.CommandText = "select UID from MessagesInfo where " + this.SearchCriteriaToSql(e.Criteria, dictionary) + ";";
                        DataSet dataSet2 = new DataSet("dsMessagesInfo");
                        using (SQLiteDataAdapter sQLiteDataAdapter2 = new SQLiteDataAdapter(sQLiteCommand2))
                        {
                            sQLiteDataAdapter2.Fill(dataSet2);
                        }
                        foreach (DataRow dataRow in dataSet2.Tables[0].Rows)
                        {
                            e.AddMessage(Convert.ToInt64(dataRow["UID"]));
                        }
                    }
                }
            }
        }
Example #5
0
        private void m_pImapServer_Session_Search(object sender,IMAP_e_Search e)
        {
            try{
                IMAP_Session ses = (IMAP_Session)sender;

                m_pApi.Search(ses.AuthenticatedUserIdentity.Name,ses.AuthenticatedUserIdentity.Name,ses.SelectedFolderName,e);
            }
            catch(Exception x){
                e.Response = new IMAP_r_ServerStatus(e.Response.CommandTag,"NO","Error: " + x.Message);
            }
        }