Download emails from Gmail via POP3

gmail
Accessing Gmail with POP3 is a bit different that accessing other POP3 servers. Still is quite easy with Mail.dll POP3 client. Remember that POP3 protocol unlike IMAP does not store the unseen information on the server.

Mail.dll has of course IMAP support

First remember to enable POP3 in Gmail.

You can read more about Gmail’s POP3 behavior.

Remember that Gmail only allows secure SSL connections, so we need to use ConnectSSL method.

// C# code:

using(Pop3 pop3 = new Pop3())
{
	pop3.ConnectSSL("pop.gmail.com");
	pop3.Login("your_email@gmail.com", "password");

	foreach (string uid in pop3.GetAll())
	{
		var eml = pop3.GetMessageByUID(uid);
		IMail mail= new MailBuilder()
			.CreateFromEml(eml);

		Console.WriteLine(mail.Subject);
		Console.WriteLine(mail.Text);
	}
	pop3.Close();
}

' VB.NET code:

Using pop3 As New Pop3()
    pop3.ConnectSSL("pop.gmail.com")
    pop3.Login("your_email@gmail.com", "password")

    For Each uid As String In pop3.GetAll()
        Dim email As IMail = New MailBuilder() _
            .CreateFromEml(pop3.GetMessageByUID(uid))
        Console.WriteLine(email.Subject)
	Console.WriteLine(mail.Text)
    Next
    pop3.Close()
End Using

If you want to have a greater control over your emails you should use IMAP protocol.

With IMAP you can:

Check POP3 vs IMAP for details.

Mail.dll contains ready to use IMAP client. Take a look on how to download email from Gmail using IMAP sample.

If you like the idea of simple Gmail access just give it a try for yourself and download it at: Mail.dll .NET email component.

Programmatically check Log4Net log

Today I needed to create unit test that checked if the NHibernate query was generated optimally.

Good thing is that, in case of an inefficient query, NHibernate puts a warning using log4net:

log.Warn( "firstResult/maxResults specified with collection fetch; applying in memory!" );

I wanted something like this:

[Test]
public void FindUsersBy_QueryWithLimit_LimitsOnSQLSide()
{
    using (LogChecker logChecker
        = new LogChecker("NHibernate", Level.Warn))
    {
        // Execute query using NHibernate

        // ....

        Assert.IsEmpty(logChecker.Messages);
    }
}

The problem is that it’s not that easy to attach MemoryAppender for a duration of unit test to the specified logger.

Anyway here’s the code:

public class LogChecker : IDisposable
{
    readonly Logger _logger;
    readonly Level _previousLevel;
    readonly MemoryAppender _appender = new MemoryAppender();

    public LogChecker(string logName, Level levelToCheck)
    {
        _logger = (Logger)LogManager.GetLogger(logName).Logger;
        _logger.AddAppender(_appender);
        _previousLevel = _logger.Level;
        _logger.Level = levelToCheck;
    }

    public List<string> Messages
    {
        get
        {
            return new List<loggingEvent>(_appender.GetEvents())
                  .ConvertAll(x => x.RenderedMessage);
        }
    }

    public void Dispose()
    {
        _logger.Level = _previousLevel;
        _logger.RemoveAppender(_appender);
    }
};

Download emails from Gmail

gmail

The task is quite easy with Mail.dll IMAP client. IMAP protocol unlike POP3 stores the unseen information on the server. So all we need to do is connect via SSL/TLS, search for unseen emails, and download them. Mail.dll will perform all the hard work.

First thing to remember is to enable IMAP in Gmail (your authentication options are app-passwords and OAuth 2.0) , second is to use ConnectSSL method, as Gmail allows secure SSL/TLS connections only.

We’ll use Imap.Search(Flag.Unseen) method to download unique ids of all unseen email messages in our inbox. Finally the sample shows how to download such emails and parse them using MailBuilder class. After this it is easy to access any email data like: Subject, Date, From and To collections, plain-text and HTML versions of the email. Also all attachments are downloaded and stored in IMail.Attachments collection.

// C# code:

using(Imap imap = new Imap())
{
	imap.ConnectSSL("imap.gmail.com");
	imap.UseBestLogin("pat@gmail.com", "app-password");

	imap.SelectInbox();

	Lis<long> uids = imap.Search(Flag.Unseen);
	foreach (long uid in uids)
	{
		var eml = imap.GetMessageByUID(uid);
		IMail mail = new MailBuilder().CreateFromEml(eml);

		Console.WriteLine(mail.Subject);
		Console.WriteLine(mail.Text);
	}
	imap.Close();
}
' VB.NET code:

Using imap As New Imap()
	imap.ConnectSSL("imap.gmail.com")
	imap.UseBestLogin("pat@gmail.com", "password")

	imap.SelectInbox()

	Dim uids As List(Of Long) = imap.Search(Flag.Unseen)
	For Each uid As Long In uids
		Dim eml = imap.GetMessageByUID(uid)
		Dim mail As IMail = New MailBuilder().CreateFromEml(eml)

		Console.WriteLine(mail.Subject)
		Console.WriteLine(mail.Text)
	Next
	imap.Close()
End Using

If you like the idea of such simple Gmail access, just give it a try and download Mail.dll .NET IMAP component.

The next step is to learn how to process email attachments.

Wrapper configuration with StructureMap

Recently we have some problems with StructureMap configuration.
We decided to move away from xml files and use registry configuration.

Imagine the following scenario, we have a service class (MyService), and decide
to create simple caching mechanism, by creating a wrapper class (MyServiceWithCaching)

public interface IService
{
	List<string> MyMethod();
}

// Original service
public class MyService : IService
{
	public List<string> MyMethod() { ... }
}

// Service that supports simple caching
public class MyServiceWithCaching : IService
{
	IService _service;
	List<string> _cached;

	// NOTE: We want IService to be injected by IoC container
  	public MyServiceWithCaching(IService service)
  	{
		_service =service;
  	}

	public List<string> MyMethod()
	{
		if (_cached == null)
		{
			// call original when no cached data
			_cached = _service.MyMethod();
		}
		return _cached;
	}
}

The problem is how to tell StructureMap that when somebody asks for IMyService it should get MyServiceWithCaching which should be injected with original MyService class.

It took us some time to figure this out and here’s the solution:

public class IocRegistry : Registry
{
    protected override void configure()
    {
        this.BuildInstancesOf<imyService>()
             .TheDefaultIs(
                Instance<imyService>()
                    .UsingConcreteType<myServiceWithCaching>()
                        .Child<imyService>()
                            .IsConcreteType<myService>()
            );

        base.configure();
    }
} ;

Unit testing is good

Recently Ayende added another spot-the-bug post. Usually I’m just curious how fast people find the problem.

Some time ago he also published few posts about how he does unit testing.

In “Scenario driven tests” he says that, as I understand it, it’s better to test a whole scenario not a single method. I’m okay with that, as long as scenarios are in reasonable numbers and code coverage is high.

In “Tests have got to justify themselves” he states “I am not using TDD.”

Well if you were you would not have so many problems in 10 lines of code, period.
You want to test scenarios ok, but when you’ve simple method with lots of logic, you are going to make a mistake at some point. Test it!

public static string FirstCharacters(this string self, int numOfChars)
{
    if (self == null)
        return "";
    if (self.Length < numOfChars)
        return self;
    return self
        .Replace(Environment.NewLine, " ")
        .Substring(0, numOfChars - 3) + "...";
}

Each of the following tests will fail:
(negative values are not reasonable input for such method, so we won’t go there)

[Test]
public void FirstCharacters_EmptyString_Truncates()
{
    Assert.AreEqual("", "".FirstCharacters(0));
}

[Test]
public void FirstCharacters_RegularString_Truncates()
{
    Assert.AreEqual("12345", "12345".FirstCharacters(5));
}

[Test]
public void Method_Condition_Result()
{
    Assert.AreEqual("...", "12345".FirstCharacters(2));
}

[Test]
public void FirstCharacters_StringWithNewLine_Truncates()
{
    Assert.AreEqual("  ", "rnrnrn".FirstCharacters(2));
}

[Test]
public void FirstCharacters_StringWithNewLine_Truncates2()
{
    Assert.AreEqual(" ...", "nnnnnnnn".FirstCharacters(4));
}

[Test]
public void FirstCharacters_StringWithNewLine_Truncates3()
{
    Assert.AreEqual("start end", "startnend".FirstCharacters(255));
}