//DATAコマンドを送る private void SendDataCommand(MailParameters mailParams) { //送信データを作成する string data = CreateSendStringDataFromMessage(mailParams); // データコマンド送信 var code = SmtpSendAndReceive("DATA\r\n"); if (code != 354) { throw new SmtpException(smtpcom.received); } // データ本体送信 code = SmtpSendAndReceive(data); if (code != 250) { throw new SmtpException(smtpcom.received); } }
private void PopBeforeSmtp(MailParameters mailParams) { Stream stream = null; System.Net.Sockets.TcpClient popclient = null; try { string rstr; popclient = new System.Net.Sockets.TcpClient(); // POPサーバーに接続 popclient.Connect(mailParams.PopServer, mailParams.PopPort); logs.AppendLine("POP: Connected."); X509CertificateCollection clientCertificateCollection = new X509CertificateCollection(); if (mailParams.IsClientCertValidate) { var clientCertificate = Cert.GetCert(mailParams.ClientCertSerialNo); clientCertificateCollection.Add(clientCertificate); } // サーバーとデータの送受信を行うストリームを取得する // 通信開始(SSL有り) switch (mailParams.PopSecureMode) { case SecureMode.SSL2: // SSL2で運用しているサーバは存在しないはずだが、一応対応しておく stream = new System.Net.Security.SslStream(popclient.GetStream(), false, ServerCertificateValidation); ((System.Net.Security.SslStream)stream).AuthenticateAsClient(mailParams.SmtpServer, clientCertificateCollection, SslProtocols.Ssl2, false); logs.AppendLine("POP: socket is over SSL2."); break; case SecureMode.SSL3: // SSL3で運用しているサーバはあるかもしれない stream = new System.Net.Security.SslStream(popclient.GetStream(), false, ServerCertificateValidation); ((System.Net.Security.SslStream)stream).AuthenticateAsClient(mailParams.SmtpServer, clientCertificateCollection, SslProtocols.Ssl3, false); logs.AppendLine("POP: socket is over SSL3."); break; case SecureMode.TLS: // TLSは現状では主流 case SecureMode.STARTTLS: stream = new System.Net.Security.SslStream(popclient.GetStream(), false, ServerCertificateValidation); ((System.Net.Security.SslStream)stream).AuthenticateAsClient(mailParams.SmtpServer, clientCertificateCollection, SslProtocols.Tls, false); logs.AppendLine("POP: socket is over TLS."); break; case SecureMode.None: stream = popclient.GetStream(); logs.AppendLine("POP: socket unsecure."); break; } stream.ReadTimeout = 5000; stream.WriteTimeout = 500; //サーバーからのはじめのメッセージを受信 // POPサーバー接続時のレスポンス受信 string connectstr = PopWriteAndRead(stream, ""); if (connectstr.StartsWith("+OK") != true) { throw new PopException("POPサーバー接続エラー"); } switch (mailParams.PopAuth) { case PopAuthMethod.Standard: // ユーザIDの送信 rstr = PopWriteAndRead(stream, "USER " + mailParams.PopUserId + "\r\n"); if (rstr.StartsWith("+OK") != true) { throw new PopException("ユーザIDエラー"); } // パスワードの送信 rstr = PopWriteAndRead(stream, "PASS " + mailParams.PopPasswd + "\r\n"); if (rstr.StartsWith("+OK") != true) { throw new PopException("パスワードエラー"); } break; case PopAuthMethod.APOP: // APOP用のタイムスタンプ文字列を取得しておく var timestamp = GetAPopTimeStamp(connectstr); if (string.IsNullOrWhiteSpace(timestamp)) { throw new PopException("APOP未対応"); } Byte[] byt = System.Text.Encoding.ASCII.GetBytes(string.Format("<{0}>{1}", mailParams.PopUserId, mailParams.PopPasswd)); System.Security.Cryptography.MD5CryptoServiceProvider md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); Byte[] res = md5.ComputeHash(byt); string aps = BitConverter.ToString(res).Replace("-", "").ToLower(); rstr = PopWriteAndRead(stream, "APOP " + mailParams.PopUserId + " " + aps + "\r\n"); if (rstr.StartsWith("+OK") != true) { throw new PopException("ユーザIDまたはパスワードエラー"); } break; case PopAuthMethod.NTLM: // ユーザIDの送信 rstr = PopWriteAndRead(stream, "USER " + mailParams.PopUserId + "\r\n"); if (rstr.StartsWith("+OK") != true) { throw new PopException("ユーザIDエラー"); } // パスワードの送信 rstr = PopWriteAndRead(stream, "PASS " + mailParams.PopPasswd + "\r\n"); if (rstr.StartsWith("+OK") != true) { throw new PopException("パスワードエラー"); } break; case PopAuthMethod.CramMd5: rstr = PopWriteAndRead(stream, "AUTH CRAM-MD5\r\n"); if (rstr.StartsWith("+OK") != true) { throw new PopException("CRAM-MD5未対応"); } rstr = PopWriteAndRead(stream, CreateCramMd5ResponseString(rstr.Substring(4), mailParams.PopUserId, mailParams.PopPasswd) + "\r\n"); if (rstr.StartsWith("+OK") != true) { throw new PopException("認証エラー"); } break; } // ステータスの送信 rstr = PopWriteAndRead(stream, "STAT" + "\r\n"); if (rstr.StartsWith("+OK") != true) { throw new PopException("STATエラー"); } // 終了の送信 rstr = PopWriteAndRead(stream, "QUIT" + "\r\n"); // 戻り値は無視 } catch (PopException ex) { throw ex; } catch (Exception ex) { throw new PopException("内部例外発生", ex); } finally { if (stream != null) { stream.Close(); stream.Dispose(); } if (popclient != null) { popclient.Close(); } } }
/// <summary> /// メール送信データ編集(ヘッダ&ボディ) /// </summary> /// <param name="mailParams"></param> /// <returns></returns> private string CreateSendStringDataFromMessage(MailParameters mailParams) { StringBuilder data = new StringBuilder(); // ヘッダ部 data.Append("From: ").Append(mailParams.From.Trim()).Append("\r\n"); string prefix; if (mailParams.To != null && mailParams.To.Length > 0) { prefix = "To: "; foreach (var to in mailParams.To) { data.Append(prefix).Append(to.Trim()); prefix = ","; } data.Append("\r\n"); } if (mailParams.Cc != null && mailParams.Cc.Length > 0) { prefix = "Cc: "; foreach (var cc in mailParams.Cc) { data.Append(prefix).Append(cc.Trim()); prefix = ","; } data.Append("\r\n"); } // Bccは送信しても消されるので編集すること自体が無意味(RCPT TOコマンドで送信済み) // ReplyTo if (string.IsNullOrWhiteSpace(mailParams.ReplyTo) != true) { data.Append("ReplyTo: ").Append(mailParams.ReplyTo.Trim()).Append("\r\n"); } // Sender if (string.IsNullOrWhiteSpace(mailParams.Sender) != true) { data.Append("Sender: ").Append(mailParams.Sender.Trim()).Append("\r\n"); } if (mailParams.ExtHeaders != null) { foreach (var ext in mailParams.ExtHeaders) { if (string.IsNullOrWhiteSpace(ext)) { // 空文字は無視 continue; } if (ext.StartsWith("X-") != true || ext.Contains(": ") != true) { throw new SmtpException("Invalid format in Optional-header[" + ext + "]"); } data.Append(ext + "\r\n"); } } //件名をBase64でエンコード data.Append("Subject: =?" + _encoding.BodyName + "?B?").Append(GetBase64String(mailParams.Subject)).Append("?=").Append("\r\n"); data.Append("MIME-Version: 1.0\r\n"); data.Append("Content-Transfer-Encoding: 7bit\r\n"); if (mailParams.Files == null || mailParams.Files.Length == 0) { // 添付ファイルがない場合(テキスト本文のみ) data.Append("Content-Type: text/plain; charset=" + _encoding.BodyName + "\r\n"); // ヘッダとボディの区切りのための改行 data.Append("\r\n"); // ボディ部 data.Append(mailParams.Body.Replace("\r\n.\r\n", "\r\n..\r\n")); } else { string boundary = string.Empty; { // boundary文字列を乱数から生成(まともな乱数にするため、System.Randomは使用禁止) System.Security.Cryptography.RNGCryptoServiceProvider rnd = new System.Security.Cryptography.RNGCryptoServiceProvider(); byte[] bytes = new byte[24]; rnd.GetBytes(bytes); boundary = "_PART_" + Convert.ToBase64String(bytes, Base64FormattingOptions.InsertLineBreaks); } // 注意:boundaryの文字列は""で囲むのがRFC的に正しい。 data.Append("Content-Type: multipart/mixed; boundary=\"" + boundary + "\"\r\n"); // ヘッダとボディの区切りのための改行 data.Append("\r\n"); // 本文は textパート data.Append("--" + boundary + "\r\n"); data.Append("Content-Type: text/plain; charset=" + _encoding.BodyName + "\r\n"); data.Append("\r\n"); data.Append(mailParams.Body.Replace("\r\n.\r\n", "\r\n..\r\n")); // 添付ファイル foreach (var file in mailParams.Files) { FileInfo fi = new FileInfo(file); byte[] fdata; FileStream fs = new FileStream(file, FileMode.Open); long length = fs.Length; if (length > int.MaxValue) { fs.Dispose(); throw new SmtpException("too large file"); } else { fdata = new byte[length]; fs.Read(fdata, 0, (int)length); fs.Dispose(); } string b64 = "Content-type: unknown;\r\n"; string fstr = Convert.ToBase64String(_encoding.GetBytes(fi.Name), Base64FormattingOptions.InsertLineBreaks); b64 += " name=" + "=?ISO-2022-JP?B?" + fstr + "?=" + "\r\n"; b64 += "Content-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n"; b64 += " filename=" + "=?ISO-2022-JP?B?" + fstr + "?=" + "\r\n\r\n"; { b64 += Convert.ToBase64String(fdata, Base64FormattingOptions.InsertLineBreaks); } data.Append("\r\n\r\n--" + boundary + "\r\n"); data.Append(b64); } // multipart の終端 data.Append("\r\n\r\n--" + boundary + "--\r\n"); } // メールデータの終端 data.Append("\r\n.\r\n"); return(data.ToString()); }
/// <summary> /// SMTPサーバーと接続する /// </summary> private SmtpCommData SmtpConnect(MailParameters mailParams) { this.IsServerCertValidate = mailParams.IsServerCertValidate; smtpcom = new SmtpCommData(); smtpcom.socket = new TcpClient(); // SMTPサーバーと接続する smtpcom.socket.Connect(mailParams.SmtpServer, mailParams.SmtpPort); logs.AppendLine("SMTP: Connected."); X509CertificateCollection clientCertificateCollection = new X509CertificateCollection(); if (mailParams.IsClientCertValidate) { var clientCertificate = Cert.GetCert(mailParams.ClientCertSerialNo); clientCertificateCollection.Add(clientCertificate); } // サーバーとデータの送受信を行うストリームを取得する // 通信開始(SSL有り) switch (mailParams.SmtpSecureMode) { case SecureMode.SSL2: // SSL2で運用しているサーバは存在しないはずだが、一応対応しておく smtpcom.stream = new System.Net.Security.SslStream(smtpcom.socket.GetStream(), false, ServerCertificateValidation); ((System.Net.Security.SslStream)smtpcom.stream).AuthenticateAsClient(mailParams.SmtpServer, clientCertificateCollection, SslProtocols.Ssl2, false); logs.AppendLine("SMTP: socket is over SSL2."); break; case SecureMode.SSL3: // SSL3で運用しているサーバはあるかもしれない smtpcom.stream = new System.Net.Security.SslStream(smtpcom.socket.GetStream(), false, ServerCertificateValidation); ((System.Net.Security.SslStream)smtpcom.stream).AuthenticateAsClient(mailParams.SmtpServer, clientCertificateCollection, SslProtocols.Ssl3, false); logs.AppendLine("SMTP: socket is over SSL3."); break; case SecureMode.TLS: // TLSは現状では主流 case SecureMode.STARTTLS: smtpcom.stream = new System.Net.Security.SslStream(smtpcom.socket.GetStream(), false, ServerCertificateValidation); ((System.Net.Security.SslStream)smtpcom.stream).AuthenticateAsClient(mailParams.SmtpServer, clientCertificateCollection, SslProtocols.Tls, false); logs.AppendLine("SMTP: socket is over TLS."); break; case SecureMode.None: smtpcom.stream = smtpcom.socket.GetStream(); logs.AppendLine("SMTP: socket unsecure."); break; } smtpcom.stream.ReadTimeout = 5000; smtpcom.stream.WriteTimeout = 500; smtpcom.reader = new System.IO.StreamReader(smtpcom.stream, _encoding); smtpcom.writer = new System.IO.StreamWriter(smtpcom.stream, _encoding); //サーバーからのはじめのメッセージを受信 var ret = ReceiveData(); if (ret != 220) { throw new SmtpException(smtpcom.received); } return(smtpcom); }
/// <summary> /// 指定されたパラメータの内容でメールを送信する(エンコードはISO-2022-JPのみをサポート) /// </summary> /// <param name="mailParams">メール送信パラメータ</param> /// <returns>送信結果(true:正常、false:失敗)</returns> public bool SendMail(MailParameters mailParams) { // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ // ■ // ■ System.Net.Mail.SmtpClient は、SMTP.SSLに対応していないため使用不可なので、 // ■ いろんなパターンに対応するため、TcpClientを使ってプロトコルを実装する。 // ■ // ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ bool result = false; mailParams.logs = string.Empty; try { // ISO-2022-JPでエンコードする _encoding = System.Text.Encoding.GetEncoding(50220); if (mailParams.IsPopBeforeSmtp) { // POP before SMTP 指定の場合 PopBeforeSmtp(mailParams); if (mailParams.PopDelayTime > 0) { System.Threading.Thread.Sleep(mailParams.PopDelayTime); } } // SMTPサーバーと接続する smtpcom = SmtpConnect(mailParams); // EHLO SendHeloCommand(System.Net.Dns.GetHostName()); if (mailParams.SmtpSecureMode == SecureMode.STARTTLS) { // TLSの実装------------------------------------------------------------------ // STARTTLSの送信 var rcode = SmtpSendAndReceive("STARTTLS" + "\r\n"); if ((rcode / 100) != 2) { throw new SmtpException("エラー:" + rcode); } // SSLの開始 smtpcom.stream = new System.Net.Security.SslStream(smtpcom.socket.GetStream()); ((System.Net.Security.SslStream)smtpcom.stream).AuthenticateAsClient(mailParams.SmtpServer); smtpcom.stream.ReadTimeout = 5000; smtpcom.stream.WriteTimeout = 500; smtpcom.reader = new System.IO.StreamReader(smtpcom.stream, _encoding); smtpcom.writer = new System.IO.StreamWriter(smtpcom.stream, _encoding); } // AUTH Authenticate(mailParams.SmtpAuth, mailParams.SmtpUserId, mailParams.SmtpPasswd); // MAIL FROM(RCPTより先に送信する) SendMailFromCommand(mailParams.From); // RCPT TO SendRcptToCommand(mailParams.To, mailParams.Cc, mailParams.Bcc); // DATA(メールヘッダ&ボディ) SendDataCommand(mailParams); // QUIT SendQuitCommand(); result = true; mailParams.status = "OK"; } catch (Exception ex) { mailParams.status = "NG : " + ex.Message + ((ex.InnerException == null) ? "" : ("\r\n" + ex.InnerException.Message)); result = false; } finally { if (smtpcom != null) { if (smtpcom.socket != null) { smtpcom.socket.Close(); } } } mailParams.logs = logs.ToString(); return(result); }