Posts Tagged ‘Mail.dll’

Send iCalendar meeting requests for different timezone

Sunday, January 1st, 2012

Usually you define time of an event in relation to UTC time zone.

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).

If the event is recurring, in May, event is going to be held on 10:00 Alaska time (18 – 8 = 10). Which is most likely not what you want.

As the time zones in different parts of world change way to often to reflect these changes in Mail.dll, you’ll need to specify the time zone 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.Sunday);
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);

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));

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.Sunday)
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)

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))

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

Reply to an email

Wednesday, December 28th, 2011

You can use Mail.dll to easy reply to an HTML and plain-text emails.

ReplyBuilder class allows you to specify custom HTML and plain-text reply templates. Mail.dll will parse HTML, extract body part, and build-in template engine will do the rest to create nicely formatted reply with all attachments, To and Cc fields set.

The next great thing is that plain-text version of the email is generated automatically.

You can also take advantage of ReplyAll method that makes it easy to reply to all: senders of the message and all To and Cc recipients

In our example we’ll use Mail.dll IMAP client to download first message from an IMAP server. Then we’ll use ReplyBuilder to create a reply email message. Finally we’ll use Mail.dll SMTP client to send this message.

// C#

IMail original = GetFirstMessage();

ReplyBuilder replyBuilder = original.Reply();
// You can specify your own custom template:
replyBuilder.HtmlReplyTemplate =
    "[Html]" +
    "<br /><br />" +
    "On [Original.Date] [Original.Sender.Name] wrote:" +
    "<blockquote " +
        "style='margin-left: 1em; " +
        "padding-left: 1em; " +
        "border-left: 1px #ccc solid;'>" +
    "[QuoteHtml]" +
    "</blockquote>";

replyBuilder.Html =
    "Alice, <br/><br/>thanks for your email.";

MailBuilder builder = replyBuilder.ReplyToAll(
    new MailBox("bob@example.org", "Bob"));

// You can add attachments to your reply
//builder.AddAttachment("report.csv");

IMail reply = builder.Create();

using (Smtp smtp = new Smtp())
{
    smtp.Connect("smtp.example.com"); // or ConnectSSL
    smtp.UseBestLogin("user", "password");
    smtp.SendMessage(reply);
    smtp.Close();
}

static IMail GetFirstMessage()
{
    IMail email;
    using (Imap imap = new Imap())
    {
        imap.Connect("imap.example.com"); // or ConnectSSL if you want to use SSL
        imap.UseBestLogin("user", "password");
        List<long> uids = imap.GetAll();
        if (uids.Count == 0)
            throw new Exception("There are no messages");
        string eml = imap.GetMessageByUID(uids[0]);
        email = new MailBuilder().CreateFromEml(eml);
        imap.Close();
    }
    return email;
}
' VB.NET

Dim original As IMail = GetFirstMessage()

Dim replyBuilder As ReplyBuilder = original.Reply()

' You can specify your own custom template:
replyBuilder.HtmlReplyTemplate = _
    "[Html]" + _
    "<br /><br />" + _
    "On [Original.Date] [Original.Sender.Name] wrote:" + _
    "<blockquote " + _
        "style='margin-left: 1em; " + _
        "padding-left: 1em; " + _
        "border-left: 1px #ccc solid;'>" + _
    "[QuoteHtml]" + _
    "</blockquote>"

replyBuilder.Html = _
    "Alice, <br/><br/>thanks for your email."

Dim builder As MailBuilder = replyBuilder.ReplyToAll( _
    New MailBox("bob@example.org", "Bob"))

' You can add attachments to your reply
'builder.AddAttachment("report.csv")

Dim reply As IMail = builder.Create()

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

Private Shared Function GetFirstMessage() As IMail
	Dim email As IMail
	Using imap As New Imap()
		imap.Connect("imap.example.com")
		' or ConnectSSL if you want to use SSL
		imap.UseBestLogin("user", "password")
		Dim uids As List(Of Long) = imap.GetAll()
		If uids.Count = 0 Then
			Throw New Exception("There are no messages")
		End If
		Dim eml As String = imap.GetMessageByUID(uids(0))
		email = New MailBuilder().CreateFromEml(eml)
		imap.Close()
	End Using
	Return email
End Function

Extract plain text from HTML email

Tuesday, December 27th, 2011

Mail.dll MIME and email API’s may be used to get the plain-text body and HTML body from an email message.

If a message contains plain-text, no conversion is necessary. It’s simply a matter of using the Text property of IMail interface.

If however the email does not contain plain-text and only HTML content is available, GetTextFromHtml method may be used to convert the HTML to plain-text.

The internal conversion process is much more sophisticated than what can be accomplished with the simple regular-expression code. Converting HTML to plain text is much more than simply removing HTML tags from an HTML document.

Mail.dll contains full-blown HTML parser that handles script tags, comments, CDATA and even incorrect HTML.

The following C# and VB.NET code extracts plain-text from the HTML body of the email message:

// C#

IMail email = ...

string text = "";
if (email.IsText)
    text = email.Text;
else if (email.IsHtml)
    text = email.GetTextFromHtml();
Console.WriteLine(text);
' VB.NET

Dim email As IMail = ...

Dim text As String = ""
If email.IsText Then
    text = email.Text
ElseIf email.IsHtml Then
    text = email.GetTextFromHtml()
End If
Console.WriteLine(text)

You can also use GetBodyAsText method that returns body in plain text format (it uses IMail.Text property or GetTextFromHtml method).

// C#

