Better public API: GetAccountStat

August 21st, 2010

Here’s some code I recently found in Mail.dll and decided to refactor.

// Before:

using(Pop3 client = new Pop3())
{
    client.GetAccountStat();
    Console.WriteLine(
        "Inbox has {0} emails.",
        client.MessageCount);
}

There are several things wrong with this code.

  • The method is called Get… but it does not get anything, it changes the internal state of the object.
  • Message count is stored in Pop3 object:

    • If the user of your API connects later to a different server you need to remember to reset this variable.
    • If the user of your API forgets to call GetAccountStat, message count is undefined (Should it be null?)

It’s really hard to say that this is a friendly API, as it requires the user to perform actions in specific order (call GetAccountStat before accessing message count).

Another problem is that GetAccountStat method is responsible for parsing the server response. It’s not necessary a bad thing, but if you have hundreds such methods then Pop3 class gets bloated with hard-to-test parsing logic.

Now lets take a look at the After code:

// After:

using(Pop3 client = new Pop3())
{
    AccountStats stats = client.GetAccountStat();
    Console.WriteLine(
        "Inbox has {0} emails.",
        stats.MessageCount);

}

Here we can see a good API:

  • Method is called Get…. and it actually gets something.
  • No specific call order is required, you simply call one method and act on the result.
  • Parsing logic was moved to the AccountStats class.

This is not seen here but AccountStats method has a static Parse method…and look how easy is to write unit test for it’s behavior:

[Test]
public void Parse_MessageCountAndMailboxSize_AreFilled()
{
    AccountStats stats = AccountStats.Parse("2 373766");
    Assert.AreEqual(2, stats.MessageCount);
    Assert.AreEqual(373766, stats.MailboxSize);
}

Note also that actually we have NOT introduced a breaking change to our public API. Following code still works:

using(Pop3 client = new Pop3())
{
    client.GetAccountStat();
    Console.WriteLine(
        "Inbox has {0} emails.",
        client.MessageCount);
}

You’ll get 2 obsolete warnings:

warning CS0618: 'Lesnikowski.Client.Pop3.MessageCount' is obsolete: 'Please use the return value of GetAccountStat method instead.'

warning CS0618: 'Lesnikowski.Client.Pop3.MailboxSize' is obsolete: 'Please use the return value of GetAccountStat method instead.'

As we marked MessageCount and MailboxSize with [obsolete] attribute, but that’s it!

New Barcode.dll tutorial videos

August 13th, 2010

New Barcode.dll tutorial videos are out. You can watch them here:

Barcode.dll ASP.NET tutorial video Barcode.dll ASP.NET tutorial video.

Barcode.dll Windows Forms tutorial video Barcode.dll Windows Forms tutorial video.

Barcode.dll Windows Forms printing tutorial video Barcode.dll Windows Forms printing tutorial video.

POP3 vs IMAP

August 5th, 2010

POP3 and IMAP are application-layer Internet standard protocols used by local e-mail clients to retrieve e-mails from a remote server over a TCP/IP connection. Virtually all modern e-mail clients and mail servers support both protocols as a means of transferring e-mail messages from a server.

They are both text-based. Both transmit more or less same amount of data.

Here is the brief comparison of both:

POP3 IMAP
Intended message store Client Server
Download message YES YES
Download message headers YES YES
Download specific message part (single attachment) NO YES
Delete message YES YES
Send message NO (use SMTP) NO (use SMTP)
Get only unseen messages NO YES
Mark message Seen/Unseen NO YES
Server side search NO YES
Folders NO YES
Sent items NO YES
SSL YES YES
Push email NO YES

Mail.dll supports both POP3 and IMAP (and SMTP), give it a try at:
http://www.lesnikowski.com/mail/

CAS .NET 4.0 and RDLC ReportViewer

August 4th, 2010

In .NET 4.0 Code Access Security (CAS) has been deprecated.

Well I never liked CAS very much, so it’s definitely a good thing!
Unfortunately because of that some things are done differently now (in .NET 4.0).

Especially when you use RDLC ReportViewer control.

If you need to use your assembly in ReportViewer (like for example Barcode.dll barcode component) you need to follow this steps:

1.

First make sure that your assembly (Barcode.dll) is registered in GAC (Barcode.dll installer does that).
You can use:
gacutil -l Barcode
to check, and
gacutil -i Barcode.dll
to register.

As Code Access Security was deprecated you have to options:

2a.

