Создание Windows-приложений на основе Visual C#

         

Класс Pop3.csM


Листинг 3.11.

(html, txt)


/// <summary> /// Инициализация класса а установленным по умолчанию портом (110). /// </summary> /// <param name="server">IP адрес сервера.</param> /// <param name="temp">Адрес временной папки.</param> public Pop3(string server, string temp) { _server = server; _port = POP3DefaultPort; TempDirectory = temp; }

/// <summary> /// Инициализация класса . /// </summary> /// <param name="server">IP адрес сервера.</param> /// <param name="port">Номер порта.</param> public Pop3(string server, int port) { _server = server; _port = port; TempDirectory = Path.GetTempPath(); }

/// <summary> /// Инициализация класса . /// </summary> /// <param name="server">IP-адрес сервера.</param> /// <param name="port">Номер порта.</param> /// <param name="temp">Адрес временной папки.</param> public Pop3(string server, int port, string temp) { _server = server; _port = port; TempDirectory = temp; } #endregion

#region Public properties /// <summary> /// Возвращается значение true, если в почтовом ящике есть сообщения. /// </summary> public bool IsMessages { get { return (NumberOfMessages > 0); } }

/// <summary> /// Количество сообщений в ящике. /// </summary> public uint NumberOfMessages { get { // not initialized if (_status == null) { GetStatus(); }

return _status.messages; } } #endregion

#region Method-Property substitution /// <summary> /// Получение количества сообщений. /// </summary> /// <returns></returns> public uint GetNumberOfMessages() { return NumberOfMessages; }

public bool GetIsMessages() { return IsMessages; } #endregion

/// <summary> /// Анализ количества строк, полученных от сервера после отправки команды STAT. /// </summary> /// <example>Команда STAT. В ответ на вызов команды сервер выдает положительный ответ "+OK", /// за которым следует количество сообщений в почтовом ящике и их общий размер в символах. /// Сообщения, которые помечены для удаления, не учитываются в ответе сервера. /// </example> void GetStatus() { CheckConnection();



Send("STAT"); string tmp = Receive();

string [] tokens = tmp.Split(new Char[] {SPACE, CR}, 4);

try { _status = new MaildropStatus( Convert.ToUInt32(tokens[1], 10), Convert.ToUInt32(tokens[2], 10) ); } catch (Exception e) { throw new CoreException("Невозможно проанализировать ответ", e); } } /// <summary> /// Установка соединения с сервером. /// </summary> /// <param name="server">Название сервера.</param> /// <param name="port">Номер порта.</param> void EstablishConnection(string server, int port) { // Получение IP-адреса сервера. IPAddress ipadr = Dns.Resolve(server).AddressList[0]; IPEndPoint ephost = new IPEndPoint(ipadr, port);



// Создание Socket для передачи данных по протоколу TCP. _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

LingerOption linger = new LingerOption(true, 10); _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, linger); // Установка времени ожидания. _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, SendTimeout); _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, ReceiveTimeout);

// Соединение с сервером. _socket.Connect(ephost); if (!_socket.Connected) { throw new CoreException("Сервер не найден: " + server); } }

/// <summary> /// Проверка соединения и авторизации пользователя. /// </summary> void CheckConnection() { if (_socket == null || !_socket.Connected) { throw new CoreException("Соединение не установлено."); }

if (!_authenticated) { throw new CoreException("Пользователь не аутентифицирован (Метод LogIn). "); } } /// <summary> /// Отправка команды на сервер. /// </summary> /// <param name="command">Текст команды.</param> void Send(string command) { // Все команды заканчиваются парой CRLF. command += CRLF; // Сервер работает с кодировкой ASCII. Encoding tmp = Encoding.ASCII; Byte [] buf = tmp.GetBytes(command);



int total = buf.Length; while (total > 0) { total -= _socket.Send(buf, command.Length, SocketFlags.None); } } /// <summary> /// Анализ POP3-строки. /// </summary> /// <param name="str">Строка.</param> void AnalyseResponse(string str) { Trace(str); // Debug.WriteLine(str);

if (str.StartsWith(STAT_ERR)) { string msg; int i = str.IndexOf(CRLF); if (i < 0) { msg = "Ответ сервера: " + STAT_ERR; } else { // Если ответ слишком большой, отсекаем его. msg = str.Substring(STAT_ERR.Length + 1, Math.Min(i - STAT_ERR.Length - 1, 79)); }

throw new ResponseException(msg); } }

