メールアドレスを指定してそこからメールが送られてきたら通知してくれるアプリをつくります。一定時間おきにメールボックスをチェックして新しくメールが来ていた場合、それが指定されていたものであれば通知メッセージを表示させます。
これまでと同様、.NET用メール送受信クラスライブラリ (TKMP.DLL) を使わせていただきます。
.NET用メール送受信クラスライブラリ (TKMP.DLL)
5分おきに自作メソッド GetNewMails()を呼び出してメールボックスを調べ、新しいメールの着信があるかどうかしらべます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
public partial class Form1 : Form { public Form1() { InitializeComponent(); Timer.Interval = 1000 * 60 * 5; Timer.Tick += Timer_Tick; } Timer Timer = new Timer(); private void Timer_Tick(object sender, EventArgs e) { GetNewMails(); } void GetNewMails() { List<Data> newMails = GetMails(); if(newMails.Count == 0) return; Datas.AddRange(newMails); listBox1.Items.Clear(); foreach(Data data in Datas) { listBox1.Items.Add(data.Subject); } this.TopMost = true; this.TopMost = false; MessageBox.Show(String.Format("新たに{0}件のメールを確認しました!", newMails.Count), "報告"); } } |
GetMails()メソッドはメールボックスのメールを取得するためのものです。Dataクラスはメールの件名、本文、誰からのものか、着信時刻などを保存するためのものです。メールボックスのメールを取得したらこれをもとにDataオブジェクトをつくり、リストに格納します。
同じメールを何度もチェックしていては時間の無駄なので、一度確認したメールのuidをリストに格納して二回目以降は無視するようにしています。
メールの削除も可能ですが、リスクが大きいのでここではやっていません。
削除するのであればこの方法で削除できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
string server = "メールサーバー"; int port = ポート番号; string userid = "アカウントID"; string pass = "パスワード"; System.Net.IPAddress address = System.Net.Dns.GetHostByName(server).AddressList[0]; TKMP.Net.IPopLogon logon = new TKMP.Net.BasicPopLogon(userid , pass); TKMP.Net.PopClient pop = new TKMP.Net.PopClient(logon , address , port); if(!pop.Connect()) { System.Windows.Forms.MessageBox.Show("接続失敗"); return ; } // メールの削除 int DeleteNumber = 1; // 削除するメールのインディックスを指定する TKMP.Net.MailData Mail = pop.MailDatas[DeleteNumber]; Mail.Delete(); // 終わったら切断 pop.Close(); |
以下のコードではうっかりミスで大事なメールを削除してしまわないように、メールの確認だけをおこなっています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
public partial class Form1 : Form { List<string> uids = new List<string>(); List<Data> Datas = new List<Data>(); List<Data> GetMails() { List<Data> datas = new List<Data>(); string server = textBoxMailServer.Text; int port = int.Parse(textBoxPort.Text); string userid = textBoxId.Text; string pass = textBoxPass.Text; //サーバへの接続用クラスを作成する System.Net.IPAddress address = System.Net.Dns.GetHostEntry(server).AddressList[0]; TKMP.Net.IPopLogon logon = new TKMP.Net.BasicPopLogon(userid, pass); TKMP.Net.PopClient pop = new TKMP.Net.PopClient(logon, address, port); //SSLを使用します pop.AuthenticationProtocol = TKMP.Net.AuthenticationProtocols.SSL; //証明書に問題があった場合に独自の処理を追加します pop.CertificateValidation += new TKMP.Net.CertificateValidationHandler(pop_CertificateValidation); //接続開始 if(!pop.Connect()) { MessageBox.Show("接続失敗"); return datas; } //着信件数の読み出し int mailcount = pop.MailDatas.Length; //MessageBox.Show(mailcount.ToString() +" kenn"); if(mailcount == 0) { MessageBox.Show("着信メールはありません"); return datas; } string[] vs = textBoxMallAdresses.Lines; vs = vs.Where(x => x != "").ToArray(); for(int i = 0; i < mailcount; i++) { TKMP.Net.MailData mailData = pop.MailDatas[i]; string uid = mailData.UID; // これを保存しておく 次回から取得しない if(uids.Any(x => x == uid)) continue; uids.Add(uid); string from = ""; if(!mailData.ReadHeader()) { System.Windows.Forms.MessageBox.Show("データ受信に失敗しました"); continue; } else { System.IO.Stream Header = mailData.HeaderStream; // 発信メールアドレスを調べる TKMP.Reader.MailReader reader1 = new TKMP.Reader.MailReader(Header, true); from = reader1.HeaderCollection["From"]; // 新着メールをすべてチェックする場合、またはチェックリストに存在する発信元であればメールを取得する。それ以外は無視 if(!checkBox1.Checked && !vs.Any(x => from.IndexOf(x) != -1)) continue; } if(!mailData.ReadBody()) { MessageBox.Show("データ受信に失敗しました"); continue; } //メッセージ表示のためのメール解析 TKMP.Reader.MailReader reader = new TKMP.Reader.MailReader(mailData.DataStream, false); string subject = reader.HeaderCollection["Subject"]; string date = reader.HeaderCollection["Date"]; string mainText = reader.MainText + "\n"; datas.Add(new Data(subject, date, mainText, from)); } //切断 pop.Close(); return datas; } private void pop_CertificateValidation(object sender, TKMP.Net.CertificateValidationArgs e) { //全ての証明書を信用します e.Cancel = false; } } |
[操作] ⇒ [スタート]をクリックするとメールを取得します。そして取得対象のメールを取得したらリストボックスに表示します。そのあと一定時間おきにメールボックスをチェックして新着メールがあればその内容を取得して表示します。
1 2 3 4 5 6 7 8 |
public partial class Form1 : Form { private void MenuItemStart_Click(object sender, EventArgs e) { Timer.Start(); GetNewMails(); } } |
リストボックスのアイテムが選択されたら必要な情報を表示させます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public partial class Form1 : Form { private void listBox1_SelectedIndexChanged(object sender, EventArgs e) { int index = listBox1.SelectedIndex; if(index == -1) return; Data data = Datas[index]; textBoxSubject.Text = data.Subject; textBoxDate.Text = data.DateText; textBoxFrom.Text = data.From; richTextBox1.Text = data.MainText; } } |
メニューで[設定] ⇒ [設定を保存]が選択されたら設定を暗号化して保存します。保存されるのはメールサーバーの情報とメールアカウントのIDとパスワード、取得対象とする送信元のメールアドレスなどです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
public partial class Form1 : Form { private void MenuSaveConfig_Click(object sender, EventArgs e) { SaveConfig(); } void SaveConfig() { SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "設定ファイル(*.xml)|*.xml"; dlg.DefaultExt = "xml"; if(dlg.ShowDialog() == DialogResult.OK) { FormSavePass f = new FormSavePass(); f.ShowDialog(); if(f.Password != "") { Config config = new Config(f.Password, textBoxMailServer.Text, textBoxPort.Text, textBoxId.Text, textBoxPass.Text, checkBox1.Checked, textBoxMallAdresses.Text); System.Xml.Serialization.XmlSerializer xml = new System.Xml.Serialization.XmlSerializer(typeof(Config)); System.IO.StreamWriter sw = new System.IO.StreamWriter(dlg.FileName); xml.Serialize(sw, config); sw.Close(); MessageBox.Show("設定ファイル保存完了"); } f.Dispose(); } dlg.Dispose(); } } |
メニューで[設定] ⇒ [設定を保存]が選択されたら設定を暗号化して保存します。保存されるのはメールサーバーの情報とメールアカウントのIDとパスワード、取得対象とする送信元のメールアドレスなどです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
public partial class Form1 : Form { private void MenuLoadConfig_Click(object sender, EventArgs e) { LoadConfig(); } void LoadConfig() { OpenFileDialog dlg = new OpenFileDialog(); dlg.Filter = "設定ファイル(*.xml)|*.xml"; dlg.DefaultExt = "xml"; if(dlg.ShowDialog() == DialogResult.OK) { FormLoadPass f = new FormLoadPass(); f.ShowDialog(); string masterPassword = f.Password; if(masterPassword != "") { System.Xml.Serialization.XmlSerializer xml = new System.Xml.Serialization.XmlSerializer(typeof(Config)); System.IO.StreamReader sr = new System.IO.StreamReader(dlg.FileName); Config config = (Config)xml.Deserialize(sr); sr.Close(); if(config.IsPasswordRight(masterPassword)) { textBoxMailServer.Text = config.GetMailServer(masterPassword); textBoxPort.Text = config.GetPort(masterPassword); textBoxId.Text = config.GetId(masterPassword); textBoxPass.Text = config.GetPassword(masterPassword); checkBox1.Checked = config.IsAll; textBoxMallAdresses.Text = config.GetFroms(masterPassword); } else { MessageBox.Show("マスターパスワードが正しくありません!", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error); } } f.Dispose(); } dlg.Dispose(); } } |
取得されたメール自体も保存したり、ファイルから読み出せるようにします。使用しているDocクラスはこのコードの下に示します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
public partial class Form1 : Form { private void MenuItemSaveMails_Click(object sender, EventArgs e) { SaveMails(); } void SaveMails() { SaveFileDialog dlg = new SaveFileDialog(); dlg.Filter = "メールデータ(*.xml)|*.xml"; dlg.DefaultExt = "xml"; if(dlg.ShowDialog() == DialogResult.OK) { FormSavePass f = new FormSavePass(); f.ShowDialog(); if(f.Password != "") { Doc doc = new Doc(f.Password, Datas); System.Xml.Serialization.XmlSerializer xml = new System.Xml.Serialization.XmlSerializer(typeof(Doc)); System.IO.StreamWriter sw = new System.IO.StreamWriter(dlg.FileName); xml.Serialize(sw, doc); sw.Close(); MessageBox.Show("メールを保存しました!"); } f.Dispose(); } dlg.Dispose(); } private void MenuItemLoadMails_Click(object sender, EventArgs e) { LoadMails(); } void LoadMails() { OpenFileDialog dlg = new OpenFileDialog(); if(dlg.ShowDialog() == DialogResult.OK) { FormLoadPass f = new FormLoadPass(); if(f.ShowDialog() == DialogResult.OK) { System.Xml.Serialization.XmlSerializer xml = new System.Xml.Serialization.XmlSerializer(typeof(Doc)); System.IO.StreamReader sr = new System.IO.StreamReader(dlg.FileName); Doc doc = (Doc)xml.Deserialize(sr); sr.Close(); string masterPassword = f.Password; Datas = doc.GetDecodedDatas(masterPassword); // 新しいメールを上側に表示させる Datas = Datas.OrderByDescending(x => x.DateText).ToList(); listBox1.Items.Clear(); foreach(Data data in Datas) { listBox1.Items.Add(data.Subject); } } f.Dispose(); } dlg.Dispose(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class Doc { public Doc() { } public Doc(string masterPassword, List<Data> datas) { foreach(Data data in datas) { data.EncodeData(masterPassword); } EncodedDatas = datas; } public List<Data> GetDecodedDatas(string masterPassword) { foreach(Data data in EncodedDatas) { data.DecodeData(masterPassword); } return EncodedDatas; } public List<Data> EncodedDatas = null; } |
[すべて通知する]にチェックされている場合はテキストボックスをEnabled = falseにします。
1 2 3 4 5 6 7 8 9 10 |
public partial class Form1 : Form { private void checkBox1_CheckedChanged(object sender, EventArgs e) { if(checkBox1.Checked) textBoxMallAdresses.Enabled = false; else textBoxMallAdresses.Enabled = true; } } |
[メール] ⇒ [クリア]が選択されたらリストボックスのアイテムを全消去してDatasの要素もクリアします。
1 2 3 4 5 6 7 8 |
public partial class Form1 : Form { private void MenuItemClear_Click(object sender, EventArgs e) { listBox1.Items.Clear(); Datas.Clear(); } } |
Configは設定に関する情報を管理するためのものです。コンストラクタにわたされた文字列を暗号化してフィールド変数に格納します。またGet~メソッドで復号された文字列を返します。
IsPasswordRight(string masterPassword)メソッドはファイルを開くときに入力されたマスターパスワードが正しいかどうかを調べるためのものです。ファイル保存のときに渡されたマスターパスワードのハッシュとIsPasswordRightに渡された文字列のハッシュが一致するかどうかで正しいパスワードかどうかを判定しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
public class Config { public Config() { } public Config(string masterPassword, string mailServer, string port, string id, string password, bool isAll, string froms) { MasterPasswordHash = GetPasswordHash(masterPassword); EncodeMailServer = Encode.EncodeString(mailServer, masterPassword); EncodePort = Encode.EncodeString(port, masterPassword); EncodeId = Encode.EncodeString(id, masterPassword); EncodePassword = Encode.EncodeString(password, masterPassword); IsAll = isAll; EncodeFroms = Encode.EncodeString(froms, masterPassword); } string GetPasswordHash(string password) { using(System.Security.Cryptography.SHA256 mySHA256 = System.Security.Cryptography.SHA256.Create()) { byte[] data = Encoding.UTF8.GetBytes(password); byte[] hashValue = mySHA256.ComputeHash(data); return Convert.ToBase64String(hashValue); } } public bool IsPasswordRight(string masterPassword) { return MasterPasswordHash == GetPasswordHash(masterPassword); } public string GetMailServer(string masterPassword) { return Encode.DecodeString(EncodeMailServer, masterPassword); } public string GetPort(string masterPassword) { return Encode.DecodeString(EncodePort, masterPassword); } public string GetId(string masterPassword) { return Encode.DecodeString(EncodeId, masterPassword); } public string GetPassword(string masterPassword) { return Encode.DecodeString(EncodePassword, masterPassword); } public string GetFroms(string masterPassword) { return Encode.DecodeString(EncodeFroms, masterPassword); } public bool IsAll = true; public string EncodeFroms = ""; public string MasterPasswordHash = ""; public string EncodeMailServer = ""; public string EncodePort = ""; public string EncodeId = ""; public string EncodePassword = ""; } |
Dataはコンストラクタの引数が異なるだけで、これまで使ってきたものとほとんど同じです。GetDateTime(string str)メソッドはMon, 19 Oct 2020 19:33:02 +0900のような文字列をDateTime型に変換するためのものですが、Mon, 19 Oct 2020 19:33:02 +0900 (JST)となっていたり、Mon, 19 Oct 2020 19:33:02 +0900 (JST)と半角スペースが余分に入っているとうまく機能しないので改良を加えています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
public class Data { public Data() { } public Data(string subject, string date, string mainText, string from) { Subject = subject; MainText = mainText; Date = GetDateTime(date); DateText = Date.ToString("yyyy-MM-dd"); From = from; } [System.Xml.Serialization.XmlIgnore] public string Subject { get; private set; } [System.Xml.Serialization.XmlIgnore] public DateTime Date { get; private set; } [System.Xml.Serialization.XmlIgnore] public string MainText { get; private set; } [System.Xml.Serialization.XmlIgnore] public string DateText { get; private set; } [System.Xml.Serialization.XmlIgnore] public string From { get; private set; } DateTime GetDateTime(string str) { try { str = str.Replace(" ", " "); int index = str.IndexOf(" ("); if(index != -1) str = str.Substring(0, index); else { index = str.IndexOf("("); if(index != -1) str = str.Substring(0, index); } return System.DateTime.ParseExact(str, "ddd, d MMM yyyy HH':'mm':'ss zzz", System.Globalization.DateTimeFormatInfo.InvariantInfo, System.Globalization.DateTimeStyles.None); } catch { return DateTime.MinValue; } } public string EncodedSubject = ""; public string EncodedDateText = ""; public string EncodedMainText = ""; public string EncodedFrom = ""; public void EncodeData(string password) { EncodedSubject = Encode.EncodeString(Subject, password); EncodedDateText = Encode.EncodeString(DateText, password); EncodedMainText = Encode.EncodeString(MainText, password); EncodedFrom = Encode.EncodeString(From, password); } public void DecodeData(string password) { Subject = Encode.DecodeString(EncodedSubject, password); DateText = Encode.DecodeString(EncodedDateText, password); MainText = Encode.DecodeString(EncodedMainText, password); From = Encode.DecodeString(EncodedFrom, password); } } |