Code Monkey home page Code Monkey logo

s22.imap's Introduction

Introduction

This repository contains an easy-to-use and well-documented .NET assembly for communicating with and receiving electronic mail from an Internet Message Access Protocol (IMAP) server.

Downloads

You can always get the latest package on Nuget (includes .NET 4.0 and 3.5 binaries) or download the binaries (targeting .NET 4.0) as a .zip archive from here. The documentation is also available for offline viewing as HTML or CHM and can be downloaded from here and here, respectively.

Usage & Examples

To use the library add the S22.Imap.dll assembly to your project references in Visual Studio. Here's a simple example that initializes a new instance of the ImapClient class and connects to Gmail's IMAP server:

using System;
using S22.Imap;

namespace Test {
	class Program {
		static void Main(string[] args) {
			// Connect on port 993 using SSL.
			using (ImapClient Client = new ImapClient("imap.gmail.com", 993, true))
			{
				Console.WriteLine("We are connected!");
			}
		}
	}
}

Here are a couple of examples of how to use the library. Please also see the documentation for further details on using the classes and methods exposed by the S22.Imap namespace.

Features

  • Supports IMAP IDLE notifications
  • Supports IMAP over SSL
  • API designed to be very easy to use
  • Allows selectively fetching parts of mail messages
  • Inherently thread-safe
  • Well documented with lots of example code
  • Robust MIME parser, tested with 100.000+ mails
  • Supports various authentication mechanisms (SCRAM-SHA-1, OAUTH2, NTLM among others)
  • Integrates well with existing System.Net.Mail infrastructure
  • Still supports .NET 3.5
  • Free to use in commercial and personal projects (MIT license)

Credits

This library is copyright © 2012-2014 Torben Könke.

Parts of this library are based on the AE.Net.Mail project (copyright © 2012 Andy Edinborough).

License

This library is released under the MIT license.

Bug reports

Please send your bug reports to [email protected] or create a new issue on the GitHub project homepage.

s22.imap's People

Contributors

nikize avatar smiley22 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

s22.imap's Issues

Efficient getallheaders method needed.

I would like to say brilliant coding and thx for the free Imap library.
Can you code a very efficient getallheaders custom Imap single command call and parse and unwrap the response and have them available in an array?
Also need the date received information to be saved also as part of header information and message information. This seems to be missing as the System.Net.Mail.MailMessage does not seem to have a property for it.
Thanks in advance.

Gmail - keeps downloading same new message every 5-10 mins

Below is the vb.net code I am using. the OnNew Message fires for the same message over an over again (randomly 5 -15 mins gap). Is there something wrong in my code?
VB .NET:

Form Load:
If mClient.Supports("IDLE") Then
AddHandler mClient.NewMessage, AddressOf mcOnNewMessage
End If

Public Sub mcOnNewMessage(ByVal sender As Object, ByVal e As IdleMessageEventArgs)

       OnNewMessage(sender, e)
End Sub

Private Delegate Sub dOnNewMessage(ByVal sender As Object, ByVal e As IdleMessageEventArgs)

Public Sub OnNewMessage(ByVal sender As Object, ByVal e As IdleMessageEventArgs)

    If Me.InvokeRequired Then

        Me.Invoke(New dOnNewMessage(AddressOf OnNewMessage), sender, e)

        Exit Sub

    End If

    Try

        Dim m As MailMessage = sender.GetMessage(e.MessageUID)

        Log("New Mail From : " & m.From.Address.ToString & "Subject: " & m.Subject.ToString)

    Catch ex As Exception

        Log(ex.Message)

    End Try

End Sub

Potential issue with NewMessage

I have a windows service that runs and waits for a new message to process. It works fine for about 20 minutes, then the NewMessage event no longer fires. Is there a timeout or something I'm missing? Do I have to dispose the client after some time and get a new instance?

//not supported, so just stop the service
if (m_Client.Supports("IDLE") == false)
this.Stop();

// We want to be informed when new messages arrive
m_Client.NewMessage += new EventHandler(OnNewMessage);

Attachments don't have filename with MONO

I suppose the source of the problem is an incorrect implementation of System.Net.Mail.Attachment in MONO because compiling with ".NET" runs ok.

This is a work around:

In MessageBuilder.cs method CreateAttachment() adding

            attachment.Name = name;
            attachment.ContentDisposition.FileName = name;

lets it go with MONO