IMail email = ...

string text = email.GetBodyAsText();
Console.WriteLine(text);
' VB.NET

Dim email As IMail = ...

Dim text As String = email.GetBodyAsText()
Console.WriteLine(text)

Import certificate, private or public keys (PEM, CER, PFX)

Tuesday, November 22nd, 2011

Encrypted private key, RSA private key in PEM file

PEM stands for Privacy Enhanced Mail format.

PemReader pem = new PemReader();
RSACryptoServiceProvider rsa = pem.ReadEncryptedPrivateKeyFromFile(
   "EncryptedPrivateKey.pem", // "EncryptedRSAPrivateKey.pem"
   "cypher");

This code handles following formats:

PKCS #8 EncryptedPrivateKeyInfo Encrypted Format:

-----BEGIN ENCRYPTED PRIVATE KEY-----
MIICojAcBgoqhkiG9w0BD .....

Private Key (Traditional SSLeay RSAPrivateKey format) Encrypted:

-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: DES-EDE3-CBC,24A667C253F8A1B9

mKz .....

You can remove the passphrase from the private key:
openssl rsa -in EncryptedPrivateKey.pem -out PrivateKey.pem

Unencrypted private key in PEM file

PemReader pem = new PemReader();
RSACryptoServiceProvider rsa = pem.ReadPrivateKeyFromFile(
   "PrivateKey.pem");

This code handles following formats:

PKCS #8 PrivateKeyInfo Unencrypted:

-----BEGIN PRIVATE KEY-----
MIICdgIBADANBgkqhkiG9w0B ......

Private Key (Traditional SSLeay RSAPrivateKey format) Unencrypted:

-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQCcHVm .....

Public key in PEM file

PemReader pem = new PemReader();
RSACryptoServiceProvider rsa = pem.ReadPublicKeyFromFile(
   "PublicKey.pem")

This code handles following formats:

Public Key (SubjecPublicKeyInfo):

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEB .....

Certificate/private key in PFX file

X509Certificate2 certificate  = new X509Certificate2(
   "certificate.pfx",
   "",
   X509KeyStorageFlags.PersistKeySet)

if (certificate.HasPrivateKey)
{
  using (RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certificate.PrivateKey)
  {
  }
}

Certificate in PEM/CER file

Note: The private key is never stored in a .pem/.cer file.

X509Certificate2 certificate = new X509Certificate2(
   "certificate.cer");

-or-

PemReader pem = new PemReader();
X509Certificate2 certificate = pem.ReadCertificateFromFile(
   "certificate.cer");

This code handles following formats:


-----BEGIN CERTIFICATE-----
MIIFsTCCA5mgAwIBAgIKYQ .....

Mail.dll .NET email spam filter

Saturday, October 22nd, 2011

Mail.dll .NET email component includes high accuracy anti-spam filter.

It uses enhanced naive Bayesian classifier, specifically modified to handle email messages. Bayesian spam filters are a very powerful technique for dealing with spam.

In our tests we achieved 99,6% accuracy with very low false positive spam detection rates (9 false positives in 54’972 emails tested – that’s 0.016%).

Training

First in the learning phase, you need to teach the classifier to recognize spam and non-spam (ham) messages. You need to prepare 100-200 spam and ham messages.

I suggest using following folder structure:

“Learn” folder is used for training the filter. Both spam and ham folders should contain around 100-200 messages each (the more the better). The number of messages in spam and ham folders must be equal. You can find a spam archive on the bottom of the article.

Messages must be in eml format with correct line endings (rn or 13 10 hex).

Now we use SpamFilterTeacher class to teach BayesianMailFilter:

// C#
using Limilabs.Mail.Tools.Spam;

BayesianMailFilter filter = new BayesianMailFilter();
SpamFilterTeacher teacher = new SpamFilterTeacher(filter);
teacher.TeachSpam(@"c:\bayes\learn\spam");
teacher.TeachHam(@"c:\bayes\learn\ham");

Testing

“Test” folder is used for testing our filter:

// C#

SpamTestResults r = teacher.Test(
    @"c:\bayes\test\spam",
    @"c:\bayes\test\ham");

Console.WriteLine(r);
r.FalsePositives.ForEach(Console.WriteLine);
r.NotMarkedAsSpam.ForEach(Console.WriteLine);

The results should be similar to this:

Accuracy=0.9949, False positives=9, Not marked as spam=271, Tests count=54972
c:\bayes\test\ham/16874.eml
...

When the filter is trained and the results are satisfactory, you can save it to disk:

// C#

filter.Save("c:\20111022.mbayes");

Using

You can load the filter from disk and check individual messages:

// C#

BayesianMailFilter filter = new BayesianMailFilter();
filter.Load("c:\20111022.mbayes");

// you can use Mail.dll to download mesage from POP3 or IMAP server:
string eml = ...

IMail email = new MailBuilder().CreateFromEml(eml);

SpamResult result = filter.Examine(email);
Console.WriteLine(result.Probability);
Console.WriteLine(result.IsSpam);

If the filter incorrectly recognizes the message you can train it again:

// C#

filter.LearnSpam(email);
// - or -
filter.LearnHam(email);

filter.Save("c:\20111022.mbayes");

Spam archives

For most recent spam you can check this great archive: http://www.untroubled.org/spam/.
Unfortunately messages don’t have correct extension (*.eml) and line endings are incorrect.

You can download spam archive including 7874 spam messages from Oct 2011 here:
http://www.limilabs.com/static/mail/spam/spam201110.zip