using System.Net.Security; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; namespace MailFromWho { internal class Imap { #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. private static StreamWriter _sw; private static TcpClient _tcpc; private static SslStream _ssl; #pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable. private readonly Settings _settings; public Imap(Settings settings) { _settings = settings; } internal void GetAllAddresses() { try { var path = Environment.CurrentDirectory + $"{Path.DirectorySeparatorChar}{_settings.IntermediateFile}"; var username = _settings.UserName; var imapServer = _settings.ImapServer; var sslPort = _settings.SslPort; var password = string.Empty; while (string.IsNullOrEmpty(password)) { Console.Write($"Enter password of '{username}' @ '{imapServer}': "); var fgColour = Console.ForegroundColor; Console.ForegroundColor = Console.BackgroundColor; password = Console.ReadLine(); Console.ForegroundColor = fgColour; } if (System.IO.File.Exists(path)) { System.IO.File.Delete(path); } _sw = new System.IO.StreamWriter(System.IO.File.Create(path)); // there should be no gap between the imap command and the \r\n // ssl.read() -- while ssl.readbyte!= eof does not work because there is no eof from server // cannot check for \r\n because in case of larger response from server ex:read email message // there are lot of lines so \r \n appears at the end of each line //ssl.timeout sets the underlying tcp connections timeout if the read or write //time out exceeds then the undelying connection is closed _tcpc = new System.Net.Sockets.TcpClient(imapServer, sslPort); _ssl = new System.Net.Security.SslStream(_tcpc.GetStream()); _ssl.ReadTimeout = 200; _ssl.AuthenticateAsClient(imapServer); receiveResponse(); sendRequest("$ LOGIN " + username + " " + password + "\r\n"); receiveResponse(); // Get all folders sendRequest("$ LIST " + "\"\"" + " \"*\"" + "\r\n"); var foldersRaw = receiveResponse(); var matches = Regex.Matches(foldersRaw, @"\s(INBOX.*?)\r\n"); var folders = new List(); foreach (Match match in matches) { folders.Add(match.Groups[1].Value.Replace("\"", "")); } foreach (var folder in folders) { sendRequest($"$ SELECT \"{folder}\"\r\n"); receiveResponse(); sendRequest($"$ STATUS \"{folder}\" (MESSAGES)\r\n"); var statusRaw = receiveResponse(); var regex = new Regex(@"MESSAGES.(\d+)"); var aantalBerichten = int.Parse(regex.Match(statusRaw).Groups[1].Value); var outputStr = $"FOLDER {folder} heeft {aantalBerichten} berichten"; WriteOutput(outputStr, true); sendRequest("$ FETCH 1:* body[header.fields (from)]\r\n"); var fromRaw = receiveResponse(); // From: "flypacificblue.com" //matches = Regex.Matches(fromRaw, "From:\\s\\\"?(.[^\"]*?)\\\"?\\<(.*)\\>\r\n"); matches = Regex.Matches(fromRaw, @"From:\s(.*)\r\n"); var froms = new List(); foreach (Match match in matches) { var addressLine = match.Groups[1].Value; froms.Add(addressLine); WriteOutput(addressLine, true); } WriteOutput("", true); } receiveResponse(); sendRequest("$ LOGOUT\r\n"); receiveResponse(); } catch (Exception ex) { Console.WriteLine("error: " + ex.Message); } finally { if (_sw != null) { _sw.WriteLine(""); _sw.Close(); _sw.Dispose(); } if (_ssl != null) { _ssl.Close(); _ssl.Dispose(); } if (_tcpc != null) { _tcpc.Close(); } } } private void WriteOutput(string outputStr, bool newline) { Console.Write(outputStr); _sw.Write(outputStr); if (newline) { Console.WriteLine(""); _sw.WriteLine(""); } } private void sendRequest(string command) { byte[] dummy; try { if (command != "") { if (_tcpc.Connected) { dummy = Encoding.ASCII.GetBytes(command); _ssl.Write(dummy, 0, dummy.Length); } else { throw new ApplicationException("TCP CONNECTION DISCONNECTED"); } } _ssl.Flush(); } catch (Exception ex) { throw new ApplicationException(ex.Message); } } private string receiveResponse() { var sb = new StringBuilder(); byte[] buffer; try { var bytes = 0; // First wait for some data, but not too long... var waitTime = DateTime.Now.AddMilliseconds(200); while (bytes <= 0 && DateTime.Now < waitTime) { try { buffer = new byte[2048]; bytes = _ssl.Read(buffer, 0, 2048); sb.Append(ReturnCleanASCII(Encoding.ASCII.GetString(buffer, 0, bytes))); } catch (IOException) { bytes = -1; } } while (bytes > 0) { try { buffer = new byte[2048]; bytes = _ssl.Read(buffer, 0, 2048); sb.Append(ReturnCleanASCII(Encoding.ASCII.GetString(buffer, 0, bytes))); } catch (IOException) { bytes = -1; } } } catch (Exception ex) { throw new ApplicationException(ex.Message); } return sb.ToString(); } private string ReturnCleanASCII(string s) { StringBuilder sb = new StringBuilder(s.Length); foreach (char c in s) { int i = (int)c; if (i > 127) // you probably don't want 127 either continue; if (i < 32 && (i != 10 && i != 13)) // I bet you don't want control characters continue; sb.Append(c); } return sb.ToString(); } } }