Skip to content

Commit c062a5f

Browse files
pmengal_cppmengal_cp
authored andcommitted
- Added support for GMail custom headers
- Added Peek method for GMail custom method - GMail retrieval method also gets message UID (might be nice to do it for all retrieval methods) - Fixed IDLE for GMail - Added (manual) tests for IMAP Contribution by user alprema
1 parent 169dce1 commit c062a5f

15 files changed

Lines changed: 150 additions & 20 deletions

Class Library/ActiveUp.Net.Imap4/Fetch.cs

Lines changed: 85 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1717

1818
using System;
19+
using System.Linq;
20+
using System.Text.RegularExpressions;
1921
using ActiveUp.Net.Mail;
2022
using ActiveUp.Net.Mail;
2123

@@ -1128,6 +1130,58 @@ public ActiveUp.Net.Mail.Message MessageObject(int messageOrdinal)
11281130
return ActiveUp.Net.Mail.Parser.ParseMessage(this.Message(messageOrdinal));
11291131
}
11301132

1133+
1134+
/// <summary>
1135+
/// Fetches the message's Rfc822 compliant form and adds the GMail specific headers
1136+
/// </summary>
1137+
/// <param name="messageOrdinal">The ordinal position of the message to be fetched.</param>
1138+
/// <returns>The message's data as a Message object.</returns>
1139+
public ActiveUp.Net.Mail.Message MessageObjectWithGMailExtensions(int messageOrdinal)
1140+
{
1141+
return InnerMessageObjectWithGMailExtensions(messageOrdinal, true);
1142+
}
1143+
1144+
/// <summary>
1145+
/// Fetches the message's Rfc822 compliant form and adds the GMail specific headers without marking it as read
1146+
/// </summary>
1147+
/// <param name="messageOrdinal">The ordinal position of the message to be fetched.</param>
1148+
/// <returns>The message's data as a Message object.</returns>
1149+
public ActiveUp.Net.Mail.Message MessageObjectPeekWithGMailExtensions(int messageOrdinal)
1150+
{
1151+
return InnerMessageObjectWithGMailExtensions(messageOrdinal, false);
1152+
}
1153+
1154+
private Message InnerMessageObjectWithGMailExtensions(int messageOrdinal, bool markMessagesAsRead)
1155+
{
1156+
if (!ParentMailbox.SourceClient.ServerCapabilities.Contains("X-GM-EXT-1"))
1157+
throw new InvalidOperationException("The server you're connecting to is missing the GMail extensions");
1158+
var response = MessageRawString(messageOrdinal, markMessagesAsRead, "X-GM-LABELS", "X-GM-MSGID", "UID");
1159+
Logger.AddEntry(response);
1160+
var messageBytes = System.Text.Encoding.UTF8.GetBytes(ExtractMessageFromReponse(response));
1161+
ParentMailbox.SourceClient.OnMessageRetrieved(new MessageRetrievedEventArgs(messageBytes, messageOrdinal));
1162+
var message = Parser.ParseMessage(messageBytes);
1163+
AppendGMailExtensions(message, response);
1164+
AppendUid(message, response);
1165+
return message;
1166+
}
1167+
1168+
private void AppendGMailExtensions(Message message, string response)
1169+
{
1170+
var labelsMatch = Regex.Match(response, @"X-GM-LABELS \(([^\)]*?)\)");
1171+
if (labelsMatch.Success)
1172+
message.AddHeaderField("X-GM-LABELS", labelsMatch.Groups[1].Value);
1173+
var msgIdMatch = Regex.Match(response, @"X-GM-MSGID ([0-9]+)");
1174+
if (msgIdMatch.Success)
1175+
message.AddHeaderField("X-GM-MSGID", Int64.Parse(msgIdMatch.Groups[1].Value).ToString("x"));
1176+
}
1177+
1178+
private void AppendUid(Message message, string response)
1179+
{
1180+
var msgIdMatch = Regex.Match(response, @"UID ([0-9]+)");
1181+
if (msgIdMatch.Success)
1182+
message.AddHeaderField("X-MSG-UID", msgIdMatch.Groups[1].Value);
1183+
}
1184+
11311185
private delegate Message DelegateMessageObject(int messageOrdinal);
11321186
private DelegateMessageObject _delegateMessageObject;
11331187

