Send email without SMTP Server (DNS Lookup)

This tutorial shows the algorithm of DNS lookup in Mail.dll email component,
which enables your application to send email without using your SMTP server.

In general, you should send email via specified SMTP server. You may ask: how does the specified SMTP server know what address this email should be sent to? The answer is simple: it queries MX record of recipient’s domain using DNS lookup. It then forwards this email to the SMTP server queried from DNS server.

If the recipient’s server doesn’t work fine or rejects the message, your SMTP server will send you an email to tell there was a failure in sending this email.

You can however remove the first step and just ask DNS MX record for the recipient’s SMTP server yourself.

You do this by using DnsHelper helper class:

// C# 

using Limilabs.Client.DNS;
using Limilabs.Client.SMTP;
using Limilabs.Mail;
using Fluent = Limilabs.Mail.Fluent;

string receipientSMTP = new DnsHelper().GetSmtpServer("pat@recipient.com");

using(Smtp client = new Smtp())
{
   client.Connect(receipientSMTP, 25);
   IMail email = Fluent.Mail.Text("Hello")
       .From("john@your_domain.com")
       .To("pat@recipient.com")
       .Create();
   client.SendMessage(email);
   client.Close();
}
' VB.NET

Imports Limilabs.Client.DNS
Imports Limilabs.Client.SMTP
Imports Limilabs.Mail
Imports Fluent = Limilabs.Mail.Fluent

Dim receipientSMTP As String = New DnsHelper().GetSmtpServer("pat@recipient.com")

Using client As New Smtp()
	client.Connect(receipientSMTP, 25)
	Dim email As IMail = Fluent.Mail.Text("Hello") _
		.From("john@your_domain.com") _
		.[To]("pat@recipient.com") _
		.Create()
	client.SendMessage(email)
	client.Close()
End Using

Notice that we are using port 25. SMTP in plain text mode usually works on two ports: 587 – client STMP port, the one that is used by parameter-less Connect method and port 25 – used by servers (previously also used by clients).

Client port (587) may be disabled on servers advertised by MX DNS records – those must use port 25 and are not required to allow clients to connect.

There are two important things you should be also aware:

  • This procedure is not what is popularly known as relaying.
  • Most likely your IP address (the one from which you connect) does not designate a permitted sender for the sender’s domain (your domain, and yours domain MX record). This means that recipient’s SMTP server may refuse the message or can treat such message as spam.

Limilabs celebrates NDC2012 with 10-percent discount

Limilabs offers 10% discount for Mail.dll .NET email component to all Norwegian Developers Conference 2012 (NDC 2012) attendees.

Just place your Mail.dll order between June 6th and June 10th using LIMI-NDC5 coupon code to get 10% discount.

Mail.dll includes .NET IMAP and POP3 components with powerful MIME parser. It automatically extracts attachments from any email message. Allows sending S/MIME signed emails, signed emails validation, encryption and decryption. Includes iCalendar appointments and vCard support. With additional features such as: proxy support, template engine, bounce handling, HTML to plain text converter, Bayesian spam filter.

You can download Mail.dll and give it a try for free.

About Limilabs

Limilabs was founded with the goal of providing the highest level of custom software development and consulting services to our clients. Our mission is to help our customers “build better software”.

Limilabs provides highly valued .NET components and libraries such as Ftp.dll FTP/FTPS component and Barcode.dll barcode component. Limilabs also offers custom application development.

For more information, visit Limilabs home page.

Send iCalendar recurring meeting requests for different timezone

Usually you define time of an event in relation to UTC timezone or local timezone.

If you need to define event on 9:00 o’clock in Alaska you simply need to subtract 9 hours from event time to get the event time in UTC
(18:00:00). 18 – 9 = 9.

It all works great in December (Alaska is UTC-9), but in May, daylight saving time is in effect in Alaska (Alaska is UTC-8 then). In May, event is going to be held at 10:00 Alaska time (18 – 8 = 10). Which is most likely not what you want.

If you need to define a single instance of the event it is still not a problem. To get UTC time for the event you subtract 9 hours in the winter and 8 hours in the summer.

But if the event is recurring, and should be held always at 9 Alaska time, you need to specify the timezone along with the event.

As the timezones in different parts of world change way to often to reflect these changes in Mail.dll, you’ll need to specify the timezone when creating new event (including the daylight savings time).

Here’s the sample:

// C#

using Fluent = Limilabs.Mail.Fluent;
using Limilabs.Client.SMTP;
using Limilabs.Mail;
using Limilabs.Mail.Appointments;

Appointment appointment = new Appointment();

// Create time zone
VTimeZone alaska = appointment.AddTimeZone();
alaska.TimeZoneId = "America/Anchorage";

// Define standard time offset
var standardRecurring = new RecurringRule();
standardRecurring.Frequency = Frequency.Yearly;
standardRecurring.ByDay.Add(Weekday.FirstSunday);
standardRecurring.ByMonths.Add(11);

