Skip links

Windows Live Mail API Anti-Virus Example

One of the top uses of our Windows Live Mail API is integrating a vendor antivirus to the Windows Live Mail desktop client. For example, Trend Micro’s Titanium Security Solutions uses it to integrate with Windows Live Mail and recognize viruses. The C# code sample below uses the nClam library to interface with an open source project: Clam AntiVirus.

While integrating Windows Live Mail API with an antivirus engine helps to mitigate risks from e-mails there are obviously many other sources of security risks. Exploits came in many disguises: they can take advantage of vulnerabilities of web browsers or of network services running on your machine. The Deviare Interception Engine can unmask them. It intercepts network APIs and can be used to discover and deactivate malware at the network level, not just within a specific application.

The code can be modified to work with commercial antivirus engines such as those by Symantec, Trend Micro, McAfee, Kaspersky, F-Secure, ESET, AVG, Microsoft, Bitdefender, and GFI. Since the list is long and each of those APIs has different idiosyncrasies, companies such as OPSWAT have developed a unified API. The various “Malware Analysis Online Services” can also be easily integrated to a custom solution.

Code

Prerequisites

  1. Visual Studio 2010
  2. Request a trial for Windows Live Mail API here
  3. Download ClamAV here and VC redistributables if needed
  4. Extract the Clam Anti Virus binaries to a specific directory and create an empty folder called DB there
  5. Run CMD as Administrator, go to the ClamAV directory and run freshclam.exe
  6. Install the ClamAV service by running: clamd –install
  7. Install the ClamAV update service by running: freshclam –install
  8. Download nClam from here and add/modify it as a reference in the project
  9. Get the Windows Live Mail API Anti-Virus example from github

Main.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using Microsoft.Win32;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using NktWLMailApi;
using NktWLMailApiInit;
using NktWLMailStore;
using nClam;

namespace Antivirus
{
    public class Main
    {
        static string pluginsKey = "Software\Nektra\WLMailApi\Plugins";
        static string pluginValueName = "AntivirusDemo";
        static string pluginValueData = "Antivirus.Main";
        private NktWLMailApiInit.WLMailApiInit wlmailApiInit;
        private NktWLMailApi.WLMailApi wlmailApiCore;
        private NktWLMailStore.FolderManager folderManager;
        private NktWLMailStore.MailAccountManager accountManager;
        private Utils utils;
        private nClam.ClamClient clamClient;
        private const string CLAM_HOST = "localhost";
        private const int CLAM_PORT = 3310;

        [ComRegisterFunctionAttribute]
        public static void RegisterFunction(Type t)
        {
            RegistryKey key = Registry.LocalMachine.OpenSubKey(pluginsKey, true);
            if (key == null)
            {
                key = Registry.LocalMachine.CreateSubKey(pluginsKey);
                if (key == null)
                {
                    System.Windows.Forms.MessageBox.Show("Error registering component");

                    return;
                }
            }

            key.SetValue(pluginValueName, pluginValueData);
            key.Close();
        }

        [ComUnregisterFunctionAttribute]
        public static void UnregisterFunction(Type t)
        {
            RegistryKey key = Registry.LocalMachine.OpenSubKey(pluginsKey, true);
            if (key == null)
            {
                return;
            }

            key.DeleteValue(pluginValueName, false);
            key.Close();
        }

        public Main()
        {
            wlmailApiInit = new NktWLMailApiInit.WLMailApiInit();
            wlmailApiInit.OnInit += new NktWLMailApiInit.IWLMailApiInitEvents_OnInitEventHandler(wlmailApiInit_OnInit);
            wlmailApiInit.OnShutdown += new NktWLMailApiInit.IWLMailApiInitEvents_OnShutdownEventHandler(wlmailApiInit_OnShutdown);

        }