/// <summary> /// Получение сообщения в POP3-формате. /// </summary> /// <param name="index">Номер сообщения.</param> /// <returns></returns> public StringReader DumpMessage(int index) { CheckConnection();

Send("RETR " + index); return new StringReader(Receive()); } /// <summary> /// Удаление символов конца сообщения. /// </summary> /// <param name="message">Сообщение.</param> /// <returns></returns> string TruncateTail(string message) { if (!message.EndsWith(FiveOctalTerm)) { Debug.WriteLine("Последние 5 символов: {" + message.Substring(message.Length — 5) + "}"); throw new ResponseException("Неправильные символы конца сообщения."); }

return message.Remove(message.Length — FiveOctalTerm.Length, FiveOctalTerm.Length); }

/// <summary> /// Получение существующих номеров сообщений. /// </summary> /// <returns>Массив с существующими индексами сообщений.</returns> /// <example>Команда LIST. Сервер выдает информацию о всех сообщениях, находящихся в почтовом ящике. /// Сообщения, помеченные для удаления, не перечисляются. /// </example> public uint[] ListMessages() { CheckConnection();

if (_messagelist == null) { Send("LIST"); string tmp = Receive(); tmp = TruncateTail(tmp); int start = tmp.IndexOf(CRLF); if (start > 0) { start += CRLF.Length; ArrayList l = new ArrayList(); Regex r = new Regex(@"\r\n"); string [] list = r.Split(tmp.Substring(start)); if (list.Length > 0) { foreach (string s in list) { string [] f = s.Split(new char [] {' '}, 2); l.Add(Convert.ToUInt32(f[0], 10)); } }



if (l.Count > 0) { _messagelist = (uint [])l.ToArray(typeof(uint)); } else _messagelist = new uint[0]; } else _messagelist = new uint[0]; }

return _messagelist; }

/// <summary> /// Отправляет команду NOOP на сервер. /// </summary> /// <remarks> /// <para>Используется для поддержания сеанса с сервером.</para> /// </remarks> /// <example>Команда NOOP. POP3-сервер ничего не делает и всегда отвечает положительно. /// </example> public void SendNoop() { CheckConnection();

Send("NOOP"); string tmp = Receive(); }

/// <summary> /// Возвращает уникальный идентификатор сообщения. /// </summary> /// <remarks> /// <para> /// Если сообщение помечено на удаление, оно не учитывается. /// </para> /// </remarks> /// <param name="index">Номер сообщения.</param> /// <returns>Уникальный идентификатор пользователя.</returns> public string GetMessageUniqueID(uint index) { CheckConnection();

Send("UIDL " + index); string tmp = Receive();

string [] f = tmp.Split(new char [] {' ', '\r', '\n'}, 4); return f[2]; }

/// <summary> /// Получение заголовка сообщения. /// </summary> /// <param name="index">Сообщение.</param> /// <param name="liens">Количество первых строк.</param> /// <returns>Сообщение с анализированными заголовками.</returns> /// <example>Команда TOP. Если ответ сервера положительный, /// он передает заголовки сообщения и указанное количество строк из тела сообщения. /// </example> public Message GetMessageHeader(uint index, int top) { CheckConnection();

Send("TOP " + index + " " + top); string message = Receive();

message = Utils.RemoveByteStuffedSequence(message); return new Message(this, TruncateTail(message), index); }

/// <summary>Удаление сообщения. /// </summary> /// <param name="index">Номер сообщения.</param> /// <example>Команда DELETE. POP3-сервер помечает указанное сообщение как удаленное, /// но не удаляет его, пока сессия не перейдет в режим UPDATE. /// </example> public override void DeleteMessage(uint index) { CheckConnection();



Send("DELE " + index); string tmp = Receive(); }

/// <summary> /// Получение сообщения. /// </summary> /// <param name="index">Номер сообщения.</param> /// <returns>Сообщение.</returns> /// <example> Команда RETR. После положительного ответа сервер передает содержание сообщения. /// </example> public override Message GetMessage(uint index) { CheckConnection();

Send("RETR " + index); string message = ReceiveMessage();

message = Utils.RemoveByteStuffedSequence(message); return new Message(this, TruncateTail(message), index); }

public void OnRecievedData( IAsyncResult ar ) { }

