Автоматизация чтения сообщений из почтового ящика

Блог 27 сентября 2018

В мае делал рассылку для пользователей ПВО. Портал немолодой, у многих пользователей сменился e-mail, почта не доходит. Надо их больше не беспокоить.

В почтовый ящик пришли сообщения о недоставленной почте. Там указан и проблемный адрес. Таких писем более 300. Вручную вычленять адреса трудоемко. Но у нас есть IMAP - "wiki - (англ. Internet Message Access Protocol) — протокол прикладного уровня для доступа к электронной почте. Базируется на транспортном протоколе TCP и использует порт 143." Есть библиотеки, есть примеры кода. Я выбрал второе - поучительнее и знаешь, что происходит под кузовом. Из нескольких примеров слепил простую программу. В основе имеем TCP/IP клиента. Команды описаны в мануале. Клиент соединился с почтовым сервером, авторизовался, послал команду, прочитал ответ, повторил, если надо, и до свидания. Я реализовал типичный сценарий: авторизация, выбор почтового ящика, чтение новых сообщений, сохранение их в файлы, удаление из ящика. А извлекать из файлов мы умеем. Ниже код программы.
using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text.RegularExpressions;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Configuration;

namespace ImapTest
{
    public class ImapSimple
    {
        private TcpClient _imapClient;
        private Stream _imapNs;
        private StreamWriter _imapSw;
        private bool bInit = false;

        /// Imap Command Identifier value:Initial 0
        protected static ushort IMAP_COMMAND_VAL = 0;

        /// Imap command Identified prefix: 
        protected const string IMAP_COMMAND_PREFIX = "abIMAP00";

        /// Imap command identified which is combination of
        /// Imap identifier prefix and val
        /// eg. Prefix:IMAP00, Val: 1
        /// Imap command Identified= IMAP001
        protected string IMAP_COMMAND_IDENTIFIER
        {
            get
            {
                return IMAP_COMMAND_PREFIX + IMAP_COMMAND_VAL.ToString() + " ";
            }

        }

        public ImapSimple()
        {
        }

		//инициализировать соединение с сервером
        public void InitializeConnection(string hostname, int port, bool bSSL)
        {
            try
            {
                _imapClient = new TcpClient(hostname, port);
                if (bSSL)
                {

                    SslStream sslStrm = new SslStream(_imapClient.GetStream(), false, new RemoteCertificateValidationCallback(ValidateServerCertificate), null);
                    //m_SSLStream.ReadTimeout = 30000;

                    try
                    {
                        sslStrm.AuthenticateAsClient(hostname);
                    }
                    catch (AuthenticationException e)
                    {
                        logError(e.ToString());
                        Console.WriteLine("Exception: {0}", e.Message);
                        if (e.InnerException != null)
                        {
                            Console.WriteLine("Inner exception: {0}", e.InnerException.Message);
                        }
                        Console.WriteLine("Authentication failed - closing the connection.");
                        _imapClient.Close();
                        return;
                    }

                    _imapNs = (Stream)sslStrm;
                    _imapSw = new StreamWriter(sslStrm);
                }
                else
                {
                    _imapNs = _imapClient.GetStream();
                    _imapSw = new StreamWriter(_imapNs);
                }

                string res = Response();
                string s = res.Replace("\n", "").Replace("\r", "").Replace("\0", "").Trim();
                Console.WriteLine("*** Connected:" + s);
                bInit = true;
            }
            catch (SocketException ex)
            {
                Console.WriteLine(ex.Message);
                logError(ex.ToString());
                bInit = false;
            }
        }

		//получить ответ от сервера
        private string Response()
        {
            if (_imapClient.ReceiveBufferSize <= 0)
            {
                if (iDebug > 0)
                    logError("Response:EMPTY");
                return "";
            }
            byte[] data = new byte[_imapClient.ReceiveBufferSize];
            int ret = _imapNs.Read(data, 0, data.Length);
            string res = Encoding.ASCII.GetString(data).Replace("\0", "").TrimEnd();
            if (iDebug > 0)
                logError("Response:" + res);
            return res;
        }

		//войти в систему
        public void AuthenticateUser(string username, string password)
        {
            IMAP_COMMAND_VAL++;
            string cmd = IMAP_COMMAND_IDENTIFIER + "LOGIN " + username + " " + password;
            if (iDebug > 0)
                logError("cmd================" + cmd);
            _imapSw.WriteLine(cmd);
            _imapSw.Flush();

            string res = Response();
            string s = res.Replace("\n", "").Replace("\r", "").Replace("\0", "").Trim();
            Console.WriteLine(s);
        }

		//выбрать ящик
        public int MailSelect(string box)
        {
            try
            {
                IMAP_COMMAND_VAL++;
                string cmd = IMAP_COMMAND_IDENTIFIER + "SELECT " + box;
                if (iDebug > 0)
                    logError("cmd================" + cmd);
                _imapSw.WriteLine(cmd);
                _imapSw.Flush();

                string res = Response();
                string s = res.Replace("\n", "").Replace("\r", "").Replace("\0", "").Trim();
                Console.WriteLine(s);
                return 0;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed " + ex.ToString());
                logError(ex.ToString());
            }
            return 0;
        }

