This project is read-only.

Examples?

Jun 27, 2008 at 8:58 PM
Edited Jun 27, 2008 at 8:59 PM
Jason,

I just downloaded your library and am excited to get to using it and converting over my CodeProject ImapLibrary code.

Do you have any code examples?  In my download there is an unavailable project called SampleMailClient, but the code is not included in the project.  I am especially intrigued in the new caching abilities, but I am not sure where to start with it.

Thanks for all your great work on this!

Todd
Jun 30, 2008 at 3:22 PM
Hi Todd,

Thanks for your post, the SampleMailClient program that is defined in the solution is, as the name implies, a sample client application that will demonstrate the use of the library. However, for this to work well, the library needs to be modified to work in a more asynchronous fashion so that the UI will not be locked while waiting for an IMAP command to finish.

In light of this i will be creating a set of new asynchronous Begin<something> methods which will have corresponding <something>Completed and <something>Progressed events. The plan is to have this new functionality work in parallel to the current synchronous method, and to ensure that both methods can work simultaneously.

The technical challenge for me to create this new system is determining the best way to implement the multi-threading architecture that is required for multiple asynchronous requests to be handled at the same time.

My first thought was, upon making the first successful connection to the server, spawn several new connections to server, and using a common request queue, have each thread check for a request on the queue, and if it finds one, pull it off the queue, execute it, calling the corresponding events during and upon completion.

I would be interested in hearing if anyone paying attention to this discussion has any suggestions as to alternate ways of implementing asynchronicity, if you do, please let me know.
Jun 30, 2008 at 4:19 PM
OK. One suggestion, and one question. 

First, in IMAP.cs there is a method called GetSelectedFolderMessageIDs().  In this method it is hard coded to return a list of all messages.  It would be nice to have the option of returning a list of all messages, or unseen messages only.  For mailboxes that process a lot of messages, the unseen option would speed up processing a lot.

Now my question.  Is there a way to individually flag a message as being opened?  For example, let's say that I pull back 20 unseen messages from the inbox, and want to flag 10 of them as opened, while leaving the rest as unopened.  How would I do this?

Thanks in advance.

Todd

ctbrewski wrote:
Jason,

I just downloaded your library and am excited to get to using it and converting over my CodeProject ImapLibrary code.

Do you have any code examples?  In my download there is an unavailable project called SampleMailClient, but the code is not included in the project.  I am especially intrigued in the new caching abilities, but I am not sure where to start with it.

Thanks for all your great work on this!

Todd



Jun 30, 2008 at 8:52 PM
What i would suggest, is use the Search method in IMAPFolder. To get all the new messages in a folder (regardless of what messages have been downloaded or not) use something like this:

IMAPFolder f = client.Folders["INBOX"];
IMAPSearchResult sResult = f.Search(IMAPSearchQuery.QuickSearchNew());

The search method will perform a search directly on the server and populate a seperate List with only the messages that match the search, in this case only returning the messages that are marked as unseen. You could then iterate through the messages like this:

foreach (IMAPMessage msg in sResult.Messages)
{
         // some custom logic goes here
          msg.MarkAsRead();
}


You may notice the call to MarkAsRead(), this method currently exists in IMAP.cs, but is not directly exposed to the client API. What you can do in the meantime before my next update is add this method to IMAPMessage.cs:

/// <summary>
        /// Marks this message as \Seen on the server
        /// </summary>
        public void MarkAsRead()
        {
            if (_client == null) return;
            _client._imap.MarkMessageAsRead(this);
            _flags.New = false;
        }

Hope this helps,
Jason
Jun 30, 2008 at 10:47 PM
Edited Jun 30, 2008 at 11:59 PM
Jason,

Addendum:  After writing the message below I realized out that GetMessageIDs does NOT populate the message. The reason I was seeing this was becase I had a watch on message.subject, which triggered RefreshData() on the Get property of Subject.   The fix I suggested below also does very little to speed things up.  It simply comes down to the fact that loading 10,000 message headers takes almost 3 minutes, and that's just the way it is.  It would be nice to have the option of not pre-loading message ID's upon logon for this reason. 

-Todd

Thanks for your reply.  I will look into the MarkAsRead method.