Also I can read about the filename that "Many mail user agents also send messages with the file name in the name parameter of the content-type header instead of the filename parameter of the content-disposition header. This practice is discouraged – the file name should be specified either through just the filename parameter, or through both the filename and the name parameters." from: http://en.wikipedia.org/wiki/MIME Content-Disposition
so this is the complete fix I suggest:

        private static Attachment CreateAttachment(Bodypart part, byte[] bytes) {
            MemoryStream stream = new MemoryStream(bytes);
            // Workaround: See http://en.wikipedia.org/wiki/MIME Content-Disposition or source from http://www.imc.org/ietf-smtp/mail-archive/msg05023.html 
            string name = part.Disposition.Filename;
            if (String.IsNullOrEmpty(name))
                name = part.Parameters["name"];
            if (String.IsNullOrEmpty(name))
                name = Path.GetRandomFileName();

            Attachment attachment = new Attachment(stream, name);
            try {
                attachment.ContentId = ParseMessageId(part.Id);
            } catch {}
            try {
                attachment.ContentType = new System.Net.Mime.ContentType(
                    part.Type.ToString().ToLower() + "/" + part.Subtype.ToLower());
            } catch {
                attachment.ContentType = new System.Net.Mime.ContentType();
            }
            // Workaround: file name from constructor ignored with MONO 
            attachment.Name = name;
            attachment.ContentDisposition.FileName = name;

            return attachment;
        }

Mapping a message to a uid?

I'm using the S22 IMAP library to retrieve some messages from a Gmail account like this:

uint[] uids = Client.Search(SearchCondition.All());

MailMessage[] messages = Client.GetMessages(uids, FetchOptions.Normal);

foreach (MailMessage msg in messages)
{
// process the message here
}

After I process each message, I may or may not want to delete it becuase on the contents of the message. In trying to figure out how I'd delete a message, I found the IMAP client's DeleteMessage() method. I just assumed that I'd be able to call DeleteMessage(msg) but I see that DeleteMessage() is actually expecting a uid (uint) and not a MailMessage. Obviously I have a list of uids as a result of the call to Client.Search() but since I don't know if I want to delete a message until I process it, how do I map a MailMessage to its corresponding uid? Thanks!

Store mail message on an IMAP server

Hi there, first of all I would sincerely thank smiley22 for authoring with such good documentation regarding Imap. I am a newbie in this field, but found his work simply amazing and understandable, thanks mate!

I was working with the codes and found something which I find hard to decipher for example:

Section taken from - Store mail message on an IMAP server

// Add the attachment
Attachment attachment = new Attachment("some_image.png", "image/png");
attachment.Name = "my_attached_image.png";
message.Attachments.Add(attachment);

could you please explain what the above statements does?

Expunge and moving messages

According to the documentation it says expunge is for deleting messages marked for deletion.

It also needs to be run after moving messages between mailboxes otherwise it leave it behind. Not sure if that's design or if the doc needs to be updated.

From Displayname does not get parsed?

When retrieving a message from GMail, it seems the DisplayName in the From is not correctly parsed. The result is an empty string.

Looking in the Headers, I do see the full result in the From part. Does the DisplayName have a mapped property? :)

Thanks in advance!

UID Search error

Getting error when searching by UID:

Error S22.Imap.BadServerResponseException: xm003 BAD Could not parse command
at S22.Imap.ImapClient.Search(SearchCondition criteria, String mailbox)

Anyone having same issue?

Thanks!

Small fix for ListMailboxes()

The problem is that ListMailboxes currently expects ALL the folder name (or inbox name) between double quotes, so it drops the name without double quotes.

Match m = Regex.Match(response,
                            "\\* LIST \\((.*)\\)\\s+\"(.+)\"\\s+\"(.+)\"");
  • LIST (\Marked \HasNoChildren) "/" "Deleted Items" OK
  • LIST (\HasNoChildren) "/" Drafts SKIPPED

As a workaround, I changed the regex and added an UnquoteString() method:

Match m = Regex.Match(response,
                              "\\* LIST \\((.*)\\)\\s+\"(.+)\"\\s+(.+)");
...
        if (add)
           mailboxes.Add(Util.UnquoteString(m.Groups[3].Value));  

[Utils.cs]
internal static string UnquoteString(string str)
{
  if(!String.IsNullOrEmpty(str) && str.Length > 1 && str[0] == '"' && str[str.Length-1] == '"')
    return str.Substring(1, str.Length-2);
  else
    return str;
}

+1)
And an embarrassing response from MS Exchange Server if the inbox name contains any double-quote sign. Example name is: Hell"oh

A001 LIST "" "*"

* LIST (\Marked \HasNoChildren) "/" "Deleted Items"
* LIST (\HasNoChildren) "/" Drafts
* LIST (\HasNoChildren) "/" {7}
Hell"oh
* LIST (\Marked \HasChildren) "/" INBOX

Yep, what the hell is that {7} + linefeed before the name?! :)

Issue decoding IMAP body

I'm attempting to retrieve messages from a mail server provided by a hosting company.

The server identifies itself as below when connecting without SSL

* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE STARTTLS AUTH=PLAIN AUTH=LOGIN] Dovecot ready.

I can search messages fine but when I call IMAPClient.GetMessage(), it either returns a MailMessage with all properties at their defaults (0/Null) or throws an Exception:

S22.Imap.BadServerResponseException: Server returned erroneous body structure:
   at S22.Imap.ImapClient.GetMessage(UInt32 uid, ExaminePartDelegate callback, Boolean seen, String mailbox)
   at S22.Imap.ImapClient.GetMessage(UInt32 uid, FetchOptions options, Boolean seen, String mailbox)
   at HandleMessage(UInt32 MessageId) in [MyCode]
   at CheckMail() in [MyCode]
   at Work() in [MyCode]
   at WorkScheduler() in [MyCode]