        void wlmailApiInit_OnShutdown()
        {
            wlmailApiCore = null;
            wlmailApiInit.OnInit -= new NktWLMailApiInit.IWLMailApiInitEvents_OnInitEventHandler(wlmailApiInit_OnInit);
            wlmailApiInit.OnShutdown -= new NktWLMailApiInit.IWLMailApiInitEvents_OnShutdownEventHandler(wlmailApiInit_OnShutdown);

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        void wlmailApiInit_OnInit()
        {
            wlmailApiCore = new NktWLMailApi.WLMailApi();
            folderManager = new NktWLMailStore.FolderManager();
            accountManager = new NktWLMailStore.MailAccountManager();

            utils = new Utils(wlmailApiCore, folderManager, accountManager);

            this.clamClient = new ClamClient(CLAM_HOST, CLAM_PORT);

            this.wlmailApiCore.OnDatabaseChange += new NktWLMailApi.IWLMailApiEvents_OnDatabaseChangeEventHandler(wlmailApiCore_OnDatabaseChange);
        }

        void wlmailApiCore_OnDatabaseChange(NktWLMailApi.tagDATABASE_TRANSACTION dt, ulong folderId, ulong objId, ulong newParentId)
        {
            var folder = folderManager.GetFolder(folderId);

            if (utils.IsFromQuickViews(folder) || !ShouldBeHandled(dt))
            {
                utils.ReleaseComObject(folder);
                return;
            }

            var message = folder.GetMessage((int)objId);

            switch (dt)
            {
                case tagDATABASE_TRANSACTION.NKT_TR_INSERT_MESSAGE:
                    if (folder.IsSent() == 0 && folder.IsOutbox() == 0)
                    {
                        List<string&rt; virusList = MessageAttachmentsContainViruses(message);

                        if (virusList.Count &rt; 0)
                        {
                            string virusNames = "";
                            foreach (string virusName in virusList)
                            {
                                if (virusNames != "")
                                    virusNames += "," + virusName;
                                else
                                    virusNames = virusName;
                            }
                            MessageBox.Show(string.Format("The message contains the following viruses: {0}", virusNames));
                        }
                    }
                    break;
            }
            utils.ReleaseComObject(message);
            utils.ReleaseComObject(folder);
        }

        private string ClamAntiVirusScanning(string filename) {
            var scanResult = this.clamClient.ScanFileOnServer("c:\users\Admin\Desktop\Temp");

            switch (scanResult.Result)
            {
                case ClamScanResults.Clean:
                    return null;
                    break;

                case ClamScanResults.VirusDetected:
                    return scanResult.InfectedFiles.First().VirusName;
                    break;

                case ClamScanResults.Error:
                    return null;
                    break;
            }

            return null;

        }

        private string GetTemporaryDirectory() // http://stackoverflow.com/questions/278439/creating-a-temporary-directory-in-windows
        {
            string tempDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
            Directory.CreateDirectory(tempDirectory);
            return tempDirectory;
        }

        private List<string&rt; MessageAttachmentsContainViruses(NktWLMailStore.Message message)
        {
            List<string&rt; virusList = new List<string&rt;();

            var attachment = message.GetFirstAttachment();
            string temporaryDirectory = this.GetTemporaryDirectory();

            while (attachment != 0)
            {
                string fullpath = Path.Combine(temporaryDirectory, message.GetFilename(attachment).ToLower() + Path.GetRandomFileName());

                if (message.SaveBodyToFile(attachment, fullpath, 0) == 0)
                {
                    utils.ShowMsgBox("An error occurred while saving message attachments.");

                    break;
                }

                string scanResultString = ClamAntiVirusScanning(fullpath);
                if (scanResultString != null)
                {
                    virusList.Add(scanResultString);
                }

                attachment = message.GetNextAttachment();
            }

            utils.ReleaseComObject(message);

            return virusList;
        }        

        private bool ShouldBeHandled(tagDATABASE_TRANSACTION dt)
        {
            return (dt == tagDATABASE_TRANSACTION.NKT_TR_INSERT_MESSAGE ||
                    dt == tagDATABASE_TRANSACTION.NKT_TR_DELETE_MESSAGE ||
                    dt == tagDATABASE_TRANSACTION.NKT_TR_RENAME_FOLDER ||
                    dt == tagDATABASE_TRANSACTION.NKT_TR_DELETE_FOLDER);
        }
    }

    // Do not pay attention to this class.
    public class WindowWrapper : IWin32Window
    {
        public WindowWrapper(IntPtr handle)
        {
            _hwnd = handle;
        }

        public IntPtr Handle
        {
            get { return _hwnd; }
        }

        private readonly IntPtr _hwnd;
    }
}

Utils.cs

using System;
using System.Globalization;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using NktWLMailApi;
using NktWLMailApiInit;
using NktWLMailStore;
using System.Windows.Forms;
using Message = NktWLMailStore.Message;

namespace Antivirus
{
    public class Utils
    {
        private WLMailApi _wlmailApiCore;
        private FolderManager _folderManager;
        private MailAccountManager _accountManager;

        private const ulong QuickViewsFolderID = 7;

        public Utils(WLMailApi wlmailApiCore, FolderManager folderManager, MailAccountManager accountManager)
        {
            _wlmailApiCore = wlmailApiCore;
            _folderManager = folderManager;
            _accountManager = accountManager;
        }

        public void ShowMsgBox(string text)
        {
            ShowMsgBoxHwnd(_wlmailApiCore.GetMainWindow(), text);
        }

        public void ShowMsgBox(IntPtr ownerWindow, string text)
        {
            ShowMsgBoxHwnd((int)ownerWindow, text);
        }

        private static void ShowMsgBoxHwnd(int hwnd, string text)
        {
            MessageBox.Show(new WindowWrapper((IntPtr)hwnd), text, @"WLMailApi", MessageBoxButtons.OK,
                            MessageBoxIcon.Information);
        }

