/// <summary> /// /// </summary> /// <param name="p_receipients"></param> /// <param name="p_reversePath"></param> /// <param name="p_message"></param> /// <returns></returns> private bool SendMessageToServer(string[] p_receipients, string p_reversePath, Stream p_message) { ArrayList _defectiveEmails = new ArrayList(); Socket _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { string _reply = ""; var _SIZE_Support = false; var _8BIT_Support = false; var _BDAT_Support = false; if (UseSmartHost == true) { IPEndPoint _ipdestination = new IPEndPoint(System.Net.Dns.GetHostEntry(SmartHost).AddressList[0], Port); _socket.Connect(_ipdestination); } else { //---- Parse e-domain -------------------------------// string _emailDomain = p_receipients[0]; // eg. Ivx <*****@*****.**> if (_emailDomain.IndexOf("<") > -1 && _emailDomain.IndexOf(">") > -1) { _emailDomain = _emailDomain.Substring(_emailDomain.IndexOf("<") + 1, _emailDomain.IndexOf(">") - _emailDomain.IndexOf("<") - 1); } if (_emailDomain.IndexOf("@") > -1) { _emailDomain = _emailDomain.Substring(_emailDomain.LastIndexOf("@") + 1); } //--- Get MX record -------------------------------------------// DnsEx _dnsex = new DnsEx(); DnsEx.DnsServers = DnsServers; MX_Record[] _mxRecords = null; DnsReplyCode _replyCode = _dnsex.GetMXRecords(_emailDomain, out _mxRecords); switch (_replyCode) { case DnsReplyCode.Ok: var _isConnected = false; // Try all available hosts by MX preference order, if can't connect specified host. foreach (MX_Record _mx in _mxRecords) { try { _socket.Connect(new IPEndPoint(System.Net.Dns.GetHostEntry(_mx.Host).AddressList[0], Port)); _isConnected = true; break; } catch (Exception ex) { // Just skip and let for to try next host. OnFaulted(SmtpErrorType.ConnectionError, p_receipients, "While retriveing MX-Servers, raise exception error: " + ex.Message); } } if (_isConnected == false) { return(false); } break; case DnsReplyCode.NoEntries: /* Rfc 2821 5 * If no MX records are found, but an A RR is found, the A RR is treated as * if it was associated with an implicit MX RR, with a preference of 0, * pointing to that host. * */ try // Try to connect with A record { IPHostEntry _ipEntry = System.Net.Dns.GetHostEntry(_emailDomain); _socket.Connect(new IPEndPoint(_ipEntry.AddressList[0], Port)); } catch { OnFaulted(SmtpErrorType.InvalidEmailAddress, p_receipients, String.Format("email domain <{0}> is invalid", _emailDomain)); _defectiveEmails.AddRange(p_receipients); return(false); } break; case DnsReplyCode.TempError: string _dnsServers = ""; foreach (string s in DnsServers) { _dnsServers += s + ";"; } throw new Exception(String.Format("Error retrieving MX record for domain '{0}' with dns servers:{{{1}}}.", _emailDomain, _dnsServers)); } } string _sessionId = _socket.GetHashCode().ToString(); string _connectedip = SmtpCore.ParseIP_from_EndPoint(_socket.RemoteEndPoint.ToString()); // Get 220 reply from server /* NOTE: reply may be multiline * 220 xx ready * or * 220-someBull * 200 xx */ // Server must reply 220 - Server OK _reply = ReadLine(_socket, _sessionId, _connectedip, true); if (IsReplyCode("220", _reply) == false) { OnFaulted(SmtpErrorType.UnKnown, p_receipients, _reply); SendLine(_socket, _sessionId, _connectedip, "QUIT", true); return(false); } else { // 220-xxx<CRLF> // 220 aa<CRLF> - means end // reply isn't complete, get more while (_reply.IndexOf("220 ") == -1) { _reply += ReadLine(_socket, _sessionId, _connectedip, false); } } // cmd EHLO/HELO // Send greeting to server SendLine(_socket, _sessionId, _connectedip, "EHLO " + SmtpCore.GetHostName(), true); _reply = ReadLine(_socket, _sessionId, _connectedip, true); if (IsReplyCode("250", _reply) == false) { // EHLO failed, maybe server doesn't support it, try HELO SendLine(_socket, _sessionId, _connectedip, "HELO " + SmtpCore.GetHostName(), true); _reply = ReadLine(_socket, _sessionId, _connectedip, false); if (IsReplyCode("250", _reply) == false) { OnFaulted(SmtpErrorType.UnKnown, p_receipients, _reply); SendLine(_socket, _sessionId, _connectedip, "QUIT", true); _defectiveEmails.AddRange(p_receipients); return(false); } // else // { // supports_ESMTP = false; // } } else { // 250-xxx<CRLF> // 250 aa<CRLF> - means end // reply isn't complete, get more while (_reply.IndexOf("250 ") == -1) { _reply += ReadLine(_socket, _sessionId, _connectedip, false); } // Check if SIZE argument is supported if (_reply.ToUpper().IndexOf("SIZE") > -1) { _SIZE_Support = true; } // Check if 8BITMIME argument is supported if (_reply.ToUpper().IndexOf("8BITMIME") > -1) { _8BIT_Support = true; } // Check if CHUNKING argument is supported if (_reply.ToUpper().IndexOf("CHUNKING") > -1) { _BDAT_Support = true; } } // If server doesn't support 8bit mime, check if message is 8bit. // If is we MAY NOT send this message or loss of data if (_8BIT_Support == false) { if (Is8BitMime(p_message) == true) { OnFaulted(SmtpErrorType.NotSupported, p_receipients, "Message is 8-Bit mime and server doesn't support it."); SendLine(_socket, _sessionId, _connectedip, "QUIT", true); return(false); } } // cmd MAIL // NOTE: Syntax:{MAIL FROM:<*****@*****.**> [SIZE=msgSize]<CRLF>} // Send Mail From if (_SIZE_Support == true) { SendLine(_socket, _sessionId, _connectedip, String.Format("MAIL FROM:<{0}> SIZE={1}", p_reversePath, p_message.Length), true); } else { SendLine(_socket, _sessionId, _connectedip, String.Format("MAIL FROM:<{0}>", p_reversePath), true); } _reply = ReadLine(_socket, _sessionId, _connectedip, false); if (IsReplyCode("250", _reply) == false) { // To Do: Check if size exceeded error: OnFaulted(SmtpErrorType.UnKnown, p_receipients, _reply); SendLine(_socket, _sessionId, _connectedip, "QUIT", true); _defectiveEmails.AddRange(p_receipients); return(false); } // cmd RCPT // NOTE: Syntax:{RCPT TO:<*****@*****.**><CRLF>} var _isAnyValidEmail = false; foreach (string _receipt in p_receipients) { // Send Mail To SendLine(_socket, _sessionId, _connectedip, String.Format("RCPT TO:<{0}>", _receipt), true); _reply = ReadLine(_socket, _sessionId, _connectedip, false); if (IsReplyCode("250", _reply) == false) { // Is unknown user if (IsReplyCode("550", _reply)) { OnFaulted(SmtpErrorType.InvalidEmailAddress, new string[] { _receipt }, _reply); } else { OnFaulted(SmtpErrorType.UnKnown, new string[] { _receipt }, _reply); } _defectiveEmails.Add(_receipt); } else { _isAnyValidEmail = true; } } // If there isn't any valid email - quit. if (_isAnyValidEmail == false) { SendLine(_socket, _sessionId, _connectedip, "QUIT", true); return(false); } // cmd DATA if (_BDAT_Support == false) { // Notify Data Start SendLine(_socket, _sessionId, _connectedip, "DATA", false); _reply = ReadLine(_socket, _sessionId, _connectedip, false); if (IsReplyCode("354", _reply) == false) { OnFaulted(SmtpErrorType.UnKnown, p_receipients, _reply); SendLine(_socket, _sessionId, _connectedip, "QUIT", true); _defectiveEmails.AddRange(p_receipients); return(false); } //------- Do period handling -----------------------------------------// // If line starts with '.', add additional '.'.(Read rfc for more info) MemoryStream _periodOk = SmtpCore.DoPeriodHandling(p_message, true, false); //--------------------------------------------------------------------// // Check if message ends with <CRLF>, if not add it. -------// if (_periodOk.Length >= 2) { byte[] _byteEnd = new byte[2]; _periodOk.Position = _periodOk.Length - 2; _periodOk.Read(_byteEnd, 0, 2); if (_byteEnd[0] != (byte)'\r' && _byteEnd[1] != (byte)'\n') { _periodOk.Write(new byte[] { (byte)'\r', (byte)'\n' }, 0, 2); } } _periodOk.Position = 0; //-----------------------------------------------------------// //---- Send message --------------------------------------------// long _totalSent = 0; long _totalSize = _periodOk.Length; while (_totalSent < _totalSize) { byte[] _buffer = new byte[4000]; int _readSize = _periodOk.Read(_buffer, 0, _buffer.Length); int _sentSize = _socket.Send(_buffer, 0, _readSize, SocketFlags.None); _totalSent += _sentSize; if (_sentSize != _readSize) { _periodOk.Position = _totalSent; } OnProgress(_sentSize, _totalSent, _totalSize); } //-------------------------------------------------------------// _periodOk.Close(); // Notify End of Data SendLine(_socket, _sessionId, _connectedip, ".", false); _reply = ReadLine(_socket, _sessionId, _connectedip, false); if (IsReplyCode("250", _reply) == false) { OnFaulted(SmtpErrorType.UnKnown, p_receipients, _reply); SendLine(_socket, _sessionId, _connectedip, "QUIT", true); _defectiveEmails.AddRange(p_receipients); return(false); } } // cmd BDAT if (_BDAT_Support) { SendLine(_socket, _sessionId, _connectedip, String.Format("BDAT {0} LAST", (p_message.Length - p_message.Position)), false); //---- Send message --------------------------------------------// long _totalSent = 0; long _totalSize = p_message.Length - p_message.Position; while (_totalSent < _totalSize) { byte[] _buffer = new byte[4000]; int _readSize = p_message.Read(_buffer, 0, _buffer.Length); int _sentSize = _socket.Send(_buffer, 0, _readSize, SocketFlags.None); _totalSent += _sentSize; if (_sentSize != _readSize) { p_message.Position = _totalSent; } OnProgress(_sentSize, _totalSent, _totalSize); } //-------------------------------------------------------------// // Get store result _reply = ReadLine(_socket, _sessionId, _connectedip, false); if (_reply.StartsWith("250") == false) { OnFaulted(SmtpErrorType.UnKnown, p_receipients, _reply); SendLine(_socket, _sessionId, _connectedip, "QUIT", true); _defectiveEmails.AddRange(p_receipients); return(false); } } // cmd QUIT // Notify server - server can exit now SendLine(_socket, _sessionId, _connectedip, "QUIT", true); _reply = ReadLine(_socket, _sessionId, _connectedip, true); } catch (Exception ex) { OnFaulted(SmtpErrorType.UnKnown, p_receipients, ex.Message); _defectiveEmails.AddRange(p_receipients); return(false); } finally { // Raise event OnCompleted(Thread.CurrentThread.GetHashCode().ToString(), p_receipients, _defectiveEmails); _socket.Close(); } return(true); }