The message in question was in a subfolder (INBOX.Username)

I've got the original source of a message which causes this exception as reported by a webmail client. I'd prefer not to post it publicly but am happy to mail it to you

Note that the same code works perfectly with GMail (Querying INBOX) over IMAP

Idle a mode after 5-10 minutes ceases to work.

Sometimes imap notifies the server that 0 new messages are received. Then when obtaining the message there is an exclusive situation, and idle a mode doesn't join thus. The same situation if there will be exceptions in work of some other functions. I suggest to add after all ResumeIdling in the handler of exclusive situations.
Excuse for bad English.

patch
http://paste.pardus-linux.org/1178

Pulling messages from GMail terribly slow

I am trying to set up a project so that I can monitor a GMail account. When I log in, and fetch my messages from my inbox (25 messages with some attachements), it is really really slow..

What am I doing wrong?

client = new ImapClient("imap.gmail.com", 993, Email, Password, AuthMethod.Login, true);

client.NewMessage += OnNewMessage;

//takes 10 seconds to get this point...
uint[] uids = client.Search(SearchCondition.All(), client.DefaultMailbox); //Takes aprox 15 seconds to get the ids..
MailMessage[] messages = client.GetMessages(uids); //Takes minutes...

IDLE does not work with dovecot because of case sensitive response

I have been testing S22.Imap with my imap account without success.

After code exploring and wiretapping I found that my server (dovecot 2.0.9) responds

xm098 IDLE

  • idling
    DONE
    xm098 OK Idle completed.

and IdleLoop() is awaiting for "OK IDLE" in a case sensitive fashion. So when some method calls StopIdling() or PauseIdling() "DONE" is sent, but idleThread never ends falling in a deadlock.

Replacing (ImapClient.cs:~1904)
if (response.Contains("OK IDLE"))
with
if (response.ToUpper().Contains("OK IDLE"))
resolves this issue.

Documentation says about Contains that "This method performs an ordinal (case-sensitive and culture-insensitive) comparison. The search..."

Smiley22 will give us the best solution, will not?

Usefull and lightweight implementation. Congratulations.

IDLE not working with Exchange 2010

Hi, I have implemented the example code for receiving IDLE notifications. Works great with GMail but on my Exchange 2010 server, the NewMessage event is never called. Client.Supports("IDLE") returns true. What am I doing wrong?

GetMEssage throwing exception about invalid character

When I try to retrieve emails using:

MailMessage eMail = Client.GetMessage(uid, false);

I get an exception thrown:

System.FormatException was caught
Message=An invalid character was found in the mail header: ';'.
Source=System
StackTrace:
at System.Net.Mail.DotAtomReader.ReadReverse(String data, Int32 index)
at System.Net.Mail.MailAddressParser.ParseDomain(String data, Int32& index)
at System.Net.Mail.MailAddressParser.ParseAddress(String data, Boolean expectMultipleAddresses, Int32& index)
at System.Net.Mail.MailAddressParser.ParseMultipleAddresses(String data)
at System.Net.Mail.MailAddressCollection.ParseValue(String addresses)
at System.Net.Mail.MailAddressCollection.Add(String addresses)
at S22.Imap.MessageBuilder.ParseAddressList(String list)
at S22.Imap.MessageBuilder.SetAddressFields(MailMessage m, NameValueCollection header)
at S22.Imap.MessageBuilder.FromHeader(String text)
at S22.Imap.MessageBuilder.FromMIME822(String text)
at S22.Imap.ImapClient.GetMessage(UInt32 uid, FetchOptions options, Boolean seen, String mailbox)
at S22.Imap.ImapClient.GetMessage(UInt32 uid, Boolean seen, String mailbox)
at MCEUserAdd.MCEUserAddForm.ProcessEMails()

Any idea what's going on and how to fix it?

MS Exchange Server may returns UID in new line. Eg: " UID 12)" - with WA

Issue: MS Exchange Server may returns UID in new line. Eg: " UID 12)"

Workaround is replacing condition in GetMailHeader(), GetBodypart(), GetMessageData():

- if ((response = GetResponse()) != ")")

To:

+ response = GetResponse();
+ if (response != ")" && response != string.Format(" UID {0})",uid))

how to get only x messages?

is there a way to get only a specific number of messages, like from 1 to 10, from 11 to 20 etc. using the search condition?

thanks

German Umlauts not working

I updated S22.Imap in my project after several months to the current build (16/03/13) in GIT and noticed that German umlauts don't seem to work anymore. When I create a MailMessage using

MailMessage message = imap.GetMessage(uid);

(when iterating over a list of unread messages), the Umlauts in message.Body are replaced by "?" signs.