var standard = new StandardOffset
{
    Name = "AKST",
    Start = new DateTime(1970, 11, 01, 02, 00, 00),
    OffsetFrom = TimeSpan.FromHours(-8),
    OffsetTo = TimeSpan.FromHours(-9),
    RecurringRule = standardRecurring
};

// Define daylight time offset
var daylightRecurring = new RecurringRule();
daylightRecurring.Frequency = Frequency.Yearly;
daylightRecurring.ByDay.Add(new Weekday(2, Weekday.Sunday));
daylightRecurring.ByMonths.Add(3);

var daylight = new DaylightOffset
{
    Name = "AKDT",
    Start = new DateTime(1970, 03, 08, 02, 00, 00),
    OffsetFrom = TimeSpan.FromHours(-9),
    OffsetTo = TimeSpan.FromHours(-8),
    RecurringRule = daylightRecurring
};

alaska.Standard.Add(standard);
alaska.Daylight.Add(daylight);

// Define event
Event e = appointment.AddEvent();
e.Start = new DateTime(2007, 08, 17, 12, 00, 00);
e.End = new DateTime(2007, 08, 17, 12, 30, 00);
e.Summary = "At noon in Alaska";
e.InTimeZone(alaska);

// Make event recurring
RecurringRule rule = e.AddRecurringRule();
rule.Interval = 1;
rule.Until = new DateTime(2011, 12, 1); // -or- rule.Count = 10;

// Every week on  Mon, Tue, Wed, Thu Fri:
rule.Frequency = Frequency.Weekly;
rule.ByDay.AddRange(new[] {
    Weekday.Monday,
    Weekday.Tuesday,
    Weekday.Wednesday,
    Weekday.Thursday,
    Weekday.Friday });

// Set participants
e.SetOrganizer(new Person("Alice", "alice@example.org"));

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

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

IMail email = Fluent.Mail
    .Text("Status meeting at noon in Alaska.")
    .Subject("Status meeting")
    .From("alice@example.org")
    .To("bob@example.org")
    .To("tom@example.org")
    .AddAppointment(appointment)
    .Create();

using (Smtp smtp = new Smtp())
{
    smtp.Connect("smtp.example.org"); // or ConnectSSL
    smtp.UseBestLogin("user", "password");
    smtp.SendMessage(email);
    smtp.Close();
}
' VB.NET

Imports Fluent = Limilabs.Mail.Fluent
Imports Limilabs.Client.SMTP
Imports Limilabs.Mail
Imports Limilabs.Mail.Appointments

Dim appointment As New Appointment()

' Create time zone
Dim alaska As VTimeZone = appointment.AddTimeZone()
alaska.TimeZoneId = "America/Anchorage"

' Define standard time offset
Dim standardRecurring = New RecurringRule()
standardRecurring.Frequency = Frequency.Yearly
standardRecurring.ByDay.Add(Weekday.FirstSunday)
standardRecurring.ByMonths.Add(11)

Dim standard = New StandardOffset() With { _
	.Name = "AKST", _
	.Start = New DateTime(1970, 11, 1, 2, 0, 0), _
	.OffsetFrom = TimeSpan.FromHours(-8), _
	.OffsetTo = TimeSpan.FromHours(-9), _
	.RecurringRule = standardRecurring _
}

' Define daylight time offset
Dim daylightRecurring = New RecurringRule()
daylightRecurring.Frequency = Frequency.Yearly
daylightRecurring.ByDay.Add(New Weekday(2, Weekday.Sunday))
daylightRecurring.ByMonths.Add(3)

Dim daylight = New DaylightOffset() With { _
	.Name = "AKDT", _
	.Start = New DateTime(1970, 3, 8, 2, 0, 0), _
	.OffsetFrom = TimeSpan.FromHours(-9), _
	.OffsetTo = TimeSpan.FromHours(-8), _
	.RecurringRule = daylightRecurring _
}

alaska.Standard.Add(standard)
alaska.Daylight.Add(daylight)

' Define event
Dim e As [Event] = appointment.AddEvent()
e.Start = New DateTime(2007, 8, 17, 12, 0, 0)
e.[End] = New DateTime(2007, 8, 17, 12, 30, 0)
e.Summary = "At noon in Alaska"
e.InTimeZone(alaska)

' Make event recurring
Dim rule As RecurringRule = e.AddRecurringRule()
rule.Interval = 1
rule.Until = New DateTime(2011, 12, 1)
' -or- rule.Count = 10;
' Every week on  Mon, Tue, Wed, Thu Fri:
rule.Frequency = Frequency.Weekly
rule.ByDay.AddRange(New () {Weekday.Monday, Weekday.Tuesday, Weekday.Wednesday, Weekday.Thursday, Weekday.Friday})

' Set participants
e.SetOrganizer(New Person("Alice", "alice@example.org"))

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

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

Dim email As IMail = Fluent.Mail _
	.Text("Status meeting at noon in Alaska.") _
	.Subject("Status meeting") _
	.From("alice@example.org") _
	.[To]("bob@example.org") _
	.[To]("tom@example.org") _
	.AddAppointment(appointment) _
	.Create()

