В мае делал рассылку для пользователей ПВО. Портал немолодой, у многих пользователей сменился 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"