You can add NetFx40_LegacySecurityPolicy entry in your App.config file:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
 <runtime>
   <!-- enables legacy CAS policy for this process -->
   <NetFx40_LegacySecurityPolicy enabled="true" />
 </runtime>
</configuration>

and use this code (it is used in samples in Form1_Load method):

// C#

this.ReportViewer1.LocalReport.ExecuteReportInCurrentAppDomain(
  Assembly.GetExecutingAssembly().Evidence);
this.ReportViewer1.LocalReport.AddTrustedCodeModuleInCurrentAppDomain(
  "Barcode, Version=2.0.0.20, Culture=neutral, PublicKeyToken=6dc438ab78a525b3");
this.ReportViewer1.LocalReport.AddTrustedCodeModuleInCurrentAppDomain(
  "System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
' VB.NET

Me.ReportViewer1.LocalReport.ExecuteReportInCurrentAppDomain( _
  Assembly.GetExecutingAssembly().Evidence)
Me.ReportViewer1.LocalReport.AddTrustedCodeModuleInCurrentAppDomain( _
  "Barcode, Version=2.0.0.20, Culture=neutral, PublicKeyToken=6dc438ab78a525b3")
Me.ReportViewer1.LocalReport.AddTrustedCodeModuleInCurrentAppDomain( _
  "System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")

or the second approach which is easier:

2b.

Use the following code in Form1_Load method:

// C#

PermissionSet permissions = new PermissionSet(
  PermissionState.Unrestricted);
this.ReportViewer1.LocalReport.SetBasePermissionsForSandboxAppDomain(
  permissions);
' VB.NET

Dim permissions As New PermissionSet( _
  PermissionState.Unrestricted)
Me.ReportViewer1.LocalReport.SetBasePermissionsForSandboxAppDomain( _
  permissions)

You can find samples how to use Barcode.dll with RDLC in C#, VB.NET in Windows Forms and ASP.NET in the Barcode.dll download package.

Sign emails with DKIM

July 27th, 2010

DKIM is short for DomainKeys Identified Mail.

Adding a signature looks like this:

// C#

using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider())
{
   IMail email = Mail
       .Text("text")
       .From("alice@mail.com")
       .To("bob@mail.com")
       .Subject("subject")
       .DKIMSign(rsa, "brisbane", "example.com")
       .Create();
}
' VB.NET

Using rsa As New RSACryptoServiceProvider()
	Dim email As IMail = Mail _
		.Text("text") _
		.From("alice@mail.com") _
		.[To]("bob@mail.com") _
		.Subject("subject") _
		.DKIMSign(rsa, "brisbane", "example.com") _
		.Create()
End Using

So what you need is RSACryptoServiceProvider with your private key, and two strings: selector and domain.

Basically how this works is the recipient of the email queries the DNS server for TXT record for selector._domainkey.domain (in our sample it is: “brisbane._domainkey.example.com”) to get the public key and validate the signature.

You can use nslookup to get the public key for a domain:

Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation.  All rights reserved.

C:\Users\Pawel>nslookup
Default Server:  UnKnown
Address:  192.168.0.1

> set type=TXT
> gamma._domainkey.gmail.com
Server:  UnKnown
Address:  192.168.0.1

Non-authoritative answer:
gamma._domainkey.gmail.com      text =

        "k=rsa; t=y; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDIhyR3oItOy22ZOaBrI
Ve9m/iME3RqOJeasANSpg2YTHTYV+Xtp4xwf5gTjCmHQEMOs0qYu0FYiNQPQogJ2t0Mfx9zNu06rfRBD
jiIU9tpx2T+NGlWZ8qhbiLo5By8apJavLyqTLavyPSrvsx0B3YzC63T4Age2CDqZYA+OwSMWQIDAQAB"

gmail.com       nameserver = ns1.google.com
gmail.com       nameserver = ns4.google.com
gmail.com       nameserver = ns3.google.com
gmail.com       nameserver = ns2.google.com
ns1.google.com  internet address = 216.239.32.10
ns2.google.com  internet address = 216.239.34.10
ns3.google.com  internet address = 216.239.36.10
ns4.google.com  internet address = 216.239.38.10
>

But don’t worry as Mail.dll will make this DNS query for you.

Validating (including DNS query for public key) is simple:

// C#

IMail email = new MailBuilder()
    .CreateFromEmlFile("signed_gamma.gmail.eml");
bool isValid = email.CheckDKIMSignature();
' VB.NET

Dim email As IMail = New MailBuilder() _
    .CreateFromEmlFile("signed_gamma.gmail.eml")
Dim isValid As Boolean = email.CheckDKIMSignature()

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