Send iCalendar meeting requests

Mail.dll .NET email component includes support for popular iCalendar standard.

iCalendar is used by most popular email clients like Outlook or Gmail. It allows Internet users to send meeting requests and tasks to other users, via email, or sharing files with an extension of .ics.

Recipients of the iCalendar data file (with supporting software, such as an email client or calendar application) can respond to the sender easily or counter propose another meeting date/time.

First make sure that your ‘usings’ or ‘Imports’ section includes all needed namespaces:

// C#

using Limilabs.Mail;
using Limilabs.Mail.Fluent;
using Limilabs.Mail.Appointments;
' VB.NET

Imports Limilabs.Mail
Imports Limilabs.Mail.Fluent
Imports Limilabs.Mail.Appointments

And now the code.

First we’ll create an appointment object add new event to it:

Appointment appointment = new Appointment();
Event e = appointment.AddEvent();

Then we need to set start and end dates:

e.Description = "Status meeting description";
e.Summary = "Status meeting summary";
e.Start = new DateTime(2015, 05, 10, 16, 00, 00);
e.End = new DateTime(2015, 05, 10, 17, 00, 00);

Now we’ll add all required and optional participants to it:

e.SetOrganizer(new Person("Alice", "alice@mail.com"));

e.AddParticipant(new Participant(
	"Bob", "bob@mail.com", ParticipationRole.Required, true));
e.AddParticipant(new Participant(
	"Tom", "tom@mail.com", ParticipationRole.Optional, true));
e.AddParticipant(new Participant(
	"Alice", "alice@mail.com", ParticipationRole.Required, true));

We’ll add an alarm to the event (set 15 minutes before the event start):

Alarm alarm = e.AddAlarm();
alarm.BeforeStart(TimeSpan.FromMinutes(15));

Finally we’ll create an email add the appointment to it and send it:

Mail.Text("Status meeting at 4PM.")
	.Subject("Status meeting")
	.From("alice@mail.com")
    .To("bob@mail.com")
    .AddAppointment(appointment)
    .UsingNewSmtp()
    .WithCredentials("alice@mail.com", "password")
	.Server("smtp.mail.com")
	.WithSSL()
	.Send();

The entire C# code looks as follows:

Appointment appointment = new Appointment();

Event e = appointment.AddEvent();
e.Description = "Status meeting description";
e.Summary = "Status meeting summary";
e.Start = new DateTime(2015, 05, 10, 16, 00, 00);
e.End = new DateTime(2015, 05, 10, 17, 00, 00);

e.SetOrganizer(new Person("Alice", "alice@mail.com"));

e.AddParticipant(new Participant(
	"Bob", "bob@mail.com", ParticipationRole.Required, true));
e.AddParticipant(new Participant(
	"Tom", "tom@mail.com", ParticipationRole.Optional, true));
e.AddParticipant(new Participant(
	"Alice", "alice@mail.com", ParticipationRole.Required, true));

Alarm alarm = e.AddAlarm();
alarm.BeforeStart(TimeSpan.FromMinutes(15));

Mail.Text("Status meeting at 4PM.")
	.Subject("Status meeting")
	.From("alice@mail.com")
    .To("bob@mail.com")
    .AddAppointment(appointment)
    .UsingNewSmtp()
    .WithCredentials("alice@mail.com", "password")
	.Server("smtp.mail.com")
	.WithSSL()
	.Send();

…and the VB.NET version:

Dim appointment As New Appointment()

Dim e As [Event] = appointment.AddEvent()
e.Description = "Status meeting description"
e.Summary = "Status meeting summary"
e.Start = New DateTime(2015, 5, 10, 16, 0, 0)
e.[End] = New DateTime(2015, 5, 10, 17, 0, 0)

e.SetOrganizer(New Person("Alice", "alice@mail.com"))

e.AddParticipant(New Participant( _
	"Bob", "bob@mail.com", ParticipationRole.Required, True))