Interestingly enough, the BodyEncoding seems to be detected correctly. If the message is sent using ISO-8859-1 I see System.Text.Latin1Encoding being set, or System.Text.UTF8Encoding if the message is sent in this format. But my Text (e.g. the word "Österreich" comes out as "?sterreich" for ISO-8859-1 or "??sterreich" for UTF-8.

I'm pretty sure, this used to work my previously used version (which was the one from nearly 7 months ago). Do I need to set something here or is it simply broken in the current version?

Thanks for your help,
Wolfgang

IsResponseOK() method should handle "statistical" responses? +WA for StoreMessage()

Issue: IsResponseOK() method should handle "statistical" responses like "* 1 RECENT" or "* 10 EXIST"
As the RFC says:
5.2. Mailbox Size and Message Status Updates
At any time, a server can send data that the client did not request.
Sometimes, such behavior is REQUIRED. For example, agents other than
the server MAY add messages to the mailbox (e.g., new message
delivery), change the flags of the messages in the mailbox (e.g.,
simultaneous access to the same mailbox by multiple agents), or even
remove messages from the mailbox. A server MUST send mailbox size
updates automatically if a mailbox size change is observed during the
processing of a command. A server SHOULD send message flag updates
automatically, without requiring the client to request such updates
explicitly.

Example: Currently StoreMessage() returns with exception, because MS Exchange Server responses with statistical rows (eg.: "* 1 RECENT" and "* 10 EXIST") before sending APPEND OK.

The Workaround in StoreMessage() is skipping these rows before calling IsResponseOK :

while (response.StartsWith("* ")) response = GetResponse(); //TODO: improve IsResponseOK()?

Cannot make idle work

Downloaded the most recent code today. Compiled and have been testing it. I can login to my IMAP server. And issue commands such as ListMailboxes() GetMailBoxInfo(), etc. All seems OK.

Supports("IDLE") returns true.

But using the sample code on the documentation page for the ImapClient.NewMessage Event I do not receive any NewMessage events.

Question: Don't I have to put the server into Idle mode before I can receive IMAP IDLE events? I didn't see anything in the docs or sample code to enter IDLE mode.

What am I missing?

MessageBuilder.cs - ParseMIMEField(string field)

Problem with parsing in ParseMIMEField(string field), it cuts the values at the first space character.

Example:
If parameter (string field) is:

attachment; filename="This is.xls"; size=18092; creation-date="Tue, 15 May 2012 08:06:29 GMT";  modification-date="Tue, 15 May 2012 08:06:29 GMT"

then the method returns with cut values:

filename = This
size = 18092
creation-date = Tue
modification-date = Tue

It may causes wrong filename for attachments.

SearchCondition.From("[email protected]") not working for me

Hi there,

i'm trying to fetch some messages with that method but i get always a null uint[].

using (ImapClient Client = new ImapClient(txtServer.Text, 993, txtUser.Text, txtPass.Text, AuthMethod.Login, true))
{
// This returns ALL messages in the inbox
uint[] uids = Client.Search(SearchCondition.From(txtFrom.Text));
Array.Reverse(uids);

            MailMessage[] messages = Client.GetMessages(uids, FetchOptions.HeadersOnly);

            txtMails.Text = "";
            foreach (MailMessage email in messages)
            {
                Attachment emailSender = Attachment.CreateAttachmentFromString("", email.From.DisplayName);
                txtMails.Text += emailSender.Name + " - " + email.From.Address + " - " +email.Subject + Environment.NewLine;

            }



            MessageBox.Show("The end");
        }          

can you please help me ???

Strange decoding of body text

I use email to send XML messages between systems that are not publicly accessible. When viewing the body text of such a message, I get what looks like HEX codes mixed with the string (see substring below)

<rm xmlns=3D"http://www.r

This should simple be <rm xmlns="http://www.r

Do yo have any ideas about how to work around this issue?

Unit Testing in VS2010 Fails

Don't Know if VS 2010 is related.
I changed Tools version in csproj from 4.5 to 4.0 to open it in VS 2010.
And run the tests.
First I would just like to know if this is a VS2010 issue or not.

MailMessageTest.MessageWithAttachmentToMIME822 fails. There is no key with name "name" in part.Parameters and that causes a Exception: System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary

Also MessageWithQuotedPrintables gets wrong result, the m.body returns the encoded variant. and not the decoded.

I have some issues that might be related to the last case, and I would like to add testcases to the unit tests. (The framework should be extended so it is easy to add.)

It won't even compile

I'm trying to use S22.Imap to connect to Yahoo IMAP (the famous GUID issue).

I can't even get it to compile.

The 1st line:
uint[] uids = Client.Search(SearchCondition.All, null);

gives an error

Error 2 Argument 1: cannot convert from 'method group' to 'S22.Imap.SearchCondition'

DecodeWords data not handled correctly in some cases

I have some data not being decoded as expected: example syntax: {orginal, expected}

{ "Information =?ISO-8859-1?Q?f=F6r?= dig",
    "Information för dig" },
{ "faktura =?ISO-8859-1?Q?F14072-=F6stersund=2Epdf?=",
    "faktura F14072-östersund.pdf" },

The first source is:
Subject: Information =?ISO-8859-1?Q?f=F6r?= dig

And the second has a attachment mime header of

Content-Type: application/pdf;
 name="faktura =?ISO-8859-1?Q?F14072-=F6stersund=2Epdf?="
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
 filename*0*=ISO-8859-1''%66%61%6B%74%75%72%61%20%46%31%34%30%37%32%2D%F6;
 filename*1*=%73%74%65%72%73%75%6E%64%2E%70%64%66

(see http://kb.mozillazine.org/Attachments_renamed for other example with filename_0_= syntax)

Working on test cases and hopefully also a fix.

Util.DecodeWords need to add both non encoded and encoded data to the same result, and not just the regexp matched values. Trying to deciper http://tools.ietf.org/html/rfc2047#section-5 right now.

Exceptions from ImapClient.GetMessages()

I just started using this library and I'm getting some exceptions using the code below to call GetMessages():

using (ImapClient Client = new ImapClient("imap.gmail.com", 993, true))
{
try
{
Client.Login("[email protected]", "password", AuthMethod.Login);
}
catch (InvalidCredentialsException)
{
Debug.WriteLine("The server rejected the supplied credentials");
return;
}

uint[] uids = Client.Search(SearchCondition.All());

MailMessage[] messages = Client.GetMessages(uids);

foreach(MailMessage msg in messages)
{
    Debug.WriteLine(msg.Subject);
}

Client.Logout();

}

If I step through this code in the debugger, as soon as I execute the following line:

MailMessage[] messages = Client.GetMessages(uids);

I see exceptions in the debug output window like this:

A first chance exception of type 'System.ArgumentNullException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in S22.Imap.dll
A first chance exception of type 'System.ArgumentNullException' occurred in mscorlib.dll
A first chance exception of type 'System.ArgumentNullException' occurred in System.dll
A first chance exception of type 'System.ArgumentNullException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in S22.Imap.dll
A first chance exception of type 'System.ArgumentNullException' occurred in mscorlib.dll
A first chance exception of type 'System.ArgumentNullException' occurred in System.dll
A first chance exception of type 'System.ArgumentNullException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in S22.Imap.dll
A first chance exception of type 'System.ArgumentNullException' occurred in mscorlib.dll
A first chance exception of type 'System.ArgumentNullException' occurred in System.dll
A first chance exception of type 'System.ArgumentNullException' occurred in mscorlib.dll
A first chance exception of type 'System.ArgumentNullException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in S22.Imap.dll
A first chance exception of type 'System.ArgumentNullException' occurred in System.dll

This is a brand new gmail account so the inbox only contains the default new messages from Google so my loop through the messages and printing msg.Subject produces this:

Get Gmail on your mobile phone
Import your contacts and old email
Customize Gmail with colors and themes
Getting started on Google+

I tried changing my SeachCondition from .All to .Unseen and then sent a new message to the account that I'm checking. I waited about 30 seconds, then executed this code again and still got exceptions following the call to .GetMessages():

A first chance exception of type 'System.ArgumentNullException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in S22.Imap.dll
A first chance exception of type 'System.FormatException' occurred in mscorlib.dll
A first chance exception of type 'System.FormatException' occurred in S22.Imap.dll
A first chance exception of type 'System.ArgumentNullException' occurred in System.dll

Has anyone else seen this behavior before? My example code is basically identical to one of the examples found here:

https://github.com/smiley22/S22.Imap/blob/master/Examples.md#2

Getting messages with no subject

I recently gave your Imap lib a shot since I'm experimenting with the IMAP IDLE feature and yours seems to work so much better. Thanks for the efforts!

I just seem to run into trouble when I receive emails without a subject. When I call GetMessage with the according uid it internally calls MailMessage message = MessageBuilder.FromHeader(header); to create the message object.
FromHeader on the other hand does a regex search on the subject - Match ma = Regex.Match(header["Subject"], @"=?([A-Za-z0-9-]+)"); which fails with an ArgumentNullException if there is no subject since header["Subject"] is null (key doesn't exist).

Regards,
Wolfgang

IDLE with Exchange 2007

I had trouble getting IDLE to work properly with my Exchange 2007 server. Whenever new messages arrived, e.MessageUID would always refer to the last email that was received before entering IDLE(before adding the NewMessage event handler). It didn't matter if I received 1 or 10 messages while in IDLE.

Doing some testing with Telnet, I noticed that if I ended IDLE "mode" and immediately requested STATUS INBOX (UIDNEXT), it always returned the same value as before entering IDLE. However, if I requested a SELECT INBOX before requesting the status, then I got the correct values. Looking in code, in SelectMailbox, you specifically do not re-select the mailbox, if it is already selected. I commented this out, and it is working properly with Exchange.

I haven't read the RFCs, but I suspect that the only drawback of re-selecting the same mailbox is the time and bandwidth for the request; if so, it may not be necessary to keep that "optimization". By far, your implementation, though, has been much less problematic than trying to use AE, which had a buggy ReadLine implementation(among other things).

IMAP IDLE in Windows Form [FIXED]

Hey man! First off I got to say thanks for providing an easy, free imap library!

I've been working with your IDLE code, and i got it to work just fine in a console application (which you only gave an example for a console app). But when i try it in a windows form app, it doesn't seem to work. Could you provide and example that uses IDLE in a windows form app? Thanks!

THIS ISSUE IS FIXED!

Bug searching mail by header value?

Hi, I'm a first time user of this lib, so forgive me if I'm missing something (and it might be something specific to my imap server?), but as far as I can see there's a bug when searching by header value. e.g:

var uids = imapClient.Search(SearchCondition.Header("Message-ID", "whatever"));

throws BadServerResponseException - "xml003 BAD Missing required argument to Search header"

The actual imap command being sent to the server in this case is:

xm003 UID SEARCH HEADER "Message-ID \"whatever\""

I had a look at the imap spec and that outer set of quotes looked unnecessary, so I changed the SearchCondition.Header(string, string) method to return a condition object with Quote=false, instead of the default Quote=true. That resulted in a command like this:

xm003 UID SEARCH HEADER Message-ID \"whatever\"

Which resolved the problem. Is that a bug? Or am I missing something?

Change made to SearchCondition.cs:

public static SearchCondition Header(string name, string text) {
    return new SearchCondition() { 
        Field = Fields.Header,
        Value = name + " " + text.QuoteString(),
        Quote = false, // inserted this line
    };
}

SelectMailbox() and ResumeIdling() deadlock

I just started to test the event NewMessage and it works perfect until I do my job in the INBOX folder.

Meanwhile idling, you start acting with other mailbox/folder it causes some trouble and server refuses response (that makes exception) due to repeated IMAP command caused by a deadlock in SelectMailbox-PauseIdling-ResumeIdling triangle.

Eg: when call client.Search(SearchCondition.All(), "Draft")

IMAP> A008 IDLE
+ IDLE accepted, awaiting DONE command.
IMAP> DONE
A008 OK IDLE completed.
DEBUG: SelectMailbox("Drafts") // current selectedMailbox="INBOX"
IMAP> A009 SELECT "Drafts"
* 3 EXISTS
* 0 RECENT
* FLAGS (\Seen \Answered \Flagged \Deleted \Draft $MDNSent)
* OK [PERMANENTFLAGS (\Seen \Answered \Flagged \Deleted \Draft $MDNSent)] Permanent flags
* OK [UIDVALIDITY 27910] UIDVALIDITY value
* OK [UIDNEXT 4138] The next unique identifier value
A009 OK [READ-WRITE] SELECT completed.
IMAP> A010 UID SEARCH ALL
* SEARCH 3704 3720 3840
A010 OK SEARCH completed.
DEBUG: SelectMailbox("null") // current selectedMailbox="Drafts"
IMAP> DONE
IMAP> A011 SELECT "INBOX"
DONE BAD Command Error. 12
DEBUG: SelectMailbox("null") // current selectedMailbox="Drafts"
IMAP> DONE
IMAP> A012 SELECT "INBOX"
* 1282 EXISTS
* 0 RECENT
* FLAGS (\Seen \Answered \Flagged \Deleted \Draft $MDNSent)
* OK [PERMANENTFLAGS (\Seen \Answered \Flagged \Deleted \Draft $MDNSent)] Permanent flags
* OK [UNSEEN 1279] Is the first unseen message
* OK [UIDVALIDITY 27889] UIDVALIDITY value
* OK [UIDNEXT 72775] The next unique identifier value
A011 OK [READ-WRITE] SELECT completed.
DEBUG: SelectMailbox("null") // current selectedMailbox="Drafts"
IMAP> DONE
IMAP> A013 SELECT "INBOX"
DONE BAD Command Error. 12
DEBUG: SelectMailbox("null") // current selectedMailbox="Drafts"
IMAP> DONE
IMAP> A014 SELECT "INBOX"

... and an exception about server closed the connection:

System.IO.IOException: Unable to write data to the transport connection: An established connection was aborted by the software in your host machine. ---> System.Net.Sockets.SocketException: An established connection was aborted by the software in your host machine
at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
at System.Net.Sockets.NetworkStream.Write(Byte[] buffer, Int32 offset, Int32 size)
--- End of inner exception stack trace ---
at System.Net.Sockets.NetworkStream.Write(Byte[] buffer, Int32 offset, Int32 size)
at System.Net.Security._SslStream.StartWriting(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security._SslStream.ProcessWrite(Byte[] buffer, Int32 offset, Int32 count, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.Write(Byte[] buffer, Int32 offset, Int32 count)
at S22.Imap.ImapClient.SendCommand(String command) in d:\smiley22-S22.Imap\ImapClient.cs:line 318
at S22.Imap.ImapClient.SendCommandGetResponse(String command) in d:\smiley22-S22.Imap\ImapClient.cs:line 333
at S22.Imap.ImapClient.SelectMailbox(String mailbox) in d:\smiley22-S22.Imap\ImapClient.cs:line 529
at S22.Imap.ImapClient.ResumeIdling() in d:\smiley22-S22.Imap\ImapClient.cs:line 1659
at S22.Imap.ImapClient.SelectMailbox(String mailbox) in d:\smiley22-S22.Imap\ImapClient.cs:line 536
at S22.Imap.ImapClient.ResumeIdling() in d:\smiley22-S22.Imap\ImapClient.cs:line 1659
at S22.Imap.ImapClient.SelectMailbox(String mailbox) in d:\smiley22-S22.Imap\ImapClient.cs:line 536
at S22.Imap.ImapClient.ResumeIdling() in d:\smiley22-S22.Imap\ImapClient.cs:line 1659
at S22.Imap.ImapClient.SelectMailbox(String mailbox) in d:\smiley22-S22.Imap\ImapClient.cs:line 536
at S22.Imap.ImapClient.ResumeIdling() in d:\smiley22-S22.Imap\ImapClient.cs:line 1659
at S22.Imap.ImapClient.Search(SearchCondition criteria, String mailbox) in d:\smiley22-S22.Imap\ImapClient.cs:line 727

No workaround this time.... It's weekend! :)

Logout before disposing is nice act, and other stuffs

Sorry, it's me again! It seems this code will win to replace currently used buggy InterIMAP library, so if you don't mind, I will make suggestions as well... :)

// I suggest to add Logout in Dispose() - Lazy coders and especially the Mail Servers will love it :)
public void Dispose() {
  Logout(true);
...
}

// Avoid throwing exception in Dispose, so introduce the "silent" mode:
public void Logout(bool silent=false) {
  if (!Authed)
    return;
  lock (sequenceLock) {
    try {
      StopIdling();
      string tag = GetTag();
      string bye = SendCommandGetResponse(tag + "LOGOUT");
      if (!bye.StartsWith("* BYE"))
        throw new BadServerResponseException(bye);
      string response = GetResponse();
      if (!IsResponseOK(response, tag))
        throw new BadServerResponseException(response);
    }
    catch (Exception e) {
      if (!silent) throw e;
    };
    Authed = false;
  }
}

+1) Avoid "overruns" GetTag with adding modulo 1000

private string GetTag() {
  Interlocked.Increment(ref tag);
  return string.Format("A{0:000} ", tag % 1000);
}

+2 ) debugIMAP. I just added to SendCommand() and GetResponse() to help (us).

private bool debugIMAP = true; // it may can be a property, ZNAGY-2012-10-01

private void SendCommand(string command) {
  System.Diagnostics.Debug.WriteLineIf(debugIMAP, "IMAP> "+command); //ZNAGY-2012-10-01
  byte[] bytes = Encoding.ASCII.GetBytes(command + "\r\n");
  lock (writeLock) {
    stream.Write(bytes, 0, bytes.Length);
  }
}

private string GetResponse() {
  const int Newline = 10, CarriageReturn = 13;
  using (var mem = new MemoryStream()) {
    lock (readLock) {
      while (true) {
        byte b = (byte)stream.ReadByte();
        if (b == CarriageReturn)
          continue;
        if (b == Newline) {
          string res = Encoding.ASCII.GetString(mem.ToArray());
          System.Diagnostics.Debug.WriteLineIf(debugIMAP, res); // ZNAGY-2012-10-01
          return res;
        } else
          mem.WriteByte(b);
      }
    }
  }
}

+3) IndleTime property can be useful. (But it doesn't really works on MS Exchange now...I will check it later)

private System.Timers.Timer noopTimer = new System.Timers.Timer(1000 * 60 * 10);
/// <summary>
/// Interval time to check new emails [sec]. Default: 600 sec
/// </summary>
public uint IdleTime { get{ return (uint)noopTimer.Interval / 1000; } set{ noopTimer.Interval = value * 1000; } }

And remove set Interval from StartIdling()

  • noopTimer.Interval = 1000 * 60 * 10;

Mail date IMAP

Hey,

Is it possible to get the date of one received Mail? I'm just getting a lof of info like Subject,body,cc,etc but the mail date i'm not founding..

Deadlock in ListMailboxes() - with fix :)

Hi,

On the first look, it seems you made a great job with this component. (I've already saw some terrible tries..)
I'm going to build a small app to test it with Exchange Server, and if you don't mind I'll report bugs if I were found one.

Actually it seems I've found an untested part in ImapClien.ListMailboxes method:

    public string[] ListMailboxes() {
     ...
    while (response.StartsWith("*")) 
    {
      Match m = Regex.Match(response, "\\* LIST \\((.*)\\)\\s+\"(.+)\"\\s+\"(.+)\"");
      if (!m.Success)
          continue;
      ...
      response = GetResponse();
    }
    ...
    }

The "if (!m.Success) continue;" part makes deadlock, since it never reach the (next) GetResponse();

In my case, the 1st row of the response was "* LIST (\HasNoChildren) "/" Calendar" that makes it run to "continue".

I've simply change the code and it's ok now:

    while (response.StartsWith("*")) 
    {
      Match m = Regex.Match(response, "\\* LIST \\((.*)\\)\\s+\"(.+)\"\\s+\"(.+)\"");
      if (m.Success)
      {
      ...
      }
      response = GetResponse();
    }

Regards,
Zoltán

Potential race condition in IdleLoop

Working on #2, I suspect there may be another race condition in IdleLoop which can happen when WaitForResponse returns and a context switch occurs before IdleLoop has a chance to pause IDLE mode. If another thread subsequently calls PauseIdle, it will deadlock.

I'll try to come up with a fix for that.

Trying to use the library

Hi Smiley22,

First of all, thanks for your great work!! I'm trying to use the library to manage individual messages and I've found some problems on it. For example, I'm trying to move a message from a mailbox to another and then extract the UID of the message in the target mailbox:

  • How can I access to the last message of a Mailbox?
  • How can I get the UID or Date of a message?

Thank you very much!

Regards

An invalid character was found in header value

hi,
I got a error message at:
try
{
mail = imapS22.GetMessage(uid, FetchOptions.HeadersOnly, false, null);
}
catch(Exception ex) {
MessageBox.Show(ex.ToString());
return;
}
please help...

Name of attached file as quoted-printable

Filename could be encoded as QP, same way as Subject. I think that it should be decoded in BodypartFromMIME().

Example:

------=_NextPart_000_0010_01CDF1AB.47D55F80
Content-Type: text/plain;
    name="=?koi8-r?B?1MXT1C50eHQ=?="
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
    filename="=?koi8-r?B?1MXT1C50eHQ=?="


------=_NextPart_000_0010_01CDF1AB.47D55F80--

yahoo mails

I tried using your code to retrieve mails from yahoo.com but am getting :
SocketException Unhandled, which usually happens when the entered ID or passwd is wrong.

using (ImapClient Client = new ImapClient("imap.mail.yahoo.in", 993,
"user", "passwd", AuthMethod.Login, true))

could you please find a solution for it?

thanks!

How to delete message from GMail correctly?

I try to delete message from gmail, but in the next get messages session deleted message returns with flag MessageFlag.Seen and Mailbox=[Gmail]/Sent.

This is my code:
client.Login(mailAccount.Email, mailAccount.Password, AuthMethod.Login);

                var messageFlags = client.GetMessageFlags(mailId, mailbox).ToList();
                if (!messageFlags.Contains(MessageFlag.Deleted))
                    client.AddMessageFlags(mailId, mailbox, MessageFlag.Deleted);
                client.DeleteMessage(mailId, mailbox);
                client.Logout();

OutOfMemory Exception

Using this lib in my app for idling gmail account.
It throws an oom exception after a few hours of running.

Unhandled Exception

I'm using the Imap client to notify me when I get a new message in gmail.

The problem I'm having is, that after a number of hours the remoter server closes the connection.
This becomes apparent in s22.imap.getresponse byte b = (byte)stream.ReadByte();

I get an unhadled exception. The message is, the connection forcibly closed by remote host.

Wrong Body Encoding due to case sensitive Bodyformat.Paramters (fixed).

New GetMessage() method works fast! Unfortunately it makes the body-encoding wrong.

internal static void AddBodypart(this MailMessage message, Bodypart part, string content) {
Encoding encoding = part.Parameters.ContainsKey("Charset") ?
Util.GetEncoding(part.Parameters["Charset"]) : Encoding.ASCII;

Now the Body Encoding is always ASCII, because the key is now "charset" (begins with small "c").
As I saw it in the code, the key/value pair are parsed from raw IMAP's response by BodypartFromMIME() method. So actually it's ok.

This example shows that, these Bodypart.Parameters should not be case sensitive.

Simply makes the property Bodypart.Parameters to be IgnoreCase type in the constructor of class Bodypart:

Parameters = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);

IMAP IDLE - Problems with multiple new messages

I tried some "stress testing" with the IMAP IDLE mode and ran into some troubles. The Code of my delegate looks like this:

private void OnNewMessage(object sender, IdleMessageEventArgs e)
{
    uint uid = e.MessageUID;
    MailMessage message = imap.GetMessage(uid);
    ParseMessage(message); // Do something with the message here
    imap.SetMessageFlags(uid, imap.DefaultMailbox, MessageFlag.Seen);
    imap.MoveMessage(uid, "ProcessedSMS", imap.DefaultMailbox);
    imap.Expunge(imap.DefaultMailbox);
}

When I send multiple email messages at the same time (4-5 is enough) I almost everytime end up with a BadServerResponse exception, thrown by GetMessage or other internal methods. It happens in

if (!IsResponseOK(GetResponse(), tag)) 

even though the response often states success. The key issue seems to be that the tag is different from the one in the response. I can only speculate, that when multiple messages are received, there's some sort concurrency issue? Maybe you can give it a try and see if you can reproduce it.

Thanks! :)

Getting eMails with empty message on Search

When I login to my yahoo imap server, I have 2 emails in my box, one of the a passphrease I'm looking for. So I do this

uint[] uids = Client.Search(SearchCondition.Body(PAYMENT_PASSPHRASE));
MailMessage[] messages = Client.GetMessages(uids);

What I get are 33 messages and 29 of them empty (everything is null or blank).

anyone have any idea what's going on?

e.g. here is what I see:
Sender = null

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.