/// <summary> /// Получение ответа сервера без проверки подлинности. /// </summary> /// <returns>Ответ сервера.</returns> StringBuilder UnsafeReceive() { StringBuilder tmp = new StringBuilder(); Encoding cenc = Encoding.ASCII; IAsyncResult asynResult; byte[] buf = new byte[1024]; int recv = 0; do { asynResult = _socket.BeginReceive(buf, 0, buf.Length, SocketFlags.None, null, null); if (asynResult.AsyncWaitHandle.WaitOne()) { recv = _socket.EndReceive(asynResult); string t = cenc.GetString(buf, 0, recv); tmp.Append(t); if (t.LastIndexOf(FiveOctalTerm) > 0) break; } } while(_socket.Poll(PollTimeout, SelectMode.SelectRead));

return tmp; } /// <summary> /// Получение ответа сервера без проверки подлинности. /// </summary> /// <returns>Ответ сервера.</returns> StringBuilder UnsafeReceiveMessage() { StringBuilder tmp = new StringBuilder(); Encoding cenc = Encoding.ASCII; IAsyncResult asynResult; byte[] buf = new byte[1024]; int recv = 0; do { asynResult = _socket.BeginReceive(buf, 0, buf.Length, SocketFlags.None, null, null); if (asynResult.AsyncWaitHandle.WaitOne()) { recv = _socket.EndReceive(asynResult); string t = cenc.GetString(buf, 0, recv); tmp.Append(t); //if (t.LastIndexOf(FiveOctalTerm) > 0) // break; } } while(!tmp.ToString().EndsWith(FiveOctalTerm));

return tmp; } /// <summary> /// Возвращение ответа сервера. /// </summary> /// <returns>Ответ сервера.</returns> string Receive() { StringBuilder tmp = UnsafeReceive(); string str = tmp.ToString(); AnalyseResponse(str);



return str; } /// <summary> /// Возвращение сообщения в виде строки. /// </summary> /// <returns></returns> string ReceiveMessage() { StringBuilder tmp = UnsafeReceiveMessage(); string str = tmp.ToString(); AnalyseResponse(str);

return str; } /// <summary> /// Аутентификация пользователя. /// </summary> /// <param name="username">Имя пользователя.</param> /// <param name="password">Пароль.</param> /// <example> После установки соединения сервер находится в режиме авторизации пользователя. /// Пользователь должен идентифицировать себя на сервере, используя команды USER и PASS. /// Сначала надо отправить команду USER, после которой в качестве аргумента следует имя пользователя. /// Если сервер отвечает положительно, то теперь необходимо отправить команду PASS, за которой следует пароль. /// <code> /// Client: USER username /// Server: +OK username /// Client: PASS mypass /// Server: +OK username /// </code> /// </example> void AuthenticateYourSelf(string username, string password) { Send("USER " + username); Receive(); Send("PASS " + password); Receive();

_authenticated = true; }

/// <summary> /// Соединение с сервером и аутентификация пользователя. /// </summary> /// <param name="username">Имя пользователя.</param> /// <param name="password">Пароль.</param> public override void LogIn(string username, string password) { try { if (_socket != null) { Quit(); ResetVariables(); } // Установка соеденения. EstablishConnection(_server, _port); Receive(); // Получение приветствия от сервера. AuthenticateYourSelf(username, password); } catch (ShutdownException e) { throw new CoreException("Невозможно завершить предыдущий сеанс.", e); } catch (Exception e) { throw new CoreException("Вход невозможен", e); } }

/// <summary> /// Закрытие транзакции на сервере. /// </summary> public override void Quit() { try { CheckConnection(); // Сервер завершает POP3-сессию и переходит в режим UPDATE. Send("QUIT"); // Ответ нас не интересует } catch (Exception e) { throw new ShutdownException("Невозможно покинуть транзакцию", e); }



CloseSocket(); }

/// <summary> /// Свойство закрытия соединения. /// </summary> void CloseSocket() { try { _socket.Shutdown(SocketShutdown.Both); _socket.Close(); // Свойство 'Connected' установлено в false, когда соединение закрыто. if (_socket.Connected) { throw new CoreException("При закрытии socket возникло исключение: " + Convert.ToString(System.Runtime.InteropServices.Marshal.GetLastWin32Error())); }

_socket = null; } catch (SocketException e) { throw new CoreException("Невозможно закрыть socket", e); } } /// <summary> /// Сброс переменных. /// </summary> void ResetVariables() { _authenticated = false; _status = null; _messagelist = null; }

/// <summary> /// Закрытие сеанса. /// </summary> public override void Dispose() { try { Quit(); ResetVariables(); } catch // Обработчик всех возникших исключений. { Debug.WriteLine("Невозможно закрыть socket"); }

GC.SuppressFinalize(this); } } }

Листинг 3.11.


Содержание раздела