e.AddParticipant(New Participant( _
	"Tom", "tom@mail.com", ParticipationRole.[Optional], True))
e.AddParticipant(New Participant( _
	"Alice", "alice@mail.com", ParticipationRole.Required, True))

Dim alarm As Alarm = e.AddAlarm()
alarm.BeforeStart(TimeSpan.FromMinutes(15))

Mail.Text("Status meeting at 4PM.") _
	.Subject("Status meeting") _
	.From("alice@mail.com") _
	.[To]("bob@mail.com") _
	.AddAppointment(appointment) _
	.UsingNewSmtp() _
	.WithCredentials("alice@mail.com", "password") _
	.Server("smtp.mail.com") _
	.WithSSL() _
	.Send()

You can download Mail.dll .NET email component here.

NavigateToTest VS extension

You can download the extension here:
NavigateToTest Visual Studio extension

Recently I decided to create a Visual Studio 2010 extension.

Extension is convention based. It matches ClassName file with ClassNameTest or ClassNameTests and vice-versa, so you can easily navigate to the test file and back.

Here are some screenshots:

You’ll need to add toolbar manually:

Here’s the toolbar:

And two opened files:

You can download the extension here:
NavigateToTest Visual Studio extension

Editor Guidelines in VS2010

In VS2008 this could be done easily, it was just required to modify the registry.

Visual Studio 2010 lacks this feature, but there is a cool plugin for that:
Editor Guidelines.

Just download, install, and put the following code in to the registry:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USERSoftwareMicrosoftVisualStudio10.0Text Editor]
"Guides"="RGB(255,0,0) 80"

IMAP IDLE: instant push email notifications

IDLE is an IMAP protocol extension described in RFC 2177. It allows an IMAP client to indicate to the server that it is ready to accept real-time notifications about the changes (e.g. new message) in the currently selected folder.

This feature is not available for every IMAP server. You need to check if your IMAP server supports ImapExtension.Idle extension.

Mail.dll .NET IMAP library supports IDLE command.

There are two Imap class methods, that allow client to receive notifications:

  • Idle() – starts accepting real-time notifications. The method hangs until the new notification is received.
  • StopIdle() – stops accepting real-time notifications. This method is thread-safe.

The following sample connects to an IMAP server and starts to receive notifications. When new message arrives (or an old message is deleted) it displays new message count, then searches IMAP server for unseen messages, downloads them, and displays subjects of all new messages.

using (Imap client = new Imap())
{
    client.ConnectSSL("imap.server.com");
    client.Login("user@server.com", "password");

    FolderStatus folderStatus = client.SelectInbox();
    Console.WriteLine("Total message count: {0}",
        folderStatus.MessageCount);

    while(true)
    {
        FolderStatus currentStatus = client.Idle();
        Console.WriteLine("Total message count: {0}",
                currentStatus.MessageCount);
        foreach(long uid in client.Search(Flag.Unseen))
        {
            IMail email = new MailBuilder().CreateFromEml(
                client.GetHeadersByUID(uid));
            Console.WriteLine(email.Subject);
        }
    }
    client.Close();
}
' VB.NET code

Using client As New Imap()
	client.ConnectSSL("imap.server.com")
	client.Login("user@server.com", "password")

	Dim folderStatus As FolderStatus = client.SelectInbox()
	Console.WriteLine("Total message count: {0}",_
		folderStatus.MessageCount)

	While True
		Dim currentStatus As FolderStatus = client.Idle()
		Console.WriteLine("Total message count: {0}",_
			currentStatus.MessageCount)
		For Each uid As Long In client.Search(Flag.Unseen)
			Dim email As IMail = New MailBuilder()_
 				.CreateFromEml(client.GetHeadersByUID(uid))
			Console.WriteLine(email.Subject)
		Next
	End While
	client.Close()
End Using

Stop IDLE gracefully

In this next sample we’ll handle stop gracefully. As you can see StopIdle method is thread safe. This means that it can be used from any other thread (for example UI thread).