Using smtp As New Smtp()
	smtp.Connect("smtp.example.org") 	' or ConnectSSL
	smtp.UseBestLogin("user", "password")
	smtp.SendMessage(email)
	smtp.Close()
End Using

Western European Time Zone (WET)

Western European Time Zone (WET) with its Daylight Saving Time – Western European Summer Time (WEST) is defined below:

// Create time zone
VTimeZone western = appointment.AddTimeZone();
western.TimeZoneId = "W. Europe Standard Time";

// Define standard time offset
var standardRecurring = new RecurringRule();
standardRecurring.Frequency = Frequency.Yearly;
standardRecurring.ByDay.Add(new Weekday(-1, Weekday.Sunday));
standardRecurring.ByMonths.Add(10);

var standard = new StandardOffset
{
    Name = "WET", // Western European Time
    Start = new DateTime(1601,01,01,03,00,00),
    OffsetFrom = TimeSpan.FromHours(2),
    OffsetTo = TimeSpan.FromHours(1),
    RecurringRule = standardRecurring
};

// Define daylight time offset
var daylightRecurring = new RecurringRule();
daylightRecurring.Frequency = Frequency.Yearly;
daylightRecurring.ByDay.Add(new Weekday(-1, Weekday.Sunday));
daylightRecurring.ByMonths.Add(3);

var daylight = new DaylightOffset
{
    Name = "WEST", // Western European Summer Time
    Start = new DateTime(1601,01,01,02,00,00),
    OffsetFrom = TimeSpan.FromHours(1),
    OffsetTo = TimeSpan.FromHours(2),
    RecurringRule = daylightRecurring
};

western.Standard.Add(standard);
western.Daylight.Add(daylight);


Event e = appointment.AddEvent();
e.Start = new DateTime(2013, 04, 12, 15, 00, 00);
e.End = new DateTime(2013, 04, 12, 16, 00, 00);
e.Summary = "";
e.InTimeZone(western);

Detect Gmail’s 2-Step Authentication

Gmail urges users to use 2-step authentication. Basically this means that Gmail texts users special codes that need to be entered every time they logs-in to theirs account.

This article describes how you can detect 2-step authentication is turned on. This way you can instruct the user to either disable it or to generate and use application specific password for your program.

When 2-steps verification is turned on, Imap.Login method will throw an ServerException with following message:

“[ALERT] Application-specific password required: http://support.google.com/accounts/bin/answer.py?answer=185833 (Failure)”

You can use it to check if application-specific password is needed:

// C#

try
{
   imap.UseBestLogin("pat", "password");
}
catch (ServerException ex)
{
   bool needs2StepVerification = ex.Message.Contains("Application-specific password required");
   imap.Close();
   if (needs2StepVerification)
       Console.WriteLine("Application-specific password required");
   retrun;
}
' VB.NET

Try
	imap.UseBestLogin("pat", "password")
Catch ex As ServerException
	Dim needs2StepVerification As Boolean = ex.Message.Contains("Application-specific password required")
	imap.Close()
	If needs2StepVerification Then
		Console.WriteLine("Application-specific password required")
	End If
	retrun
End Try

Folder access with IMAP

This article describes how to access different folder then inbox using Mail.dll IMAP library.

In most cases the first thing you need from an IMAP server is to access INBOX folder. Mail.dll has a special method for that, as this is the only folder that must exist on every IMAP server. It’s Imap.SelectInbox.

Most servers, of course, allow IMAP client to access other folders as well. You can list all IMAP folders using Imap.GetFolders method.

If you want to access different IMAP folder than Inbox, you need to use overloaded Imap.Select method which takes string parameter (folder name) or FolderInfo parameter (returned from a call to Imap.GetFolders method).

After that you can use GetAll, Search and GetMessageByUID methods to search and download email messages.

// C#

using (Imap imap = new Imap())
{
    imap.Connect("imap.example.com");    // or ConnectSSL for SSL
    imap.UseBestLogin("user", "password");

    imap.Select("Sent items");

    List<long> uids = imap.GetAll();

    foreach (long uid in uids)
    {
        IMail email = new MailBuilder()
            .CreateFromEml(imap.GetMessageByUID(uid));
        string subject = email.Subject;
    }
    imap.Close();
}
' VB.NET

Using imap As New Imap()
	imap.Connect("imap.example.com")	' or ConnectSSL for SSL
	imap.UseBestLogin("user", "password")

	imap.[Select]("Sent items")


	Dim uids As List(Of Long) = imap.GetAll()

	For Each uid As Long In uids
		Dim email As IMail = New MailBuilder().CreateFromEml(imap.GetMessageByUID(uid))
		Dim subject As String = email.Subject
	Next
	imap.Close()
End Using

There is no established standard on naming common IMAP folders like ‘Sent mail’, ‘Spam’ etc. However some servers support XLIST command (Gmail) or SpecialUse extension that allow to identify common folders on this particular IMAP server.