        public Message GetFirstSelectedMessage()
        {
            var folder = GetCurrentFolder();

            var messageID = _wlmailApiCore.GetFirstSelectedMessageID();

            var message = folder.GetMessage(messageID);

            ReleaseComObject(folder);

            return message.GetID() == -1 ? null : message;
        }

        public Message GetNextSelectedMessage()
        {
            var folder = GetCurrentFolder();

            var messageID = _wlmailApiCore.GetNextSelectedMessageID();

            var message = folder.GetMessage(messageID);

            ReleaseComObject(folder);

            return message.GetID() == -1 ? null : message;
        }

        public Folder GetCurrentFolder()
        {
            var folderId = (ulong)_wlmailApiCore.GetSelectedFolderID();
            return _folderManager.GetFolder(folderId);
        }

        public bool IsFromQuickViews(Folder folder)
        {
            var rootFolder = _folderManager.GetFolder((int)tagSPECIALFOLDERID.NKT_FOLDER_ROOT);
            var rootFolderId = rootFolder.GetID();
            ReleaseComObject(rootFolder);
            rootFolder = null;

            var childrenIds = GetChildrenIds((int)rootFolderId);

            var quickViewsFoldersIds = new List<int&rt;();

            foreach (var childId in childrenIds)
            {
                var child = _folderManager.GetFolder((ulong)childId);
                if (child.GetID() == QuickViewsFolderID)
                {
                    quickViewsFoldersIds = GetChildrenIds(childId);
                    ReleaseComObject(child);
                    break;
                }
                ReleaseComObject(child);
            }

            return quickViewsFoldersIds.Contains((int)folder.GetID());
        }

        private List<int&rt; GetChildrenIds(int folderId)
        {
            var folder = _folderManager.GetFolder((ulong)folderId);

            var childFolder = folder.GetFirstChild();

            var childrenIds = new List<int&rt;();

            while (childFolder != null)
            {
                childrenIds.Add((int)childFolder.GetID());
                ReleaseComObject(childFolder);
                childFolder = folder.GetNextChild();
            }

            childFolder = null;

            ReleaseComObject(folder);
            folder = null;

            return childrenIds;
        }

        public int CommitIfNotInQuickViews(Message msg)
        {
            var folder = _folderManager.GetFolder(msg.GetFolderID());

            if (IsFromQuickViews(folder))
            {
                ShowMsgBox("You can't save nor modify a message " +
                           "in a Quick Views folder.n");
                return 0;
            }

            ReleaseComObject(folder);
            folder = null;

            return msg.Commit();

        }

        public int MoveIfNotInQuickViews(Message msg, int destFolderId)
        {
            var folder = _folderManager.GetFolder(msg.GetFolderID());

            if (IsFromQuickViews(folder))
            {
                ShowMsgBox("You can't move a message you selected " +
                            "from a Quick Views folder.n");
                return 0;
            }

            ReleaseComObject(folder);
            folder = null;

            return _folderManager.MoveMessage(msg.GetFolderID(), (ulong)destFolderId, msg.GetID());
        }

        public void CleanWLMailApiReferences()
        {
            _wlmailApiCore = null;
            _folderManager = null;
            _accountManager = null;
        }

        public int ReleaseComObject(object obj)
        {
            var remainingRefs = -1;

            if (obj != null)
            {
                remainingRefs = Marshal.ReleaseComObject(obj);
                obj = null;
            }

            return remainingRefs;
        }

        public class RegexUtils
        {
            static bool _invalid;

            public static bool IsValidEmail(string strIn)
            {
                _invalid = false;
                if (String.IsNullOrEmpty(strIn))
                    return false;

                // Use IdnMapping class to convert Unicode domain names.
                strIn = Regex.Replace(strIn, @"(@)(.+)$", DomainMapper);
                if (_invalid)
                    return false;

                // Return true if strIn is in valid e-mail format.
                return Regex.IsMatch(strIn,
                       @"^(?("")(""[^""]+?""@)|(([0-9a-z]((.(?!.))|[-!#$%&'*+/=?^`{}|~w])*)(?<=[0-9a-z])@))" +
                       @"(?([)([(d{1,3}.){3}d{1,3}])|(([0-9a-z][-w]*[0-9a-z]*.)+[a-z0-9]{2,17}))$",
                       RegexOptions.IgnoreCase);
            }

            private static string DomainMapper(Match match)
            {
                // IdnMapping class with default property values.
                var idn = new IdnMapping();

                string domainName = match.Groups[2].Value;
                try
                {
                    domainName = idn.GetAscii(domainName);
                }
                catch (ArgumentException)
                {
                    _invalid = true;
                }
                return match.Groups[1].Value + domainName;
            }
        }

    }
}

Related Services

  1. Windows driver development
  2. Office 365 Plugin Development
  3. Custom Software Development Company
  4. Data Loss Prevention Solution Development

Leave a comment

This website uses cookies to improve your web experience.
Home
Account
Cart
Search