この記事は古いです。ここも参照してください
ログイン機能を付け足しました。loginとplainだけですが。CRAM-MD5はどうしてもうまくいかないのでコメントアウトしてあります。
文字列のエンコードがぐちゃぐちゃなのであとでなおします。(いちおう文字化けはしないはず。)
コメントアウトが沢山でごめんなさい。一旦コンパイルして逆コンパイルすれば取り除けますが(笑)
using System; using System.Collections.Generic; //using System.Linq; using System.Text; using System.Net.Sockets; using System.IO; using System.Security.Cryptography; namespace Mail { public class Mail { static void Main(string[] args) { Message mes = new Message(); mes.Body = "ああああああああ"; mes.From = new Address("from@msn.com",null); mes.To = new List<Address>(); mes.To.Add(new Address("to@msn.com","あおんと")); SMTP smtp = new SMTP("mail.asahi-net.or.jp", 25); smtp.Auth(SMTP.AuthTypes.Plain, "user", "password"); smtp.From("from@from.com"); smtp.Recipient("aont@hatena.com"); smtp.Send(mes); smtp.Quit(); Console.ReadKey(true); } /* =========================================================== */ static string ToBase64(byte[] bytes) { return Convert.ToBase64String(bytes, Base64FormattingOptions.InsertLineBreaks); } static string FromBase64(string b64) { return Encoding.Unicode.GetString(System.Convert.FromBase64String(b64)); } static string ToBase64(string @string) { return Convert.ToBase64String( JIS_Enc.GetBytes( @string ), Base64FormattingOptions.None ); } static string RandomB64(int n) { Random rnd = new Random(); byte[] bytes = new byte[n]; for (int i = 0; i < n; i++) { bytes[i] = (byte)rnd.Next(256); } return ToBase64(bytes); } /* static string MD5(string s) { byte[] data = System.Text.Encoding.Unicode.GetBytes(s); MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider(); byte[] bs = md5.ComputeHash(data); StringBuilder result = new StringBuilder(); foreach (byte b in bs) { result.Append(b.ToString("x2")); } return result.ToString(); } */ static Encoding JIS_Enc = Encoding.GetEncoding(50220); static string ToBase64_JIS(string @string) { byte[] bytes = JIS_Enc.GetBytes(@string); return "=?ISO-2022-JP?B?" + ToBase64(bytes) + "?="; } /* =========================================================== */ public class SMTP { NetworkStream Stream; TcpClient Client; int Port; string Server; StreamReader Reader; //List<string> messages = null; public SMTP(string Server, int Port) { this.Server = Server; this.Port = Port; Connect(); } public SMTP(string Server) { this.Server = Server; this.Port = 25; Connect(); } void Connect() { Client = new TcpClient(Server, Port); Stream = Client.GetStream(); Stream.ReadTimeout = 5; Reader = new StreamReader(Stream); ReadEx("220"); WriteLine("EHLO ", Server); ReadEx("250"); } //IEnumerable<string> string ReadEx(params string[] codes) { string read = null; string read_toend = ""; do { read = Read(); read_toend += read + "\r\n"; //write(read); //yield return read; //Console.WriteLine(read); bool error = false; foreach (string code in codes) { if (error = read.StartsWith(code)) break; } if (!error) { throw new Exception(read); } } while (read[3] == '-'); return read_toend; } string[] _AuthTypes; string Read() { string read = Reader.ReadLine(); if (read.StartsWith("250-AUTH ")) { _AuthTypes = read.Substring(9).Split(' ', '\0'); } return read; } void Write(byte[] bytearray) { Stream.Write(bytearray, 0, bytearray.Length); } void Write(byte[] bytearray, int offset, int size) { Stream.Write(bytearray, offset, size); } void Write(string write) { //Console.Write(write);////////////////////////////////////////////////////////////////////////// byte[] bytearray = JIS_Enc.GetBytes(write); Write(bytearray); } void WriteLine(string write) { Write(write); Stream.Write(new byte[] { 13, 10 }, 0, 2); //Console.WriteLine();////////////////////////////////////////////////////////////////////////////////// } void Write(params string[] write_array) { foreach (string write in write_array) { Write(write); } } void WriteLine(params string[] write_array) { foreach (string write in write_array) { Write(write); } Stream.Write(new byte[] { 13, 10 }, 0, 2); } public static class AuthTypes { public const string Plain = "PLAIN", Login = "LOGIN"; //Cram_MD5 = "CRAM-MD5"; } public void Auth(string type, string user, string password) { type = type.ToUpper(); AuthCheck(type); switch (type) { case "PLAIN": WriteLine("AUTH PLAIN " + ToBase64(user + '\0' + user + '\0' + password)); break; case "LOGIN": WriteLine("AUTH LOGIN"); ReadEx("334"); WriteLine(ToBase64(user)); ReadEx("334"); WriteLine(ToBase64(password)); break; /* case "CRAM-MD5": WriteLine("AUTH CRAM-MD5"); string md5_key = System.Text.Encoding.ASCII.GetString(System.Convert.FromBase64String(Read().Substring(4))); Console.WriteLine(md5_key); Console.ReadKey(true); Console.WriteLine(MD5(md5_key + password)); Console.ReadKey(true); WriteLine(ToBase64(user + '\0' + MD5(md5_key + password)));///////////////////////// break; */ } ReadEx("235"); } private void AuthCheck(string type) { foreach (string _type in _AuthTypes) { if (_type == type) { return; } } throw new Exception("Authtype:" + type + " Invalid."); //return; } public void From(string from) { //throw new Exception("AAAAAAAAAAAAAAAAAAa"); WriteLine("MAIL FROM:<", from, ">"); ReadEx("250"); } public void Recipient(string rcpt) { WriteLine("RCPT TO:<", rcpt, ">"); ReadEx("250", "251"); } public void Recipient(params string[] rcpts) { foreach (string rcpt in rcpts) { Recipient(rcpt); } } public void Send(Message message, Encoding encoding) { Send(message.ToString(), encoding); } public void Send(Message message) { Send(message.ToString(), JIS_Enc); } public void Send(string message, Encoding encoding) { WriteLine("DATA"); ReadEx("250", "354"); Write(message); WriteLine("\r\n."); ReadEx("250"); } public void Send(string message) { Send(message, JIS_Enc); } public void Send_Eml(string emlfile) { WriteLine("DATA"); ReadEx("250", "354"); FileStream fs = new FileStream(emlfile, FileMode.Open); long length = fs.Length; byte[] data; if (length > int.MaxValue) { throw new Exception("too large file"); } else { data = new byte[length]; fs.Read(data, 0, (int)length); fs.Dispose(); } Write(data); WriteLine("\r\n."); ReadEx("250"); } public void Quit() { WriteLine("QUIT"); ReadEx("221"); Dispose(); } public void Dispose() { Reader.Dispose(); Stream.Dispose(); } } /* ============================================================= */ public class Message { string boundary = RandomB64(16); public Address From = null; public List<Address> To = null; public Address ReplyTo = null; public string Subject = null; public string Body = null; public List<File> Files = null; void AddAddress(ref string s, string type, Address address) { if (address != null) { s += type + ": "; s += address; s += "\r\n"; } } void AddAddress(ref string s, string type, List<Address> addresses) { if (addresses != null) { bool first = true; s += type + ": "; foreach (Address address in addresses) { if (!first) { s += ","; first = false; } s += address; } s += "\r\n"; } } public override string ToString() { string mail = ""; AddAddress(ref mail, "From", From); AddAddress(ref mail, "To", To); AddAddress(ref mail, "Reply-To", ReplyTo); if (Subject != null) mail += "Subject: " + ToBase64_JIS(Subject) + "\r\n"; mail += "MIME-Version: 1.0\r\n"; mail += "X-Mail-Agent: Aont Mailer\r\n"; if (Files == null) { mail += "Content-Type: text/plain; charset=ISO-2022-JP\r\n"; //mail += "Content-Transfer-Encoding: base64\r\n"; mail += "\r\n"; mail += Body; //mail += ToBase64(jisEnc.GetBytes(Body)); } else { mail += "Content-Type: multipart/mixed; boundary=\"" + boundary + "\"\r\n\r\n"; mail += "--" + boundary + "\r\n"; mail += "Content-Type: text/plain; charset=ISO-2022-JP\r\n"; //mail += "Content-Transfer-Encoding: base64\r\n"; mail += "\r\n"; mail += Body; mail += "\r\n\r\n"; foreach (File file in Files) { mail += "--" + boundary + "\r\n"; mail += file; mail += "\r\n"; } mail += "--" + boundary + "--"; } return mail; } } /* ========================================================= */ public class Address { public string address; public string name; public override string ToString() { if (name != null && name != "") return ToBase64_JIS(name) + "<" + address + ">"; else return address; } public Address(string address, string name) { this.address = address; this.name = name; } } /* ========================================================= */ public class File { public string name; string _address; public string address { get { return _address; } set { _address = value; data = null; } } public byte[] data; public File(string name, string address) { this.name = name; this.address = address; } public File(string name, byte[] data) { this.name = name; this.data = data; } public override string ToString() { if (data == null) { FileStream fs = new FileStream(address, FileMode.Open); long length = fs.Length; //byte[] data; if (length > int.MaxValue) { fs.Dispose(); throw new Exception("too large file"); } else { data = new byte[length]; fs.Read(data, 0, (int)length); fs.Dispose(); } } string b64 = "Content-type: unknown;\r\n"; b64 += " name=" + ToBase64_JIS(name) + "\r\n"; b64 += "Content-Transfer-Encoding: base64\r\nContent-Disposition: attachment;\r\n"; b64 += " filename=" + ToBase64_JIS(name) + "\r\n\r\n"; { b64 += ToBase64(data); } return b64; } } } }
IEnumerableにはめられた…IEnumerableを戻り値とする関数のなかで例外発生させたり、標準出力しても反応しない…まじうざかった。しょうがないから改行つきのテキストを返すようにした。