|
|
@@ -0,0 +1,232 @@
|
|
|
+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<string>();
|
|
|
+ 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" <reservations@flypacificblue.com>
|
|
|
+ //matches = Regex.Matches(fromRaw, "From:\\s\\\"?(.[^\"]*?)\\\"?\\<(.*)\\>\r\n");
|
|
|
+ matches = Regex.Matches(fromRaw, @"From:\s(.*)\r\n");
|
|
|
+ var froms = new List<string>();
|
|
|
+ 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();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|