using System;
using System.Text;
using ComponentPro.Net;
using ComponentPro.Net.Mail;
using ComponentPro;
using System.Web.UI.WebControls;
using System.IO;

/// <summary>
/// Contains information for sending notification to the administrator.
/// </summary>
public class NotificationInfo
{
    public string MailFrom; // Send from.
    public string MailTo; // Who will receive the notification email.
    public string SmtpServer; // SMTP server address.
    public int SmtpPort; // SMTP server port (default is 25).
    public string SmtpUser; // SMTP credentials.
    public string SmtpPassword; // SMTP credentials.
    public SecurityMode SecurityMode; // Implicit, Explicit or Unsecure
    public SmtpAuthenticationMethod AuthenticationMethod; // SMTP Authentication method.
}

public partial class _Default : System.Web.UI.Page
{
    void PopulateEnum(Type enumType, DropDownList ddl)
    {
        Array arr = Enum.GetValues(enumType);

        foreach (object item in arr)
        {
            ddl.Items.Add(new ListItem(item.ToString(), ((int)item).ToString()));
        }

        int index;
        if (int.TryParse(Request.Form[ddl.ID], out index))
            ddl.SelectedIndex = index;
    }
    
    protected void Page_Load(object sender, EventArgs e)
    {
        PopulateEnum(typeof(SmtpAuthenticationMethod), ddlSmtpAuth);
        PopulateEnum(typeof(ImapAuthenticationMethod), ddlAuthentication);
    }

    protected void btnSmtp_Click(object sender, EventArgs e)
    {
        try
        {
            // Setup the notification info.
            NotificationInfo info = new NotificationInfo();
            info.MailFrom = txtFrom.Text;
            info.MailTo = txtTo.Text;
            info.SmtpServer = txtSmtpServer.Text;
            info.SmtpPort = int.Parse(txtSmtpPort.Text);
            info.SmtpUser = txtSmtpUser.Text;
            info.SmtpPassword = txtSmtpPassword.Text;
            info.AuthenticationMethod = (SmtpAuthenticationMethod)int.Parse(ddlSmtpAuth.SelectedValue);
            info.SecurityMode = (SecurityMode)ddlSmtpSec.SelectedIndex;

            // Send a test message.
            SendMail(info, "Test Subject", "Test SMTP for BounceInspector");
            // Show result.
            lblResult.Text = "Test message has been sent to " + info.MailTo;
        }
        catch (Exception exc)
        {
            // An error occurred.
            lblResult.Text = exc.Message;
        }
    }

    protected void btnProcess_Click(object sender, EventArgs e)
    {
        try
        {
            // Setup the directory for storing messages.
            string mailsPath = AppDomain.CurrentDomain.BaseDirectory + "Mails";
            if (!System.IO.Directory.Exists(mailsPath))
                System.IO.Directory.CreateDirectory(mailsPath);

            // Initialize the message string.
            StringBuilder sb = new StringBuilder();
            // Parse the IMAP port.
            int port = int.Parse(txtPort.Text);

            Imap client = new Imap();

            // Connect to the server.
            client.Connect(txtServer.Text, port, (SecurityMode)ddlSecurityMode.SelectedIndex);

            // Login to the server.
            ImapAuthenticationMethod ia = (ImapAuthenticationMethod)int.Parse(ddlAuthentication.SelectedValue);
            // Login to the IMAP server.
            client.Authenticate(txtUser.Text, txtPassword.Text, ia);

            // Selecting folder is needed.
            client.Select(txtFolder.Text);

            // Create a new instance of the BounceInspector class for detecting and filtering bounced messages.
            BounceInspector filter = new BounceInspector();
            // Delete hard bounce file?
            filter.AllowDelete = chkDelete.Checked;
            // Delete bard bounce in inbox as well?
            filter.AllowInboxDelete = chkDelete.Checked;
            // The maximum number of message to be downloaded.
            filter.MaxMessages = int.Parse(txtMessages.Text);

            // Register an event handler when messages are processed.
            filter.ProcessMessagesCompleted += filter_ProcessMessagesCompleted;

            // Setup the notification info.
            NotificationInfo info = new NotificationInfo();
            info.MailFrom = txtFrom.Text;
            info.MailTo = txtTo.Text;
            info.SmtpServer = txtSmtpServer.Text;
            info.SmtpPort = int.Parse(txtSmtpPort.Text);
            info.SmtpUser = txtSmtpUser.Text;
            info.SmtpPassword = txtSmtpPassword.Text;
            info.AuthenticationMethod = (SmtpAuthenticationMethod)int.Parse(ddlSmtpAuth.SelectedValue);
            info.SecurityMode = (SecurityMode)ddlSmtpSec.SelectedIndex;

            // Get the newest messages only.
            ImapMessageCollection list = client.ListMessages(ImapEnvelopeParts.MessageInboxIndex | ImapEnvelopeParts.UniqueId);
            ImapMessageCollection newList = new ImapMessageCollection();
            for (int i = 0; i < filter.MaxMessages; i++)
                newList.Add(list[list.Count - i - 1]);

            // Start a new thread to process messages. 
            // You do not have to care about this. The BounceInspector will do the job for you.
            // Asynchronously process messages. The method filter_ProcessMessagesCompleted will be invoked when messages are processed.
            filter.ProcessMessagesAsync(client, txtFolder.Text, newList, mailsPath, info);

            // Show result.
            lblResult.Text = "The bounce processing has begun. You will receive a notification via e-mail when it is completed.";
        }
        catch (Exception exc)
        {
            lblResult.Text = string.Format(null, "Exception: {0}", exc.Message);
        }
    }

