Click here to Skip to main content
15,895,774 members
Articles / Programming Languages / C#
Tip/Trick

Really Simple POP3 Blacklist Spam Filter in C#

Rate me:
Please Sign up or sign in to vote.
4.00/5 (1 vote)
25 Mar 2012CPOL2 min read 14.9K   451   3  
A really simple POP3 blacklist spam filter in C#.

Output format Example

Introduction

This project is a really simple POP3 email spam filter. It's a C# .NET console application that has been kept as simple as possible. In its current form, it can be used as a simple blacklist spam filter. Or it could be extended to act as a POP3 client to meet your own bespoke requirements.

Background

Suppose you were receiving large quantities of image based spam. And that your client side spam filter insisted on downloading the entire email before applying any filtering. That would slow your email download and consume unnecessary bandwidth. What you would need is a simple bit of code that could log on to your email server, down the email headers, search these against a blacklist, and delete the offending emails from the server. This is exactly what Pop3Filter does.

Using the code

Rather than using a configuration file, Pop3Filter is completely command line driven. Usage is:

Pop3Filter <host name> <port number> <user name> <password> <blacklist...>

E.g.:

Pop3Filter pop3.domain.com 111 user1 password1 viagra refinance

This example will connect to the POP3 server at "pop3.domain.com" on port 111. It will authenticate with username "user1", and password "password1". It will then iterate the headers for each email found on the server, looking for a match with anything on the blacklist. In this case, any email whose header contains either of the terms "viagra" or "refinance" will be deleted from the server.

Download the project and compile with Visual Studio 2010. Rebuild. You will need the host name, port number, user name, and password of your email account, and a list of blacklisted terms. These options can be set in Visual Studio in the usual way (Project / Pop3Filter Properties / Debug / Start Options / Command line arguments), or by creating a Windows shortcut and adding the arguments to the shortcut's Target.

Here is a quick look at the main code:

C#
static void Main(string[] args)
{
    try
    {
        // Check for the minimum number of arguments.
        if (args.Length < 5)
            throw new Exception("Usage is Pop3Filter <host> <port> <username> <password> <blacklist...>");

        // Read the mandatory arguments.
        string host = args[0];
        int port = int.Parse(args[1]);
        string userName = args[2];
        string password = args[3];

        // Load the blacklist.
        string[] blacklist = new string[args.Length - 4];
        Array.Copy(args, 4, blacklist, 0, args.Length - 4);

        // Connect to the server.
        TcpClient tcpClient = new TcpClient();
        tcpClient.Connect(host, port);
        string reply = tcpClient.ReceivePop3Command("\r\n");

        // Authenticate.
        reply = tcpClient.ProcessPop3Command("USER " + userName, "\r\n");
        reply = tcpClient.ProcessPop3Command("PASS " + password, "\r\n");

        // Get the number of messages currently held on the server.
        string[] tokens = tcpClient.ProcessPop3Command("STAT", "\r\n").Split(' ');
        int maxMessageNo = int.Parse(tokens[1]);
        Console.WriteLine("{0} message{1} found.", maxMessageNo, maxMessageNo == 1 ? "" : "s");

        // Iterate the messages.
        for (int messageNo = 1; messageNo <= maxMessageNo; messageNo++)
        {
            // Get the message header.
            string header = tcpClient.ProcessPop3Command("TOP " + 
                                      messageNo + " 0", ".\r\n").ToLower();

            // Parse out and display the header details.
            Console.Write("{0}\t{1}\t{2}\t\"{3}\"",
                          header.Substring("date: ", "\r\n"),
                          header.Substring("from: ", "\r\n").Substring("<", ">", true),
                          header.Substring("to: ", "\r\n").Substring("<", ">", true),
                          header.Substring("subject: ", "\r\n"));

            // Iterate the blacklist.
            foreach (string sender in blacklist)
            {        
                if (header.Contains(sender.ToLower()))
                {
                    // Delete the offending message.
                    reply = tcpClient.ProcessPop3Command("DELE " + messageNo, "\r\n");
                    Console.Write("\tDeleted {0}", sender);
                }
            }
            Console.WriteLine();
        }

        // Sign off.
        reply = tcpClient.ProcessPop3Command("QUIT", "\r\n");
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }

    Console.Write("press any key ...");
    Console.ReadKey();
}

In order to keep the above code clean, we define a few extension methods:

C#
static class Extensions
{
    // Sends a pop3 command.
    public static void SendPop3Command(this TcpClient tcpClient, string message)
    {
        NetworkStream networkStream = tcpClient.GetStream();
        ASCIIEncoding asciiEncoding = new ASCIIEncoding();
        byte[] bytes = asciiEncoding.GetBytes(message);
        networkStream.Write(bytes, 0, bytes.Length);
        Debug.WriteLine(string.Format("Sent \"{0}\"", message.Trim()));
    }

    // Returns the result of a pop3 command.
    public static string ReceivePop3Command(this TcpClient tcpClient, string endOfReply)
    {
        NetworkStream networkStream = tcpClient.GetStream();
        string reply = "";
        while (!reply.EndsWith(endOfReply))
        {
            byte[] bytes = new byte[16384];
            int bytesRead = networkStream.Read(bytes, 0, 16384);

            ASCIIEncoding asciiEncoding = new ASCIIEncoding();
            reply += asciiEncoding.GetString(bytes, 0, bytesRead);
            if (!reply.EndsWith(endOfReply))
                Thread.Sleep(100);
        }
        if (!reply.StartsWith("+"))
            throw new Exception(reply);
        Debug.WriteLine(string.Format("Read \"{0}\"", reply.Trim()));
        return reply;
    }

    // Sends a pop3 command and returns the result.
    public static string ProcessPop3Command(this TcpClient tcpClient, 
                  string command, string endOfReply)
    {
        tcpClient.SendPop3Command(command + "\r\n");
        return tcpClient.ReceivePop3Command(endOfReply);
    }

    // Extracts a substring from a source string bounded by start and end
    // strings. Optionally returns the original string if nothing is found.
    public static string Substring(this string s, string start, 
                  string end, bool defaultToSource = false)
    {
        string result = "";
        int startIndex = s.IndexOf(start);
        int endIndex = -1;
        if (startIndex != -1)
        {
            startIndex += start.Length;
            endIndex = s.IndexOf(end, startIndex);
            if (endIndex != -1 && endIndex > startIndex)
                result = s.Substring(startIndex, endIndex - startIndex);
        }

        if ((startIndex == -1 || endIndex == -1) && defaultToSource)
            result = s;

        return result;
    }
}

Enjoy.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Greenfield Computers
United Kingdom United Kingdom
Steve has been developing software since 1981. He now works for his own company, supplying his skills to clients in the UK.
He likes:
Developing C#, WPF, Real-time applications, Long walks with his wife, kids and a big yellow dog, and warm brown beer with bits floating in it. Not always in that order.

Comments and Discussions

 
-- There are no messages in this forum --