		//число непрочитанных сообщений
        public int MailUnreadCount(string box)
        {
            try
            {
                IMAP_COMMAND_VAL++;
                string cmd = IMAP_COMMAND_IDENTIFIER + "STATUS " + box + " (unseen)";
                if (iDebug > 0)
                    logError("cmd================" + cmd);
                _imapSw.WriteLine(cmd);
                _imapSw.Flush();

                string res = Response();
                int k = 0;
                while (res.IndexOf("" + IMAP_COMMAND_IDENTIFIER) < 0 && k < 100)
                {
                    res += Response();
                    k++;
                }

                //Console.WriteLine(res.Replace("\n", "").Replace("\r", "").Replace("\0", "").Trim());
                Match m = Regex.Match(res, "[0-9]*[0-9]");
                int rc = Convert.ToInt32(m.ToString());
                return rc;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed " + ex.ToString());
                logError(ex.ToString());
            }
            return 0;
        }

		//искать непрочитанные сообщения
        public string SearchUnread()
        {
            try
            {
                IMAP_COMMAND_VAL++;
                string cmd = IMAP_COMMAND_IDENTIFIER + "SEARCH (UNSEEN)";
                if (iDebug > 0)
                    logError("cmd================" + cmd);
                _imapSw.WriteLine(cmd);
                _imapSw.Flush();

                string res = Response();
                int k = 0;
                while (res.IndexOf("" + IMAP_COMMAND_IDENTIFIER) < 0 && k < 100)
                {
                    res += Response();
                    k++;
                }
                return res;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed " + ex.ToString());
                logError(ex.ToString());
            }
            return "";
        }

		//получить текст сообщения
        public string GetMessage(int index)
        {
            try
            {
                IMAP_COMMAND_VAL++;
                string cmd = IMAP_COMMAND_IDENTIFIER + "FETCH " + index + " (body[header.fields (from)] body[text])";
                if (iDebug > 0)
                    logError("cmd================" + cmd);
                _imapSw.WriteLine(cmd);
                _imapSw.Flush();

                int k = 0;
                string res = Response();
                while (res.IndexOf("" + IMAP_COMMAND_IDENTIFIER) < 0 && k < 1000)
                {
                    res += Response();
                    k++;
                }
                return res;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed " + ex.ToString());
                logError(ex.ToString());
            }
            return "";
        }

        //получить заголовок сообщения
        public string GetMessageHeader(int index)
        {
            try
            {
                IMAP_COMMAND_VAL++;
                string cmd = IMAP_COMMAND_IDENTIFIER + "FETCH " + index + " (body[header.fields (from)])";// subject date
                if (iDebug > 0)
                    logError("cmd================" + cmd);
                _imapSw.WriteLine(cmd);
                _imapSw.Flush();

                string res = Response();
                return res;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed " + ex.ToString());
                logError(ex.ToString());
            }
            return "";
        }

		//установить флаг
        public int SetFlag(int uid, string flag, int iMode)
        {
            try
            {
                IMAP_COMMAND_VAL++;
                string mdf = " FLAGS";
                if (iMode == 1) mdf = " +FLAGS";
                if (iMode == -1) mdf = " -FLAGS";
                string cmd = IMAP_COMMAND_IDENTIFIER + "STORE " + uid +  mdf + " (" + flag + ")";
                if (iDebug > 0)
                    logError("cmd================" + cmd);
                _imapSw.WriteLine(cmd);
                _imapSw.Flush();

                string res = Response();
                string s = res.Replace("\n", "").Replace("\r", "").Replace("\0", "").Trim();
                Console.WriteLine(s);
                return 0;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Failed " + ex.ToString());
                logError(ex.ToString());
            }
            return 0;
        }

		//закрыть соединение
        public void Disconnect()
        {
            if (_imapSw != null)
                _imapSw.Close();
            if (_imapNs != null)
                _imapNs.Close();
        }

		//проверить сертификат
        public bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
        {
            if (sslPolicyErrors == SslPolicyErrors.None)
                return true;

            Console.WriteLine("Failed to validate Server Certificate. Error: {0}", sslPolicyErrors);

            return false;
        }

        static int timer_ds = 0;
        static int iDebug = 1;
        string author = "";

        static Regex _regex = new Regex(@"From\:(.+?)([a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)", RegexOptions.Singleline);
        //найти автора
		static protected string FindAuthor(string s)
        {
            string s0 = "", s1 = "", s2 = "";
            foreach (Match ItemMatch in _regex.Matches(s))
            {
                s0 = ItemMatch.Groups[0].Value.Trim();
                s1 = ItemMatch.Groups[1].Value.Trim();
                s2 = ItemMatch.Groups[2].Value.Trim();
                break;
            }
            return s2;
        }

