Search IIS SMTP log file for distinct "from" address' and count

G-Oker

Member
Joined
Jan 21, 2021
Messages
8
Programming Experience
1-3
Hi, I am trying to create a program to read through IIS logs and count the unique "FROM" address, and count how many emails that address has sent. I have done a horrible version of it, based on knowing the emails in the file already, but I am trying to polish this up now but getting reading the file for address, and then matching. on that. I can get ALL email address, but I just need to get the "from" one, and then match them to "0 MAIL - +FROM:<(+ email address)>" .
Can someone please advise on how I can refine my program to a) search for all distinct FROM address', and then count home may times those addresses appear in the file?
C#:
using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
  
    using System;
    using System.IO;
    using System.Text;
    using System.Text.RegularExpressions;
  
    namespace IISemailCount
        {
        public partial class Form1 : Form
            {
            string fname = "", EoE = "25 QUIT - - 0 0 4 0", DOM1 = "0 MAIL - +FROM:<[EMAIL]address1@domain.com[/EMAIL]>", DOM2 = "0 MAIL - +FROM:<[EMAIL]address2@domain.com[/EMAIL]>",
        DOM2 = "0 MAIL - +FROM:<[EMAIL]somethingelse@anotherdomain.co.uk[/EMAIL]>",etc;
  
            private void button2_Click_1(object sender, EventArgs e)
                {
                // read data
                string input = File.ReadAllText(fname);
                //Console.Write(input);
  
                // email address pattern
                const string Pattern =
                   @"(([\w-]+\.)+[\w-]+|([a-zA-Z]{1}|[\w-]{2,}))@"
                   + @"((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\."
                     + @"([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|"
                   + @"([a-zA-Z]+[\w-]+\.)+[a-zA-Z]{2,4})";
  
                // set up regex instance with options
                Regex emailPattern = new Regex(Pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);
  
                // perform match
                MatchCollection emailMatches = emailPattern.Matches(input);
  
                // set up our string builder
                StringBuilder sb = new StringBuilder();
  
                // build the list
                mainWindow.AppendText("---EXTRACTED EMAIL ADDRESSES---\n\n");
                foreach (Match emailMatch in emailMatches)
                    {
                    // add address to builder
                    sb.AppendLine(emailMatch.Value);
  
                    // display to console
                    mainWindow.AppendText("\n\n"+ emailMatch.Value);
  
                    }
  
                }
  
            private void button1_Click_1(object sender, EventArgs e)
                {
                if (searchParam.Text != "")
                    {
                    if (fname == "")
                        {
                        MessageBox.Show("No file selected.\nClick the LOAD button to select a file first.");
                        }
                    else
                        {
                        foreach (string line in File.ReadLines(fname))
                            {
                            if (line.Contains(searchParam.Text.ToString()))
                                {
                                mainWindow.AppendText(line + Environment.NewLine);
                                }
                            }
                        }
                    }
                else { MessageBox.Show("Search box empty"); }
                        }
  
                public Form1()
                {
                InitializeComponent();
                }
  
            private void button1_Click(object sender, EventArgs e)
                {
  
  
  
  
  
                int eoe =0, runningTotal = 0, totalcounter = 0, address1Counter = 0, address2Counter = 0, address3Counter = 0, etc;
  
                if (fname == "")
                    {
                    MessageBox.Show("No file selected.\nClick the LOAD button to select a file first.");
                    }
                else
                    {
                    foreach (string line in File.ReadLines(fname))
                        {
                        if (line.Contains(eoe))
                            {
                            totalcounter++;
                            }
  
                        if (line.Contains(DOM1))
                            {
                            address1Counter++;
                            }
                        else
  
                        if (line.Contains(DOM2))
                            {
                            address2Counter++;
                            }
                        else
  
                        if (line.Contains(DO3))
                            {
                            address3Counter++;
                            }
                        etc
                        }
  
                    //label3.Text = runningTotal + " e-Mails counted";
                    if (address1Counter > 0) { mainWindow.AppendText(address1Counter + " address1 address." + Environment.NewLine); runningTotal = runningTotal + address1Counter; }
                    if (address2Counter > 0) { mainWindow.AppendText(address2Counter + " address2 address" + Environment.NewLine); runningTotal = runningTotal + address2Counter; }
                   etc
                    }
                label3.Text = runningTotal + " e-Mails counted";
                }
  
            private void button2_Click(object sender, EventArgs e)
                {
                if (mainWindow.TextLength == 0)
                    {
                    MessageBox.Show("No content to copy");
                    }
                else
                    {
                    StringBuilder sb = new StringBuilder();
  
                    foreach (string line in mainWindow.Lines)
                        sb.AppendLine(line);
                    sb.AppendLine(label3.Text);
                    Clipboard.SetText(sb.ToString());
                    MessageBox.Show("Main windows content copied to clipboard");
                    }
                }
  
            private void button3_Click(object sender, EventArgs e)
                {
                OpenFileDialog dialog = new OpenFileDialog();
                dialog.Filter =
                   "log files (*.log)|*.log|All files (*.*)|*.*";
                dialog.InitialDirectory = "C:\\";
                dialog.Title = "Select a log file";
                if (dialog.ShowDialog() == DialogResult.OK)
                    {
                    fname = dialog.FileName;
                    //richTextBox1.Text = System.IO.File.ReadAllText(fname);
  
                    label2.Text = Path.GetFileNameWithoutExtension(fname);
                    }
                }
            }
        }