// C# code

using (Imap client = new Imap())
{
    client.ConnectSSL("imap.server.com");
    client.Login("user@server.com", "password");

    FolderStatus folderStatus = client.SelectInbox();
    Console.WriteLine("Total message count: {0}",
        currentStatus.MessageCount);

    bool stop = false;
    // We start a new thread to handle user input, enter = stop idle
    new Thread(() =>
	{
		Console.ReadLine();
		client.StopIdle();
		stop = true;
	}).Start();

    while(stop == false)
    {
        FolderStatus currentStatus = client.Idle();
        if (stop == true)
            break;

        Console.WriteLine("Total message count: {0}",
            currentStatus.MessageCount);

        foreach(long uid in client.Search(Flag.Unseen))
        {
            IMail email = new MailBuilder().CreateFromEml(
                client.GetHeadersByUID(uid));
            Console.WriteLine(email.Subject);
        }
    }
    client.Close();
}
' VB.NET code

Using client As New Imap()
	client.ConnectSSL("imap.server.com")
	client.Login("user@server.com", "password")

	Dim folderStatus As FolderStatus = client.SelectInbox()
	Console.WriteLine("Total message count: {0}",_
		currentStatus.MessageCount)

	Dim [stop] As Boolean = False
	' We start a new thread to handle user input, enter = stop idle
	New Thread(Function() Do
		Console.ReadLine()
		client.StopIdle()
		[stop] = True
	End Function).Start()

	While [stop] = False
		Dim currentStatus As FolderStatus = client.Idle()
		If [stop] = True Then
			Exit While
		End If

		Console.WriteLine("Total message count: {0}",_
			currentStatus.MessageCount)

		For Each uid As Long In client.Search(Flag.Unseen)
			Dim email As IMail = New MailBuilder()_
				.CreateFromEml(client.GetHeadersByUID(uid))
			Console.WriteLine(email.Subject)
		Next
	End While
	client.Close()
End Using

Drawbacks

IDLE has some drawbacks:

  • It requires constant server connection.
  • IDLE command leaves TCP/IP connection unused for a long time, and although IDLE is reissued/refreshed every 10 minutes (RFC requirement is 29 minutes, but this is much to long), some routers may assume the connection is dead, because of too long period of inactivity.

    Shortening the timeout using Imap.Idle(TimeSpan) overload usually solves this issue. Don’t be afraid of shortening the timeout value as reissuing IDLE command takes less that a hundred bytes of data.

A non-blocking socket operation could not be completed

There is a bug in .NET 2.0 regarding timeout handling by Socket class.
Here’s the code that reproduces this behavior.

using(TcpClient client = new TcpClient())
{
    // Set timeout
    client.ReceiveTimeout =
        (int)TimeSpan.FromSeconds(1).TotalMilliseconds;

    // Connect to perfectly working imap server
    client.Connect("mail.limilabs.com", 143);

    // Create reader and writer
    NetworkStream stream = client.GetStream();
    StreamReader reader = new StreamReader(stream);
    StreamWriter writer = new StreamWriter(stream);

    // Read hello line
    Console.WriteLine(reader.ReadLine());

    // this works with no problems
    writer.WriteLine("a NOOP");             // No operation command
    writer.Flush();
    Console.WriteLine(reader.ReadLine());   // No operation response

    try
    {
        reader.ReadLine();                  // Nothing to read
    }
    catch (Exception ex)                    // Timeout
    {
        Console.WriteLine("timeout");       // This is expected
    }

    writer.WriteLine("b NOOP");             // No operation command
    writer.Flush();

    // SocketException: A non-blocking socket operation
    // could not be completed immediately
    Console.WriteLine(reader.ReadLine());

    client.Close();
}

The above code runs as expected on Mono and on .NET 4.0.

The problem is in UpdateStatusAfterSocketError internal Socket method that executes SetToDisconnected method when any exception is thrown, including timeout exception.