Thanks for fixing the connection side of things, now that is working I started using the dll to examine my Gmail account and hit a few issues.
The first is that Gmail seems to use different return strings than the ones you are expecting.
FetchNotOk is "BAD FETCH", and FetchComplete is "OK Success".
The other problem is that if you call Connection.Read() when there is nothing to receive it'll hang.
I have put in fixes for these things into ImapCommand.cs, I'll just post teh functions I have changed since the file is quite big.
1) I have added a patOkSuccess const and test against it as well as patFetchComplete, this works but the regular expression may not be the best one to use.
2) Added BAD to the expression for patFetchNotOk.
3) Changed ParseMessages() not to need to call Connection.Read() at the end of the do.while().
I changed this function quite a bit because I couldn't quite get my head around it the way it was, the only important bits are the test for patOkSuccess and removing the call to Connection.Read().
I like what you did with the parse messages. I think I am gonna implement it except that I found that some servers send more than one line per message part. The issue is if the string is too long the break it into two lines so I will have to modify it and test otherwise i think what you have is a bit more readable and easier to understand.
Keith
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Vincent,
I got the new code into source but havent created the new library I am hoping to have enough time to do a list command in the next couple days before I do the next release. Anyway I wanted to see if you could test it out. I think what I've got should work well.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi,
Thanks for fixing the connection side of things, now that is working I started using the dll to examine my Gmail account and hit a few issues.
The first is that Gmail seems to use different return strings than the ones you are expecting.
FetchNotOk is "BAD FETCH", and FetchComplete is "OK Success".
The other problem is that if you call Connection.Read() when there is nothing to receive it'll hang.
I have put in fixes for these things into ImapCommand.cs, I'll just post teh functions I have changed since the file is quite big.
1) I have added a patOkSuccess const and test against it as well as patFetchComplete, this works but the regular expression may not be the best one to use.
2) Added BAD to the expression for patFetchNotOk.
3) Changed ParseMessages() not to need to call Connection.Read() at the end of the do.while().
I changed this function quite a bit because I couldn't quite get my head around it the way it was, the only important bits are the test for patOkSuccess and removing the call to Connection.Read().
Hope this is of use to you, Vin
#region private variables
const string patFetchComplete = @"^kw\d+\WOK\W([Ff][Ee][Tt][Cc][Hh]\W|)[Cc][Oo][Mm][Pp][Ll][Ee][Tt][Ee]";
const string patOkSuccess = @"kw\d+\WOK [Ss][Uu][Cc][Cc][Ee][Ss][Ss]$";
const string patFetchNotOk = @"^kw\d+\W(NO|BAD)";
ImapConnect _connection;
#endregion
...
public void Expunge()
{
if (!(Connection.ConnectionState == ConnectionState.Open))
NoOpenConnection();
Connection.Write("EXPUNGE\r\n");
string response;
do
{
response = Connection.Read();
} while (!(response.EndsWith("))") || Regex.IsMatch(response, patFetchComplete) || Regex.IsMatch(response, patFetchNotOk) ||
Regex.IsMatch(response, patOkSuccess)));
}
...
private void ParseMessages(ref ImapMailbox Mailbox)
{
List<string> responses = new List<string>();
string response = string.Empty;
if (Mailbox.Messages == null)
Mailbox.Messages = new List<ImapMailboxMessage>();
do
{
response = Connection.Read();
responses.Add(response);
} while (!(response.EndsWith("))") || Regex.IsMatch(response, patFetchComplete) || Regex.IsMatch(response, patFetchNotOk) ||
Regex.IsMatch(response, patOkSuccess)));
foreach (string currentResponse in responses)
{
response = currentResponse;
if (!response.StartsWith("*")) continue;
ImapMailboxMessage Message = new ImapMailboxMessage();
Message.Flags = new ImapMessageFlags();
Message.Addresses = new ImapAddressCollection();
Match match;
if ((match = Regex.Match(response, @"\* (\d*)")).Success)
Message.ID = Convert.ToInt32(match.Groups[1].ToString());
if ((match = Regex.Match(response, @"\(FLAGS \(([^\)]*)\)")).Success)
Message.Flags.ParseFlags(match.Groups[1].ToString());
if ((match = Regex.Match(response, @"INTERNALDATE ""([^""]+)""")).Success)
Message.Received = DateTime.Parse(match.Groups[1].ToString());
if ((match = Regex.Match(response, @"RFC822.SIZE (\d+)")).Success)
Message.Size = Convert.ToInt32(match.Groups[1].ToString());
if ((match = Regex.Match(response, @"ENVELOPE")).Success)
response = response.Remove(0, match.Index + match.Length);
if ((match = Regex.Match(response, @"\(""(?:\w{3}\, )?([^""]+)""")).Success)
{
Match subMatch;
subMatch = Regex.Match(match.Groups[1].ToString(), @"([\-\+]\d{4}.*|NIL.*)"); //(-\d{4}|-\d{4}[^""]+|NIL)
DateTime d;
DateTime.TryParse(match.Groups[1].ToString().Remove(subMatch.Index), out d);
Message.Sent = d;
Message.TimeZone = subMatch.Groups[1].ToString();
response = response.Remove(0, match.Index + match.Length);
}
string TOKEN;
int TOKEN_OFFSET = 0;
if (response.Contains("(("))
TOKEN = "((";
else
{
TOKEN = "\" NIL";
TOKEN_OFFSET = 2;
}
Message.Subject = response.Substring(0, response.IndexOf(TOKEN) + TOKEN_OFFSET).Trim();
if (Message.Subject == "NIL")
Message.Subject = null;
else if ((match = Regex.Match(Message.Subject, "^\"(.*)\"$")).Success)
Message.Subject = match.Groups[1].ToString();
Message.Subject = ImapDecode.Decode(Message.Subject);
response = response.Remove(0, response.Substring(0, response.IndexOf("((")).Length);
//if ((match = Regex.Match(response, @"(""[^""]*"" \(\(|NIL)")).Success)
//{
// Message.Subject = match.Groups[1].ToString();
// if (Message.Subject == "NIL")
// Message.Subject = null;
// else if (Message.Subject.StartsWith("\""))
// Message.Subject = Message.Subject.Substring(1, Message.Subject.Length -5);
// response = response.Remove(0, match.Index + match.Length - 3);
//}
if ((match = Regex.Match(response, @"""<([^>]+)>""\)\)")).Success)
{
Message.MessageID = match.Groups[1].ToString();
response = response.Remove(match.Index).Trim();
}
if (response.EndsWith("NIL"))
response = response.Remove(response.Length - 3);
else {
match = Regex.Match(response, @"""<([^>]+)>""");
Message.Reference = match.Groups[1].ToString();
}
try
{
Message.Addresses = Message.Addresses.ParseAddresses(response);
}
catch (Exception ex)
{
Message.Errors = response + ex.ToString();
}
Mailbox.Messages.Add(Message);
//response = string.Empty;
//do
//{
// response += Connection.Read();
//} while (!(response.EndsWith("))") || Regex.IsMatch(response, patFetchComplete) || Regex.IsMatch(response, patFetchNotOk)));
//match = Regex.Match(response, @"\(FLAGS \(([\w\\]+)\) INTERNALDATE ""([^""]+)"" RFC822\.SIZE (\d+) ENVELOPE \(""([^""]+)"" ""([^""]+)"" \(\(NIL NIL ""([^""]+""\)\)");
}
}
...
private string ParseBodyPart(Imap.BodyPartEncoding encoding, Encoding en)
{
string response;
StringBuilder sb = new StringBuilder("");
do
{
response = Connection.Read();
if (Regex.IsMatch(response, patFetchComplete) || Regex.IsMatch(response, patOkSuccess))
break;
if (encoding == Imap.BodyPartEncoding.BASE64)
sb.Append(response);
else if (encoding == Imap.BodyPartEncoding.QUOTEDPRINTABLE)
if (response.EndsWith("=") || response.EndsWith(")"))
sb.Append(response.Substring(0, response.Length - 1));
else
sb.AppendLine(response);
else
sb.AppendLine(response);
} while (true);
//} while (!(response.EndsWith("==") || response == ")"));
if (sb.ToString().Trim().EndsWith(")"))
sb = sb.Remove(sb.ToString().LastIndexOf(")"), 1);
if (encoding != BodyPartEncoding.BASE64)
return ImapDecode.Decode(sb.ToString(), en);
return sb.ToString();
}
I like what you did with the parse messages. I think I am gonna implement it except that I found that some servers send more than one line per message part. The issue is if the string is too long the break it into two lines so I will have to modify it and test otherwise i think what you have is a bit more readable and easier to understand.
Keith
Vincent,
I got the new code into source but havent created the new library I am hoping to have enough time to do a list command in the next couple days before I do the next release. Anyway I wanted to see if you could test it out. I think what I've got should work well.
Hi,
Got the tar ball down and gave it a run through against Gmail.
I did a select and fetch then got the subject, body and attachments of a few mails and then deleted the mails.
Everything worked perfectly :o)
Thanks for that, Vin