Thank you
 
Last edited by a moderator:
Solution
Simply use a dictionary. It looks like you have have a regular expression that recognizes any email address. Furthermore, you have a regular expression that recognized a "FROM" log entry for a specific email. So you just combine the two regular expressions so that you can recognize a FROM log entry from any any email email address. For each match that is found, you increment the count for that email address within the dictionary. If the key doesn't exist yet for that email address, then add it to the dictionary with an initial count of 1.

If you don't want to manage the counts in your own dictionary, then just generate a list of all the email addresses, and use LINQ's GroupBy() method to group them, and give you the size...

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
3,175
Location
Chesapeake, VA
Programming Experience
10+
Simply use a dictionary. It looks like you have have a regular expression that recognizes any email address. Furthermore, you have a regular expression that recognized a "FROM" log entry for a specific email. So you just combine the two regular expressions so that you can recognize a FROM log entry from any any email email address. For each match that is found, you increment the count for that email address within the dictionary. If the key doesn't exist yet for that email address, then add it to the dictionary with an initial count of 1.

If you don't want to manage the counts in your own dictionary, then just generate a list of all the email addresses, and use LINQ's GroupBy() method to group them, and give you the size of each group.
 
Solution

G-Oker

Member
Joined
Jan 21, 2021
Messages
8
Programming Experience
1-3
THANK YOU Skydiver. Dictionary was a great idea, now works some much better.
C#:
                        string fname = "test.log", SentEmail = "+FROM:<";
                        int totalsentcounter = 0, countedEmails =0;
        
                foreach (string line in File.ReadLines(fname))
                    {
                    //If the line contains +FROM:< then we want to count it, and interrogate it some more
                    if (line.Contains(SentEmail))
                        {
                        totalsentcounter++;

                        const string Pattern =
   @"(([\w-]+\.)+[\w-]+|([a-zA-Z]{1}|[\w-]{2,}))@"
   + @"((([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\."
     + @"([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-1]?[0-9]{1,2}|25[0-5]|2[0-4][0-9])){1}|"
   + @"([a-zA-Z]+[\w-]+\.)+[a-zA-Z]{2,4})";

                        // set up regex instance with options
                        Regex emailPattern = new Regex(Pattern, RegexOptions.Compiled | RegexOptions.IgnoreCase);

                        // perform match
                        MatchCollection emailMatches = emailPattern.Matches(line);

                        // set up our string builder
                        StringBuilder sb = new StringBuilder();

                        // build the list
                        foreach (Match emailMatch in emailMatches)
                            {
                            // add address to builder
                            sb.AppendLine(emailMatch.Value);

                            // add to dictonary
                            if (uniqueAddressList.ContainsKey(emailMatch.ToString()))
                                {
                                // if already exists in dictionary, add + to the count
                                uniqueAddressList[emailMatch.ToString()]++;
                                }
                            else { //else, add to dictionary, and assign a value of 1
                                uniqueAddressList.Add(emailMatch.ToString(), 1); }
                            }
                        }
                    }
                mainWindow.Clear();
                mainWindow.AppendText("---Finished counting emails in " + fname+"---" + Environment.NewLine + Environment.NewLine);

                //No of lines that matched "+FROM:<" string
                label3.Text = totalsentcounter + " sent e-Mails counted";

                //Content of Dictionary
                //order by descending value ( Highest at top )
                foreach (KeyValuePair<string, Int16> address in uniqueAddressList.OrderByDescending(key => key.Value))
                    {
                    mainWindow.AppendText("Address: "+address.Key+" Total sent:"+address.Value + Environment.NewLine);
                    }

                //add Dictionary values together, so we can match "lines" count with total number of emails in dictionary count. 
                uniqueAddressList.ToList().ForEach(x => countedEmails = countedEmails + x.Value );
                mainWindow.AppendText(Environment.NewLine + countedEmails + " emails counted");
 
Last edited by a moderator:

JohnH

C# Forum Moderator
Staff member
Joined
Apr 23, 2011
Messages
1,184
Location
Norway
Programming Experience
10+
Do this when posting code, not "quote":
insertcode.png
 
Top Bottom