@@ -1213,22 +1267,40 @@ public System.IO.MemoryStream EndUidMessageStream(IAsyncResult result)
12131267
/// <param name="messageOrdinal">The ordinal position of the message to be fetched.</param>
12141268
/// <returns>The message's data as a string.</returns>
12151269
/// <example><see cref="Fetch.MessageObject"/></example>
1216-
public string MessageString(int messageOrdinal) {
1217-
this.ParentMailbox.SourceClient.SelectMailbox(this.ParentMailbox.Name);
1218-
this.ParentMailbox.SourceClient.OnMessageRetrieving(new ActiveUp.Net.Mail.MessageRetrievingEventArgs(messageOrdinal));
1219-
string response = this.ParentMailbox.SourceClient.Command("fetch " + messageOrdinal.ToString() + " rfc822", getFetchOptions());
1220-
ActiveUp.Net.Mail.Logger.AddEntry(response);
1270+
public string MessageString(int messageOrdinal)
1271+
{
1272+
var response = MessageRawString(messageOrdinal, true);
1273+
Logger.AddEntry(response);
1274+
var message = ExtractMessageFromReponse(response);
1275+
1276+
this.ParentMailbox.SourceClient.OnMessageRetrieved(new ActiveUp.Net.Mail.MessageRetrievedEventArgs(System.Text.Encoding.UTF8.GetBytes(message), messageOrdinal));
1277+
return message;
1278+
}
12211279

1222-
int messageSize = Convert.ToInt32(response.Substring(response.IndexOf("{") + 1, response.IndexOf("}") - response.IndexOf("{") - 1));
1280+
private static string ExtractMessageFromReponse(string response)
1281+
{
1282+
int messageSize = Convert.ToInt32(response.Substring(response.IndexOf("{") + 1, response.IndexOf("}") - response.IndexOf("{") - 1));
12231283
int messageStart = response.IndexOf("}") + 3;
12241284
string message = response.Substring(messageStart, messageSize);
12251285
// Some (older) MS Exchange versions return a smaller message size, use old version as fallback if last character is not new line or the next line starts not with FLAGS
1226-
if (message.Substring(message.Length - 2, 2) != "\r\n" || !response.Substring(messageStart + messageSize).Trim().ToUpper().StartsWith("FLAGS")) {
1286+
if (message.Substring(message.Length - 2, 2) != "\r\n" || !response.Substring(messageStart + messageSize).Trim().ToUpper().StartsWith("FLAGS"))
12271287
message = response.Substring(messageStart, response.LastIndexOf(")") - messageStart);
1228-
}
1288+
return message;
1289+
}
12291290

1230-
this.ParentMailbox.SourceClient.OnMessageRetrieved(new ActiveUp.Net.Mail.MessageRetrievedEventArgs(System.Text.Encoding.UTF8.GetBytes(message), messageOrdinal));
1231-
return message;
1291+
/// <summary>
1292+
/// Fetches the raw reponse for a message fetch.
1293+
/// </summary>
1294+
/// <param name="messageOrdinal">The ordinal position of the message to be fetched.</param>
1295+
/// <param name="markMessagesAsRead">Set to true to mark the retrieved messages as read</param>
1296+
/// <param name="extraDataItemToFetch">Extra fields to retrive besides rfc822</param>
1297+
/// <returns>The response data as a string.</returns>
1298+
private string MessageRawString(int messageOrdinal, bool markMessagesAsRead, params string[] extraDataItemToFetch)
1299+
{
1300+
ParentMailbox.SourceClient.SelectMailbox(ParentMailbox.Name);
1301+
ParentMailbox.SourceClient.OnMessageRetrieving(new MessageRetrievingEventArgs(messageOrdinal));
1302+
var dataItemsToRetrieve = string.Join(" ", new[] { markMessagesAsRead ? "rfc822" : "body.peek[]" }.Concat(extraDataItemToFetch));
1303+
return ParentMailbox.SourceClient.Command("fetch " + messageOrdinal.ToString() + " (" + dataItemsToRetrieve + ")", getFetchOptions());
12321304
}
12331305

12341306
private delegate string DelegateMessageString(int messageOrdinal);
@@ -1418,19 +1490,13 @@ public string MessageStringPeek(int messageOrdinal) {
14181490
response = this.ParentMailbox.SourceClient.Command("fetch " + messageOrdinal.ToString() + " body[mime]", getFetchOptions());
14191491
else
14201492
response = this.ParentMailbox.SourceClient.Command("fetch " + messageOrdinal.ToString() + " rfc822.peek", getFetchOptions());
1421-
1422-
int messageSize = Convert.ToInt32(response.Substring(response.IndexOf("{") + 1, response.IndexOf("}") - response.IndexOf("{") - 1));
1423-
int messageStart = response.IndexOf("}") + 3;
1424-
string message = response.Substring(messageStart, messageSize);
1425-
// Some (older) MS Exchange versions return a smaller message size, use old version as fallback if last character is not new line or the next line starts not with FLAGS
1426-
if (message.Substring(message.Length - 2, 2) != "\r\n" || !response.Substring(messageStart + messageSize).Trim().ToUpper().StartsWith("FLAGS")) {
1427-
message = response.Substring(messageStart, response.LastIndexOf(")") - messageStart);
1428-
}
1493+
1494+
var message = ExtractMessageFromReponse(response);
14291495

14301496
this.ParentMailbox.SourceClient.OnMessageRetrieved(new ActiveUp.Net.Mail.MessageRetrievedEventArgs(System.Text.Encoding.UTF8.GetBytes(message), messageOrdinal));
14311497
return message;
14321498
}
1433-
1499+
14341500
private delegate string DelegateMessageStringPeek(int messageOrdinal);
14351501
private DelegateMessageStringPeek _delegateMessageStringPeek;
14361502

Class Library/ActiveUp.Net.Imap4/Imap4Client.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -994,7 +994,7 @@ public void StartIdle()
994994
response = sr.ReadLine();
995995
this.OnTcpRead(new ActiveUp.Net.Mail.TcpReadEventArgs(response));
996996

997-
if (response.ToUpper().IndexOf("RECENT") > 0)
997+
if (response.ToUpper().IndexOf("RECENT") > 0 || response.ToUpper().IndexOf("EXISTS") > 0)
998998
{
999999
this.OnNewMessageReceived(new NewMessageReceivedEventArgs(int.Parse(response.Split(' ')[1])));
10001000
}

Class Library/ActiveUp.Net.Tests/ActiveUp.Net.Tests.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@
7272
</ItemGroup>
7373
<ItemGroup>
7474
<Compile Include="CommonTests.cs" />
75+
<Compile Include="FluentTests.cs" />
76+
<Compile Include="ImapTests.cs" />
7577
<Compile Include="SmtpTests.cs" />
7678
<Compile Include="Properties\AssemblyInfo.cs" />
7779
</ItemGroup>
@@ -102,6 +104,10 @@
102104
<Project>{3A83AE95-C23F-48B4-9F1A-AD4B32C37B93}</Project>
103105
<Name>ActiveUp.Net.Common</Name>
104106
</ProjectReference>
107+
<ProjectReference Include="..\ActiveUp.Net.Imap4\ActiveUp.Net.Imap4.csproj">
108+
<Project>{6BEE77DF-2DD2-41C3-BA16-60E20B1EDDCC}</Project>
109+
<Name>ActiveUp.Net.Imap4</Name>
110+
</ProjectReference>
105111
<ProjectReference Include="..\ActiveUp.Net.Mail\ActiveUp.Net.Mail.csproj">
106112
<Project>{2BD4C73E-C8DB-420E-9505-96D3F3BCA7B6}</Project>
107113
<Name>ActiveUp.Net.Mail</Name>
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using NUnit.Framework;
2+
3+
namespace ActiveUp.Net.Tests
4+
{
5+
public static class FluentTests
6+
{
7+
public static void ShouldEqual(this object obj, object other)
8+
{
9+
Assert.AreEqual(other, obj);
10+
}
11+
}
12+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
using System;
2+
using ActiveUp.Net.Mail;
3+
using ActiveUp.Net.Security;
4+
using NUnit.Framework;
5+
6+
namespace ActiveUp.Net.Tests
7+
{
8+
public class ImapTests
9+
{
10+
private const string _imapLogin = "[login]";
11+
private const string _imapPassword = "[password]";
12+
private const int _imapPort = 993;
13+
private const string _imapServerAddress = "imap.gmail.com";
14+
15+
[Test, Ignore("Manual tests")]
16+
public void GetGmailLabels()
17+
{
18+
var client = new Imap4Client();
19+
client.ConnectSsl(_imapServerAddress, _imapPort);
20+
client.Login(_imapLogin, _imapPassword);
21+
var inbox = client.SelectMailbox("ToImport");
22+
23+
var message = inbox.Fetch.MessageObjectWithGMailExtensions(1);
24+
25+
message.HeaderFields["X-GM-LABELS"].ShouldEqual(@"""\\Important""");
26+
message.HeaderFields["X-GM-MSGID"].ShouldEqual(@"13c48ecc5057ff3c");
27+
message.HeaderFields["X-MSG-UID"].ShouldEqual(@"151");
28+
var toto = message.ToMimeString();
29+
}
30+
31+
[Test, Ignore("Manual tests")]
32+
public void PeekAtGmailLabels()
33+
{
34+
var client = new Imap4Client();
35+
client.ConnectSsl(_imapServerAddress, _imapPort);
36+
client.Login(_imapLogin, _imapPassword);
37+
var inbox = client.SelectMailbox("ToImport");
38+
39+
var message = inbox.Fetch.MessageObjectPeekWithGMailExtensions(1);
40+
41+
message.HeaderFields["X-GM-LABELS"].ShouldEqual(@"""\\Important""");
42+
message.HeaderFields["X-GM-MSGID"].ShouldEqual(@"13c48ecc5057ff3c");
43+
message.HeaderFields["X-MSG-UID"].ShouldEqual(@"151");
44+
}
45+
}
46+
}
0 Bytes
Binary file not shown.

Samples/Lib/ActiveUp.Net.Dns.dll

0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.

Samples/Lib/ActiveUp.Net.Imap4.dll

1.5 KB
Binary file not shown.

Samples/Lib/ActiveUp.Net.Mail.dll

0 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)