		//логировать сообщение
        static void logError(String s)
        {
            bool bResponseFailed = false;
            try
            {
                String sFile = string.Format("ImapTest{0:yyyy-MM-dd}.log", DateTime.Now);
                StreamWriter sw = new StreamWriter(sFile, true, Encoding.UTF8);
                sw.WriteLine(string.Format("{0:HH:mm:ss} {1}", DateTime.Now, s));
                sw.Close();
            }
            catch (Exception se)
            {
                if (bResponseFailed == false)
                {
                    Console.Write(se.Message + "
\n"); } } } //сохранить в файл static void logFile(String s, string sFile) { bool bResponseFailed = false; try { StreamWriter sw = new StreamWriter(sFile, true, Encoding.UTF8); sw.WriteLine(s); sw.Close(); } catch (Exception se) { if (bResponseFailed == false) { Console.Write(se.Message + "
\n"); } } } //типичная обработка public static int SimpleProcess(string host, int port, bool bSSL, string user, string pass, string box, string from, int mode, string savedir) { int nr = 0, kk = 0; string mes = ""; logError("SimpleProcess"); try { ImapSimple im = new ImapSimple(); im.InitializeConnection(host, port, bSSL); if (im.bInit) { im.AuthenticateUser(user, pass); im.MailSelect(box); while (true) { string data = im.SearchUnread(); string[] arr = data.Split('\n'); string[] arr_2 = arr[0].Split(' '); int ipos = arr[0].IndexOf("SEARCH"); for (int i = 0; i < arr_2.Length && ipos >= 0; i++) { if (arr_2[i] == "" || arr_2[i] == "*" || arr_2[i] == "SEARCH") continue; int k = 0; if (!int.TryParse(arr_2[i], out k)) continue; mes = im.GetMessage(k); logError("RAW=" + mes); im.author = FindAuthor(mes); if (im.author == "") { logError("From: email not found!"); continue; } if (!string.IsNullOrEmpty(from) && im.author.IndexOf(from) < 0) { logError("skip from " + im.author); continue; } mes = mes.Replace("=3D", "="); mes = mes.Replace("=\r", ""); mes = mes.Replace("\r", ""); if ((mode & 0x1) > 0) logFile(mes, savedir + k + ".log"); if ((mode & 0x2) > 0) im.SetFlag(k, "\\Deleted", 1); Console.WriteLine("\n, mes=" + mes); } Console.WriteLine((kk++) + ". Press 'q' to cancel, d - debug, r - stop debug"); System.Threading.Thread.Sleep(timer_ds); if (Console.KeyAvailable) { ConsoleKey key = Console.ReadKey(true).Key; if (key == ConsoleKey.D) iDebug = 1; else if (key == ConsoleKey.R) iDebug = 0; else if (key == ConsoleKey.Q) break; } } im.Disconnect(); } } catch (Exception ex) { Console.WriteLine("Failed " + ex.ToString()); logError(ex.ToString()); } return nr; } public static int Main() { string host, user, pass, box, from, savedir, s; int port = 993, mode = 0; bool bSSL = false; host = ConfigurationManager.AppSettings["host"]; user = ConfigurationManager.AppSettings["user"]; pass = ConfigurationManager.AppSettings["pass"]; box = ConfigurationManager.AppSettings["box"]; from = ConfigurationManager.AppSettings["from"]; savedir = ConfigurationManager.AppSettings["savedir"]; if (savedir == null) savedir = ""; s = ConfigurationManager.AppSettings["port"]; if (!string.IsNullOrEmpty(s)) port = int.Parse(s); s = ConfigurationManager.AppSettings["ssl"]; if (!string.IsNullOrEmpty(s) && s == "1") bSSL = true; s = ConfigurationManager.AppSettings["mode"]; if (!string.IsNullOrEmpty(s)) mode = int.Parse(s); s = ConfigurationManager.AppSettings["timer_ds"]; if (!string.IsNullOrEmpty(s)) timer_ds = int.Parse(s); SimpleProcess(host, port, bSSL, user, pass, box, from, mode, savedir); return 0; } } } Пример конфига2 add key="host" value="imap.mail.ru" add key="port" value="993" add key="ssl" value="1" add key="user" value="ххх" add key="pass" value="ууу" add key="box" value="яяя" add key="from" value="postmaster@1gb.ru" add key="savedir" value="C:\Andrei\sana2\utils\imap\imap\bin\Debug\mail\" add key="mode" value="3"/> add key="timer_ds" value="3000"

Теги: IMAP, TCP/IP клиент, SSL, доступ к почтовому серверу


Комментарии

lambada: 28-09-2018 11:56

Полезно. Простор для автоматизации. Уважуха автору.

Добавить комментарий могут только авторизованные пользователи. Авторизоваться
Комментарий

Оценка





Авторизоваться через https://www.pvobr.ru
Логин
Пароль
Регистрация

Авторизоваться через соцсети
Наверх