/// <summary> /// Process a transmitted message to import any signing certificates for subsequent S/MIME encryption. /// </summary> /// <param name="o">A ProcessMessageArguments object containing message parameters.</param> private void ProcessMessage(object o) { ProcessMessageArguments arguments = (ProcessMessageArguments)o; // Export the message to a local directory. if (!string.IsNullOrEmpty(arguments.ExportDirectory)) { string messageId = Functions.ReturnBetween(arguments.MessageText.ToLower(), "message-id: <", ">"); if (string.IsNullOrEmpty(messageId)) { messageId = Guid.NewGuid().ToString(); } string fileName = ProxyFunctions.GetExportFileName(arguments.ExportDirectory, messageId, arguments.InstanceId, arguments.UserName); File.WriteAllText(fileName, arguments.MessageText); } // Only parse the message if it contains a known S/MIME content type. string canonicalMessageText = arguments.MessageText.ToLower(); if (canonicalMessageText.IndexOf("application/x-pkcs7-signature") > -1 || canonicalMessageText.IndexOf("application/pkcs7-mime") > -1) { try { // Parse the message. MailMessage message = new MailMessage(arguments.MessageText); // If the message contains a signing certificate that we haven't processed on this session, import it. foreach (X509Certificate2 cert in message.SmimeSigningCertificateChain) { if (cert != null && !SmimeCertificatesReceived.Contains(cert)) { // Import the certificate to the Local Machine store. ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Importing certificate with Serial Number {" + cert.SerialNumber + "}.", Proxy.LogLevel.Information, LogLevel); CertHelper.InstallWindowsCertificate(cert, StoreLocation.LocalMachine); // Remember this ceriticate to avoid importing it again this session. SmimeCertificatesReceived.Add(cert); } } } catch (Exception ex) { if (arguments.DebugMode || System.Diagnostics.Debugger.IsAttached) { ProxyFunctions.Log(LogWriter, SessionId, "Exception while processing message: " + ex.ToString(), Proxy.LogLevel.Error, LogLevel); } else { ProxyFunctions.Log(LogWriter, SessionId, "Exception while processing message: " + ex.Message, Proxy.LogLevel.Error, LogLevel); } } } }
/// <summary> /// Relay data read from one connection to another. /// </summary> /// <param name="o">A TransmitArguments object containing local and remote server parameters.</param> private async void RelayData(object o) { // Cast the passed-in parameters back to their original objects. TransmitArguments arguments = (TransmitArguments)o; Stream clientStream = arguments.ClientStream; Stream remoteServerStream = arguments.RemoteServerStream; // A byte array to streamline bit shuffling. char[] buffer = new char[Constants.SMALLBUFFERSIZE]; // Placeholder variables to track the current message being transmitted. StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE); // The overall number of bytes transmitted on this connection. ulong bytesTransmitted = 0; if (arguments.Credential != null) { UserName = arguments.Credential.UserName; } bool stillReceiving = true; try { using (StreamReader clientStreamReader = new StreamReader(clientStream)) { using (StreamWriter remoteServerStreamWriter = new StreamWriter(remoteServerStream)) { remoteServerStreamWriter.AutoFlush = true; while (Started && stillReceiving) { // Read data from the source and send it to its destination. string stringRead = await clientStreamReader.ReadLineAsync(); if (stringRead != null) { int bytesRead = stringRead.Length; bytesTransmitted += (ulong)bytesRead; messageBuilder.AppendLine(stringRead); // If this data comes from the client, log it. Otherwise, process it. if (arguments.IsClient) { bool messageRelayed = false; string[] commandParts = stringRead.Substring(0, stringRead.Length).Split(new char[] { ' ' }, 2); // Optionally replace credentials with those from our settings file. if (commandParts.Length == 2) { if (commandParts[0] == "USER") { if (arguments.Credential != null) { await remoteServerStreamWriter.WriteLineAsync("USER " + arguments.Credential.UserName); ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: USER " + arguments.Credential.UserName, Proxy.LogLevel.Raw, LogLevel); messageRelayed = true; } else { UserName = commandParts[1]; } } else if (arguments.Credential != null && commandParts[0] == "PASS") { await remoteServerStreamWriter.WriteLineAsync("PASS" + arguments.Credential.Password); ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: PASS " + arguments.Credential.Password, Proxy.LogLevel.Raw, LogLevel); messageRelayed = true; } } if (LogLevel == Proxy.LogLevel.Verbose) { ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "Command {" + commandParts[0] + "} processed.", Proxy.LogLevel.Verbose, LogLevel); } if (!messageRelayed) { await remoteServerStreamWriter.WriteLineAsync(stringRead); ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + stringRead, Proxy.LogLevel.Raw, LogLevel); } } else { await remoteServerStreamWriter.WriteLineAsync(stringRead); ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "S: " + stringRead, Proxy.LogLevel.Raw, LogLevel); // If we see a period between two linebreaks, treat it as the end of a message. if (stringRead == ".") { string message = messageBuilder.ToString(); int endPos = message.IndexOf("\r\n.\r\n"); if (message.Contains("\r\n\r\n")) { int lastOkPos = message.LastIndexOf("+OK", endPos); if (lastOkPos > -1) { message = message.Substring(message.IndexOf("\r\n", lastOkPos) + 2); if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1 || !string.IsNullOrEmpty(arguments.ExportDirectory)) { Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage)); processThread.Name = "OpaqueMail POP3 Proxy Signature Processor"; ProcessMessageArguments processMessageArguments = new ProcessMessageArguments(); processMessageArguments.MessageText = message.Substring(0, message.Length - 5); processMessageArguments.ConnectionId = ConnectionId.ToString(); processMessageArguments.ExportDirectory = arguments.ExportDirectory; processMessageArguments.InstanceId = arguments.InstanceId; processMessageArguments.DebugMode = arguments.DebugMode; processMessageArguments.UserName = UserName; processThread.Start(processMessageArguments); } messageBuilder.Remove(0, endPos + 5); } } messageBuilder.Clear(); } } } else { stillReceiving = false; } } } } } catch (IOException) { // Ignore either stream being closed. } catch (ObjectDisposedException) { // Ignore either stream being closed. } catch (Exception ex) { // Log other exceptions. if (arguments.DebugMode || System.Diagnostics.Debugger.IsAttached) { ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.ToString(), Proxy.LogLevel.Error, LogLevel); } else { ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.Message, Proxy.LogLevel.Error, LogLevel); } } finally { // If sending to the local client, log the connection being closed. if (!arguments.IsClient) { ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + arguments.IPAddress + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel); } if (clientStream != null) { clientStream.Dispose(); } if (remoteServerStream != null) { remoteServerStream.Dispose(); } } }
/// <summary> /// Relay data read from one connection to another. /// </summary> /// <param name="o">A TransmitArguments object containing local and remote server parameters.</param> private async void RelayData(object o) { // Cast the passed-in parameters back to their original objects. TransmitArguments arguments = (TransmitArguments)o; Stream clientStream = arguments.ClientStream; Stream remoteServerStream = arguments.RemoteServerStream; // A byte array to streamline bit shuffling. char[] buffer = new char[Constants.SMALLBUFFERSIZE]; // Placeholder variables to track the current message being transmitted. StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE); // The overall number of bytes transmitted on this connection. ulong bytesTransmitted = 0; if (arguments.Credential != null) UserName = arguments.Credential.UserName; bool stillReceiving = true; try { using (StreamReader clientStreamReader = new StreamReader(clientStream)) { using (StreamWriter remoteServerStreamWriter = new StreamWriter(remoteServerStream)) { remoteServerStreamWriter.AutoFlush = true; while (Started && stillReceiving) { // Read data from the source and send it to its destination. string stringRead = await clientStreamReader.ReadLineAsync(); if (stringRead != null) { int bytesRead = stringRead.Length; bytesTransmitted += (ulong)bytesRead; messageBuilder.AppendLine(stringRead); // If this data comes from the client, log it. Otherwise, process it. if (arguments.IsClient) { bool messageRelayed = false; string[] commandParts = stringRead.Substring(0, stringRead.Length).Split(new char[] { ' ' }, 2); // Optionally replace credentials with those from our settings file. if (commandParts.Length == 2) { if (commandParts[0] == "USER") { if (arguments.Credential != null) { await remoteServerStreamWriter.WriteLineAsync("USER " + arguments.Credential.UserName); ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: USER " + arguments.Credential.UserName, Proxy.LogLevel.Raw, LogLevel); messageRelayed = true; } else UserName = commandParts[1]; } else if (arguments.Credential != null && commandParts[0] == "PASS") { await remoteServerStreamWriter.WriteLineAsync("PASS" + arguments.Credential.Password); ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: PASS " + arguments.Credential.Password, Proxy.LogLevel.Raw, LogLevel); messageRelayed = true; } } if (LogLevel == Proxy.LogLevel.Verbose) ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "Command {" + commandParts[0] + "} processed.", Proxy.LogLevel.Verbose, LogLevel); if (!messageRelayed) { await remoteServerStreamWriter.WriteLineAsync(stringRead); ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + stringRead, Proxy.LogLevel.Raw, LogLevel); } } else { await remoteServerStreamWriter.WriteLineAsync(stringRead); ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "S: " + stringRead, Proxy.LogLevel.Raw, LogLevel); // If we see a period between two linebreaks, treat it as the end of a message. if (stringRead == ".") { string message = messageBuilder.ToString(); int endPos = message.IndexOf("\r\n.\r\n"); if (message.Contains("\r\n\r\n")) { int lastOkPos = message.LastIndexOf("+OK", endPos); if (lastOkPos > -1) { message = message.Substring(message.IndexOf("\r\n", lastOkPos) + 2); if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1 || !string.IsNullOrEmpty(arguments.ExportDirectory)) { Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage)); processThread.Name = "OpaqueMail POP3 Proxy Signature Processor"; ProcessMessageArguments processMessageArguments = new ProcessMessageArguments(); processMessageArguments.MessageText = message.Substring(0, message.Length - 5); processMessageArguments.ConnectionId = ConnectionId.ToString(); processMessageArguments.ExportDirectory = arguments.ExportDirectory; processMessageArguments.InstanceId = arguments.InstanceId; processMessageArguments.DebugMode = arguments.DebugMode; processMessageArguments.UserName = UserName; processThread.Start(processMessageArguments); } messageBuilder.Remove(0, endPos + 5); } } messageBuilder.Clear(); } } } else stillReceiving = false; } } } } catch (IOException) { // Ignore either stream being closed. } catch (ObjectDisposedException) { // Ignore either stream being closed. } catch (Exception ex) { // Log other exceptions. if (arguments.DebugMode || System.Diagnostics.Debugger.IsAttached) ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.ToString(), Proxy.LogLevel.Error, LogLevel); else ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.Message, Proxy.LogLevel.Error, LogLevel); } finally { // If sending to the local client, log the connection being closed. if (!arguments.IsClient) ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + arguments.IPAddress + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel); if (clientStream != null) clientStream.Dispose(); if (remoteServerStream != null) remoteServerStream.Dispose(); } }
/// <summary> /// Relay data read from one connection to another. /// </summary> /// <param name="o">A TransmitArguments object containing local and remote server parameters.</param> private async void RelayData(object o) { // Cast the passed-in parameters back to their original objects. TransmitArguments arguments = (TransmitArguments)o; Stream clientStream = arguments.ClientStream; Stream remoteServerStream = arguments.RemoteServerStream; // A byte array to streamline bit shuffling. char[] buffer = new char[Constants.SMALLBUFFERSIZE]; // Placeholder variables to track the current message being transmitted. bool inMessage = false; int messageLength = 0; StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE); // The overall number of bytes transmitted on this connection. ulong bytesTransmitted = 0; // When a "[THROTTLED]" notice was last received. DateTime lastThrottleTime = new DateTime(1900, 1, 1); if (arguments.Credential != null) { UserName = arguments.Credential.UserName; } bool stillReceiving = true; try { using (StreamReader clientStreamReader = new StreamReader(clientStream)) { using (StreamWriter remoteServerStreamWriter = new StreamWriter(remoteServerStream)) { remoteServerStreamWriter.AutoFlush = true; while (Started && stillReceiving) { // Read data from the source and send it to its destination. string stringRead = await clientStreamReader.ReadLineAsync(); if (stringRead != null) { int bytesRead = stringRead.Length; bytesTransmitted += (ulong)bytesRead; // If this data comes from the client, log it. Otherwise, process it. if (arguments.IsClient) { bool messageRelayed = false; string[] commandParts = stringRead.Split(new char[] { ' ' }, 4); if (commandParts.Length > 1) { // Optionally, transform the login details. if (commandParts[1] == "LOGIN" && commandParts.Length == 4) { messageRelayed = TransformLogin(remoteServerStreamWriter, stringRead, arguments, ref UserName); } else { ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + stringRead, Proxy.LogLevel.Raw, LogLevel); } // Remember the previous command. if (commandParts[1].ToUpper() == "UID" && commandParts.Length > 2) { LastCommandReceived = (commandParts[1] + " " + commandParts[2]).ToUpper(); } else { LastCommandReceived = commandParts[1].ToUpper(); } // Stop after a logout order is received. if (LastCommandReceived == "LOGOUT") { stillReceiving = false; } if (LogLevel == Proxy.LogLevel.Verbose) { switch (LastCommandReceived) { case "APPEND": case "AUTHENTICATE": case "CAPABILITY": case "CHECK": case "CLOSE": case "COPY": case "CREATE": case "DELETE": case "ENABLE": case "EXAMINE": case "EXPUNGE": case "FETCH": case "GETQUOTA": case "GETQUOTAROOT": case "LIST": case "LOGIN": case "LOGOUT": case "LSUB": case "MOVE": case "NOOP": case "NOTIFY": case "RENAME": case "SEARCH": case "SELECT": case "SETQUOTA": case "STATUS": case "STORE": case "SUBSCRIBE": case "UID COPY": case "UID FETCH": case "UID SEARCH": case "UID STORE": case "UNSUBSCRIBE": case "XLIST": ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "Command {" + LastCommandReceived + "} processed.", Proxy.LogLevel.Verbose, LogLevel); break; } } } // If the command wasn't processed, send the raw command. if (!messageRelayed) { await remoteServerStreamWriter.WriteLineAsync(stringRead); } } else { // If we're currently receiving a message, check to see if it's completed. if (inMessage) { messageBuilder.AppendLine(stringRead); if (messageBuilder.Length >= messageLength) { // If the message has been completed and it contains a signature, process it. string message = messageBuilder.ToString(0, messageLength); if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1 || !string.IsNullOrEmpty(arguments.ExportDirectory)) { Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage)); processThread.Name = "OpaqueMail IMAP Proxy Signature Processor"; ProcessMessageArguments processMessageArguments = new ProcessMessageArguments(); processMessageArguments.MessageText = message; processMessageArguments.ConnectionId = ConnectionId.ToString(); processMessageArguments.ExportDirectory = arguments.ExportDirectory; processMessageArguments.InstanceId = arguments.InstanceId; processMessageArguments.DebugMode = arguments.DebugMode; processMessageArguments.UserName = UserName; processThread.Start(processMessageArguments); } // We're no longer receiving a message, so continue. inMessage = false; messageBuilder.Clear(); } } else { // Disallow proxy stream compression. if (stringRead.StartsWith("* CAPABILITY")) { stringRead = stringRead.Replace(" COMPRESS=DEFLATE", ""); } // Check for IMAP meta flags. string betweenBraces = Functions.ReturnBetween(stringRead, "[", "]"); switch (betweenBraces) { case "CLIENTBUG": case "CORRUPTION": case "OVERQUOTA": case "SERVERBUG": case "UNAVAILABLE": ProxyFunctions.Log(LogWriter, SessionId, ConnectionId.ToString(), stringRead.Substring(stringRead.IndexOf("[")), Proxy.LogLevel.Warning, LogLevel); break; case "THROTTLED": if (DateTime.Now - lastThrottleTime >= new TimeSpan(0, 20, 0)) { ProxyFunctions.Log(LogWriter, SessionId, ConnectionId.ToString(), "Connection speed throttled by the remote server.", Proxy.LogLevel.Warning, LogLevel); lastThrottleTime = DateTime.Now; } break; } // Messages are denoted by FETCH headers with their lengths in curly braces. if (stringRead.ToUpper().Contains(" FETCH ")) { int openBrace = stringRead.IndexOf("{"); if (openBrace > -1) { int closeBrace = stringRead.IndexOf("}", openBrace); if (closeBrace > -1) { // Only proceed if we can parse the size of the message. if (int.TryParse(stringRead.Substring(openBrace + 1, closeBrace - openBrace - 1), out messageLength)) { inMessage = true; } } } } } await remoteServerStreamWriter.WriteLineAsync(stringRead); ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "S: " + stringRead, Proxy.LogLevel.Raw, LogLevel); } } else { stillReceiving = false; } } } } } catch (IOException) { // Ignore either stream being closed. } catch (ObjectDisposedException) { // Ignore either stream being closed. } catch (Exception ex) { // Log other exceptions. if (arguments.DebugMode || System.Diagnostics.Debugger.IsAttached) { ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.ToString(), Proxy.LogLevel.Error, LogLevel); } else { ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.Message, Proxy.LogLevel.Error, LogLevel); } } finally { // If sending to the local client, log the connection being closed. if (!arguments.IsClient) { ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + arguments.IPAddress + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel); } if (clientStream != null) { clientStream.Dispose(); } if (remoteServerStream != null) { remoteServerStream.Dispose(); } } }
/// <summary> /// Relay data read from one connection to another. /// </summary> /// <param name="o">A TransmitArguments object containing local and remote server parameters.</param> private async void RelayData(object o) { // Cast the passed-in parameters back to their original objects. TransmitArguments arguments = (TransmitArguments)o; Stream clientStream = arguments.ClientStream; Stream remoteServerStream = arguments.RemoteServerStream; // A byte array to streamline bit shuffling. char[] buffer = new char[Constants.SMALLBUFFERSIZE]; // Placeholder variables to track the current message being transmitted. bool inMessage = false; int messageLength = 0; StringBuilder messageBuilder = new StringBuilder(Constants.SMALLSBSIZE); // The overall number of bytes transmitted on this connection. ulong bytesTransmitted = 0; // When a "[THROTTLED]" notice was last received. DateTime lastThrottleTime = new DateTime(1900, 1, 1); if (arguments.Credential != null) UserName = arguments.Credential.UserName; bool stillReceiving = true; try { using (StreamReader clientStreamReader = new StreamReader(clientStream)) { using (StreamWriter remoteServerStreamWriter = new StreamWriter(remoteServerStream)) { remoteServerStreamWriter.AutoFlush = true; while (Started && stillReceiving) { // Read data from the source and send it to its destination. string stringRead = await clientStreamReader.ReadLineAsync(); if (stringRead != null) { int bytesRead = stringRead.Length; bytesTransmitted += (ulong)bytesRead; // If this data comes from the client, log it. Otherwise, process it. if (arguments.IsClient) { bool messageRelayed = false; string[] commandParts = stringRead.Split(new char[] { ' ' }, 4); if (commandParts.Length > 1) { // Optionally replace credentials with those from our settings file. if (commandParts[1] == "LOGIN" && commandParts.Length == 4) { if (arguments.Credential != null) { await remoteServerStreamWriter.WriteLineAsync(commandParts[0] + " " + commandParts[1] + " " + arguments.Credential.UserName + " " + arguments.Credential.Password); ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + commandParts[0] + " " + commandParts[1] + " " + arguments.Credential.UserName + " " + arguments.Credential.Password, Proxy.LogLevel.Raw, LogLevel); messageRelayed = true; } else UserName = commandParts[2].Replace("\"", ""); } else ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "C: " + stringRead, Proxy.LogLevel.Raw, LogLevel); if (commandParts[1].ToUpper() == "UID" && commandParts.Length > 2) LastCommandReceived = (commandParts[1] + " " + commandParts[2]).ToUpper(); else LastCommandReceived = commandParts[1].ToUpper(); if (LastCommandReceived == "LOGOUT") stillReceiving = false; if (LogLevel == Proxy.LogLevel.Verbose) { switch (LastCommandReceived) { case "APPEND": case "AUTHENTICATE": case "CAPABILITY": case "CHECK": case "CLOSE": case "COPY": case "CREATE": case "DELETE": case "ENABLE": case "EXAMINE": case "EXPUNGE": case "FETCH": case "GETQUOTA": case "GETQUOTAROOT": case "LIST": case "LOGIN": case "LOGOUT": case "LSUB": case "MOVE": case "NOOP": case "NOTIFY": case "RENAME": case "SEARCH": case "SELECT": case "SETQUOTA": case "STATUS": case "STORE": case "SUBSCRIBE": case "UID COPY": case "UID FETCH": case "UID SEARCH": case "UID STORE": case "UNSUBSCRIBE": case "XLIST": ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "Command {" + LastCommandReceived + "} processed.", Proxy.LogLevel.Verbose, LogLevel); break; } } } if (!messageRelayed) await remoteServerStreamWriter.WriteLineAsync(stringRead); } else { // If we're currently receiving a message, check to see if it's completed. if (inMessage) { messageBuilder.AppendLine(stringRead); if (messageBuilder.Length >= messageLength) { // If the message has been completed and it contains a signature, process it. string message = messageBuilder.ToString(0, messageLength); if (message.IndexOf("application/x-pkcs7-signature") > -1 || message.IndexOf("application/pkcs7-mime") > -1 || !string.IsNullOrEmpty(arguments.ExportDirectory)) { Thread processThread = new Thread(new ParameterizedThreadStart(ProcessMessage)); processThread.Name = "OpaqueMail IMAP Proxy Signature Processor"; ProcessMessageArguments processMessageArguments = new ProcessMessageArguments(); processMessageArguments.MessageText = message; processMessageArguments.ConnectionId = ConnectionId.ToString(); processMessageArguments.ExportDirectory = arguments.ExportDirectory; processMessageArguments.InstanceId = arguments.InstanceId; processMessageArguments.DebugMode = arguments.DebugMode; processMessageArguments.UserName = UserName; processThread.Start(processMessageArguments); } // We're no longer receiving a message, so continue. inMessage = false; messageBuilder.Clear(); } } else { // Disallow proxy stream compression. if (stringRead.StartsWith("* CAPABILITY")) stringRead = stringRead.Replace(" COMPRESS=DEFLATE", ""); string betweenBraces = Functions.ReturnBetween(stringRead, "[", "]"); switch (betweenBraces) { case "CLIENTBUG": case "CORRUPTION": case "OVERQUOTA": case "SERVERBUG": case "UNAVAILABLE": ProxyFunctions.Log(LogWriter, SessionId, ConnectionId.ToString(), stringRead.Substring(stringRead.IndexOf("[")), Proxy.LogLevel.Warning, LogLevel); break; case "THROTTLED": if (DateTime.Now - lastThrottleTime >= new TimeSpan(0, 20, 0)) { ProxyFunctions.Log(LogWriter, SessionId, ConnectionId.ToString(), "Connection speed throttled by the remote server.", Proxy.LogLevel.Warning, LogLevel); lastThrottleTime = DateTime.Now; } break; } // Messages are denoted by FETCH headers with their lengths in curly braces. if (stringRead.ToUpper().Contains(" FETCH ")) { int openBrace = stringRead.IndexOf("{"); if (openBrace > -1) { int closeBrace = stringRead.IndexOf("}", openBrace); if (closeBrace > -1) { // Only proceed if we can parse the size of the message. if (int.TryParse(stringRead.Substring(openBrace + 1, closeBrace - openBrace - 1), out messageLength)) inMessage = true; } } } } await remoteServerStreamWriter.WriteLineAsync(stringRead); ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId.ToString(), "S: " + stringRead, Proxy.LogLevel.Raw, LogLevel); } } else stillReceiving = false; } } } } catch (IOException) { // Ignore either stream being closed. } catch (ObjectDisposedException) { // Ignore either stream being closed. } catch (Exception ex) { // Log other exceptions. if (arguments.DebugMode || System.Diagnostics.Debugger.IsAttached) ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.ToString(), Proxy.LogLevel.Error, LogLevel); else ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Exception while transmitting data: " + ex.Message, Proxy.LogLevel.Error, LogLevel); } finally { // If sending to the local client, log the connection being closed. if (!arguments.IsClient) ProxyFunctions.Log(LogWriter, SessionId, arguments.ConnectionId, "Connection from {" + arguments.IPAddress + "} closed after transmitting {" + bytesTransmitted.ToString("N0") + "} bytes.", Proxy.LogLevel.Information, LogLevel); if (clientStream != null) clientStream.Dispose(); if (remoteServerStream != null) remoteServerStream.Dispose(); } }