As far as the search for unseen emails, maybe I need to clarify.  I have an inbox with over 10,000 messages in it (all have been opened previously and are marked as opened). As soon as I do an IMAPClient.Logon, all of these messages are loaded into memory, taking close to 5 minutes to complete.  In looking at the Logon method there is this code at the bottom which loads the folders, then when each folder's GetMessageIDs is called, it loads all the messages: 

            if (!UsingCache)
            {
                XmlDocument doc = new XmlDocument();
                _folders.Clear();
                _folders = _imap.ProcessFolders(_config.DefaultFolderName);
                foreach (IMAPFolder f in _folders)
                {
                    f.SetClient(this);
                    if (_config.AutoGetMsgID)
                        f.GetMessageIDs();
                }

If IMAPClient is supposed to load the folders and messages upon logon, it would be nice to be able to specify "All" or "UNSEEN", but in reality it may make sense not load on logon, and allow the developer specify what messages to pull in a later method call.

One other recommendation on speeding up an area.  In IMAPFolder in the GetMessageIDs you have this loop:

                    foreach (IMAPMessage m in _messages)
                    {
                        if (m.Uid == id)
                            found = true;
                    }

For long lists of emails, I would recommend changing it to:

                    foreach (IMAPMessage m in _messages)
                    {
                        if (m.Uid == id)
                        {
                            found = true;
                            break;
                        }
                    }

Not a HUGE improvement, but for long lists it will help.

Todd


JasonMiesionczek wrote:
What i would suggest, is use the Search method in IMAPFolder. To get all the new messages in a folder (regardless of what messages have been downloaded or not) use something like this:

IMAPFolder f = client.Folders["INBOX"];
IMAPSearchResult sResult = f.Search(IMAPSearchQuery.QuickSearchNew());

The search method will perform a search directly on the server and populate a seperate List with only the messages that match the search, in this case only returning the messages that are marked as unseen. You could then iterate through the messages like this:

foreach (IMAPMessage msg in sResult.Messages)
{
         // some custom logic goes here
          msg.MarkAsRead();
}


You may notice the call to MarkAsRead(), this method currently exists in IMAP.cs, but is not directly exposed to the client API. What you can do in the meantime before my next update is add this method to IMAPMessage.cs:

/// <summary>
        /// Marks this message as \Seen on the server
        /// </summary>
        public void MarkAsRead()
        {
            if (_client == null) return;
            _client._imap.MarkMessageAsRead(this);
            _flags.New = false;
        }

Hope this helps,
Jason



Jul 1, 2008 at 2:41 AM
In your IMAPConfig instance, if you set AutoGetMsgID = false, the msg UIDs will not be downloaded until requested.

Additionally, you can modify IMAPFolderCollection to look like this:

if (f._client.Config.AutoGetMsgID)
                            f.GetMessageIDs();

This will further ensure that the UIDs are not downloaded automatically. So this should enable you to connect to the server, select the desired folder, and search for new messages without having to download all the messge ids of that folder.

Question, without knowing the application that you are building, would it be possible to move opened messages to another folder once they've been processed? That could improve performance as well as there would only be new messages in the inbox.. just a thought.

Jason
Jul 1, 2008 at 6:59 PM
Jason

Thanks for your reply.  I made the changes and it looks like I am back to square one. When you run a search, the search returns a list of UIDS, which are then compared with the folder's message list, returning the found messages.  This means that if you don't have the folder's messages loaded, search will not return any results, even if matches are found from the server call.

By the way, if you have AutoGetMsgID = true, it will load all messages recursively through all the folders, so I couldn't use this if I moved messages into sub-folders.  You may consider changing the autoload to load the folder messages only if/when you access the folder.

The solution I will use is to change the GetSelectedFolderMessageIDs to pull UNSEEN rather than ALL messages.

One request for a future enhancement though:  Add a QuickSearchUnseen() in addition to QuickSearchNew(). I added this to my code and it returns a different result than the New does (this may have something to do with how Outlook flags messages as seen/unseen).


Thanks for all your help.  You've really improved the IMAP library.

Todd

JasonMiesionczek wrote:
In your IMAPConfig instance, if you set AutoGetMsgID = false, the msg UIDs will not be downloaded until requested.

Additionally, you can modify IMAPFolderCollection to look like this:

if (f._client.Config.AutoGetMsgID)
                            f.GetMessageIDs();

This will further ensure that the UIDs are not downloaded automatically. So this should enable you to connect to the server, select the desired folder, and search for new messages without having to download all the messge ids of that folder.

Question, without knowing the application that you are building, would it be possible to move opened messages to another folder once they've been processed? That could improve performance as well as there would only be new messages in the inbox.. just a thought.

Jason



Jul 1, 2008 at 9:10 PM
Hey Todd,

Check out my latest post to Haser's discussion, Checking for new messages. I checked-in an update to the source which incorporates specific functionality to check for new messages, without having to process the rest of the messages in the folder. So check out that post, and check out the new code and let me know if that is helpful to you.

Jason