    void SendMail(NotificationInfo info, string subject, string content)
    {
        // Create a new instance of the SmtpClient for sending notification email.
        Smtp client = new Smtp();

        // Connect to the specified SMTP server.
        client.Connect(info.SmtpServer, info.SmtpPort, info.SecurityMode);

        if (!string.IsNullOrEmpty(info.SmtpUser))
            // Login.
            client.Authenticate(info.SmtpUser, info.SmtpPassword);

        // Prepare a notification message.
        MailMessage msg = new MailMessage();
        msg.From.Add(info.MailFrom);
        msg.To.Add(info.MailTo);
        msg.Subject = subject;
        msg.BodyHtml = content;
        // Send it.
        client.Send(msg);

        // Close the SMTP session.
        client.Disconnect();
    }

    void LogError(NotificationInfo info, Exception exc)
    {
        try
        {
            // Send error information.
            SendMail(info, "Bounce processing completed with an exception", exc.Message);
        }
        catch (Exception exc2)
        {
            // Write to a log file when SMTP sending has a problem.
            StreamWriter s = new StreamWriter(AppDomain.CurrentDomain.BaseDirectory + "Error.log");
            s.WriteLine(exc2.Message);
            s.Close();
        }
    }

    void filter_ProcessMessagesCompleted(object sender, ExtendedAsyncCompletedEventArgs<BounceResultCollection> e)
    {
        // Get the state object.
        NotificationInfo info = (NotificationInfo)e.UserState;

        if (e.Error != null)
        {
            LogError(info, e.Error);
            return;
        }        

        // sender is the filter instance that we have created before.
        BounceInspector filter = (BounceInspector)sender;

        try
        {
            // Retrieve a collection of BounceResult class.
            BounceResultCollection result = e.Result;

            // Init a StringBuilder object for creating the notification message body.
            StringBuilder sb = new StringBuilder();

            // Process the result.
            foreach (BounceResult r in result)
            {
                // If the processing message is a bounced message.
                if (r.Identified)
                {
                    // Add to the result string.
                    sb.AppendFormat("FileName: {0}<br>Subject: {1}<br>" +
                                               "Address: {2}<br>" +
                                               "Bounce Category Name: {3}<br>" +
                                               "Bounce Type: {4}<br>" +
                                               "Deleted: {5}<br>" +
                                               "DSN-Action: {6}<br>" +
                                               "DSN-DiagnosticCode: {7}<br>" + 
                                               "Received: {8}<br><br>",
                                    System.IO.Path.GetFileName(r.FilePath),
                                    r.MailMessage.Subject,
                                    r.Addresses.Length > 0 ? r.Addresses[0] : string.Empty,
                                    r.BounceCategory.Name,
                                    r.BounceType.Name,
                                    r.FileDeleted.ToString(),
                                    r.Dsn.Action.ToString(),
                                    r.Dsn.DiagnosticCode,
                                    r.MailMessage.ReceivedDate
                                    );
                }
            }

            if (sb.Length == 0)
                sb.Append("No bounced messages found. You can extend the search range to scan more messages.");

            // Send the notification to the specified recipient.
            SendMail(info, "Bounce processing completed successfully", sb.ToString());
        }
        catch (Exception exc)
        {
            LogError(info, exc);
        }
    }
}