using System; using System.Text.RegularExpressions; using System.Net; using System.IO; using System.Text; using System.Collections; using System.Collections.Specialized; using System.Xml; using System.Data; using System.Data.SqlClient; using System.Timers; using System.Web; //using anmar.SharpMimeTools; public class POP3Main { protected enum service_state {STARTED, PAUSED, STOPPED}; protected service_state state = service_state.STARTED; protected string config_file; protected bool verbose = true; protected string LogFileFolder; protected string LogEnabled = "0"; protected bool suspended = false; static object dummy = new object(); protected Timer timer; protected int FetchIntervalInMinutes = 15; protected System.Collections.ArrayList websites; protected string MessageInputFile; protected string MessageOutputFile; protected string ConnectionString; protected string Pop3Server; protected string Pop3Port; protected string Pop3UseSSL; protected string SubjectMustContain; protected string SubjectCannotContain; protected string FromMustContain; protected string FromCannotContain; protected string DeleteMessagesOnServer; protected string InsertBugUrl; protected string ServiceUsername; protected string ServicePassword; protected string TrackingIdString; protected int TotalErrorsAllowed = 999999; protected int total_error_count = 0; protected int ReadInputStreamCharByChar = 0; System.Threading.Thread thread; /////////////////////////////////////////////////////////////////// public POP3Main(string config_file, bool verbose) { this.config_file = config_file; this.verbose = verbose; get_settings(); write_line("creating"); thread = new System.Threading.Thread(new System.Threading.ThreadStart(thread_proc)); thread.Start(); } /////////////////////////////////////////////////////////////////// public void start() { // call do_work() write_line("starting"); state = service_state.STARTED; } /////////////////////////////////////////////////////////////////// public void pause() { write_line("pausing"); state = service_state.PAUSED; } /////////////////////////////////////////////////////////////////// public void stop() { write_line("stopping"); state = service_state.STOPPED; } /////////////////////////////////////////////////////////////////////// public string get_log_file_path() { // determine log file name DateTime now = DateTime.Now; string now_string = (now.Year).ToString() + "_" + (now.Month).ToString("0#") + "_" + (now.Day).ToString("0#"); string path = LogFileFolder + "\\" + "btnet_service_log_" + now_string + ".txt"; return path; } /////////////////////////////////////////////////////////////////////// public void write_to_log(string s) { string path = get_log_file_path(); lock(dummy) { System.IO.StreamWriter w = System.IO.File.AppendText(path); w.WriteLine(DateTime.Now.ToLongTimeString() + " " + s); w.Close(); } } /////////////////////////////////////////////////////////////////// public void write_line(object o) { if (LogEnabled == "1") { write_to_log(Convert.ToString(o)); } if (verbose) { Console.WriteLine(o); } } /////////////////////////////////////////////////////////////////// public void thread_proc() { write_line ("entering thread"); do_work(null, null); while (true) { System.Threading.Thread.Sleep(2000); if (state == service_state.STOPPED) { timer.Enabled = false; break; } } write_line ("exiting thread"); } /////////////////////////////////////////////////////////////////// public void do_work(object source, ElapsedEventArgs eea) { write_line ("doing work"); if (state != service_state.STARTED) { write_line ("not in STARTED state"); } else { get_settings(); for (int i = 0; i < websites.Count; i++) { if (state != service_state.STARTED) { break; } StringDictionary settings = (StringDictionary) websites[i]; MessageInputFile = settings["MessageInputFile"]; MessageOutputFile = settings["MessageOutputFile"]; ConnectionString = settings["ConnectionString"]; Pop3Server = settings["Pop3Server"]; Pop3Port = settings["Pop3Port"]; Pop3UseSSL = settings["Pop3UseSSL"]; SubjectMustContain = settings["SubjectMustContain"]; SubjectCannotContain = settings["SubjectCannotContain"]; FromMustContain = settings["FromMustContain"]; FromCannotContain = settings["FromCannotContain"]; DeleteMessagesOnServer = settings["DeleteMessagesOnServer"]; InsertBugUrl = settings["InsertBugUrl"]; ServiceUsername = settings["ServiceUsername"]; ServicePassword = settings["ServicePassword"]; TrackingIdString = settings["TrackingIdString"]; write_line("*** fetching messages for website " + Convert.ToString(i+1) + " " + InsertBugUrl); fetch_messages_for_projects(); } } resume(); // reset the timer } /////////////////////////////////////////////////////////////////// public void resume() { // Set up a timer so that we keep fetching messages if (timer != null) { timer.Stop(); timer.Dispose(); } timer = new System.Timers.Timer(); timer.AutoReset = false; timer.Elapsed+=new ElapsedEventHandler(do_work); // Set the timer interval timer.Interval= 60 * 1000 * FetchIntervalInMinutes; timer.Enabled=true; } /////////////////////////////////////////////////////////////////// protected void get_settings() { write_line ("get_settings"); websites = new ArrayList(); StringDictionary settings = null; string filename = config_file; XmlTextReader tr = null; try { tr = new XmlTextReader(filename); while(tr.Read()) { //continue; if (tr.Name == "add") { if (tr["key"] == "FetchIntervalInMinutes") { write_line(tr["key"] + "=" + tr["value"]); FetchIntervalInMinutes = Convert.ToInt32(tr["value"]); } else if (tr["key"] == "TotalErrorsAllowed") { write_line(tr["key"] + "=" + tr["value"]); TotalErrorsAllowed = Convert.ToInt32(tr["value"]); } else if (tr["key"] == "ReadInputStreamCharByChar") { write_line(tr["key"] + "=" + tr["value"]); ReadInputStreamCharByChar = Convert.ToInt32(tr["value"]); } else if (tr["key"] == "LogFileFolder") { write_line(tr["key"] + "=" + tr["value"]); LogFileFolder = Convert.ToString(tr["value"]); } else if (tr["key"] == "LogEnabled") { write_line(tr["key"] + "=" + tr["value"]); LogEnabled = Convert.ToString(tr["value"]); } else { if (tr["key"] == "ConnectionString" || tr["key"] == "Pop3Server" || tr["key"] == "Pop3Port" || tr["key"] == "Pop3UseSSL" || tr["key"] == "SubjectMustContain" || tr["key"] == "SubjectCannotContain" || tr["key"] == "FromMustContain" || tr["key"] == "FromCannotContain" || tr["key"] == "DeleteMessagesOnServer" || tr["key"] == "FetchIntervalInMinutes" || tr["key"] == "InsertBugUrl" || tr["key"] == "ServiceUsername" || tr["key"] == "ServicePassword" || tr["key"] == "TrackingIdString" || tr["key"] == "MessageInputFile" || tr["key"] == "MessageOutputFile") { write_line(tr["key"] + "=" + tr["value"]); if (settings != null) { settings[tr["key"]] = tr["value"]; } } } // else an uninteresting setting } else { // create a new dictionary of settings each time we encounter a new Website section if (tr.Name.ToLower() == "website" && tr.NodeType == XmlNodeType.Element) { settings = new System.Collections.Specialized.StringDictionary(); settings["MessageInputFile"] = ""; settings["MessageOutputFile"] = ""; settings["ConnectionString"] = ""; settings["Pop3Server"] = ""; settings["Pop3Port"] = ""; settings["Pop3UseSSL"] = ""; settings["SubjectMustContain"] = ""; settings["SubjectCannotContain"] = ""; settings["FromMustContain"] = ""; settings["FromCannotContain"] = ""; settings["DeleteMessagesOnServer"] = ""; settings["InsertBugUrl"] = ""; settings["ServiceUsername"] = ""; settings["ServicePassword"] = ""; settings["TrackingIdString"] = ""; websites.Add(settings); write_line("*** loading settings for website " + Convert.ToString(websites.Count)); } } } } catch (Exception e) { write_line("Error trying to read file: " + filename); write_line(e); } tr.Close(); } /////////////////////////////////////////////////////////////////////// protected void fetch_messages_for_projects() { // Get the list of accounts to read try { string sql = @"select pj_id, pj_pop3_username, pj_pop3_password from projects where pj_enable_pop3 = 1"; DataSet ds = get_dataset(sql); foreach (DataRow dr in ds.Tables[0].Rows) { if (state != service_state.STARTED) { break; } write_line ("processing project " + Convert.ToString(dr["pj_id"]) + " using account " + dr["pj_pop3_username"]); fetch_messages ( (string) dr["pj_pop3_username"], (string) dr["pj_pop3_password"], (int) dr["pj_id"]); } } catch (Exception e) { write_line("Error trying to process messages"); write_line(e); return; } } /////////////////////////////////////////////////////////////////////// protected void fetch_messages(string user, string password, int projectid) { string[] messages = null; Regex regex = new Regex("\r\n"); string[] test_message_text = new string[100]; POP3Client.POP3client client = null; if (MessageInputFile == "") { try { client = new POP3Client.POP3client(ReadInputStreamCharByChar); write_line ("****connecting to server:"); int port = 110; if (Pop3Port != "") { port = Convert.ToInt32(Pop3Port); } bool use_ssl = false; if (Pop3UseSSL != "") { use_ssl = Pop3UseSSL == "1" ? true : false; } write_line (client.connect (Pop3Server, port, use_ssl)); write_line ("sending POP3 command USER"); write_line (client.USER (user)); write_line ("sending POP3 command PASS"); write_line (client.PASS (password)); write_line ("sending POP3 command STAT"); write_line (client.STAT () ); write_line ("sending POP3 command LIST"); string list; list = client.LIST (); write_line ("list follows:"); write_line (list); messages = regex.Split(list); } catch (Exception e) { write_line("Exception trying to talk to pop3server"); write_line(e); return; } } else { StringBuilder builder = new StringBuilder(4096); write_line ("opening test input file " + MessageInputFile); using (FileStream fs = File.OpenRead(MessageInputFile)) { byte[] b = new byte[4096]; //UTF8Encoding encoding = new UTF8Encoding(true); // Does not work... int bytes_read = fs.Read(b,0,b.Length); while (bytes_read > 0) { //test_messages += encoding.GetString(b); // Does not work.... for (int i = 0; i < bytes_read; i++) { builder.Append(Convert.ToChar(b[i])); // Does work } bytes_read = fs.Read(b,0,b.Length); } } string test_messages = builder.ToString(); Regex test_regex = new Regex("Q6Q6\r\n"); test_message_text = test_regex.Split(test_messages); } string message; int message_number = 0; int start; int end; if (MessageInputFile == "") { start = 1; end = messages.Length-1; } else { start = 0; end = test_message_text.Length; if (end > 99) end = 99; } // loop through the messages for (int i = start;i < end; i++) { if (state != service_state.STARTED) { break; } // fetch the message write_line ("i:" + Convert.ToString(i)); if (MessageInputFile == "") { int space_pos = messages[i].IndexOf(" "); message_number = Convert.ToInt32(messages[i].Substring(0,space_pos)); message = client.RETR (message_number); } else { message = test_message_text[message_number++]; } // for diagnosing problems if (MessageOutputFile != "") { System.IO.StreamWriter w = System.IO.File.AppendText(MessageOutputFile); w.WriteLine(message); w.Flush(); w.Close(); } // break the message up into lines string [] lines = regex.Split(message); string from = ""; string subject = ""; bool encountered_subject = false; bool encountered_from = false; // Loop through the lines of a message. // Pick out the subject and body for (int j=0;j < lines.Length; j++) { if (state != service_state.STARTED) { break; } // We know from // http://www.devnewsgroups.net/group/microsoft.public.dotnet.framework/topic62515.aspx // that headers can be lowercase too. if ( (lines[j].IndexOf("Subject: ") == 0 || lines[j].IndexOf("subject: ") == 0 ) && !encountered_subject) { subject = lines[j].Replace("Subject: ",""); subject = subject.Replace("subject: ",""); // try lowercase too if (j+1 < lines.Length) { if (lines[j+1].StartsWith("\t")) { subject += lines[j+1].Substring(1); } } // subject = SharpMimeTools.parserfc2047Header(subject); encountered_subject = true; } else if (lines[j].IndexOf("From: ") == 0 && !encountered_from) { from = lines[j].Replace("From: ",""); encountered_from = true; } else if (lines[j].IndexOf("from: ") == 0 && !encountered_from) { from = lines[j].Replace("from: ",""); encountered_from = true; } } // end for each line write_line("\nFrom: " + from); write_line("Subject: " + subject); if (SubjectMustContain != "" && subject.IndexOf(SubjectMustContain) < 0) { write_line("skipping because subject does not contain: " + SubjectMustContain); continue; } if (SubjectCannotContain != "" && subject.IndexOf(SubjectCannotContain) >= 0) { write_line("skipping because subject cannot contain: " + SubjectCannotContain); continue; } if (FromMustContain != "" && from.IndexOf(FromMustContain) < 0) { write_line("skipping because from does not contain: " + FromMustContain); continue; } if (FromCannotContain != "" && from.IndexOf(FromCannotContain) >= 0) { write_line("skipping because from cannot contain: " + FromCannotContain); continue; } write_line("calling insert_bug.aspx"); string Url = InsertBugUrl; // Try to parse out the bugid from the subject line string bugidString=TrackingIdString; if (TrackingIdString == "") { bugidString="DO NOT EDIT THIS:"; } int pos = subject.IndexOf(bugidString); if (pos >= 0) { // position of colon pos=subject.IndexOf(":",pos); pos++; // position of close paren int pos2=subject.IndexOf(")",pos); if (pos2 > pos) { string bugid_string = subject.Substring(pos,pos2-pos); write_line("BUGID=" + bugid_string); try { int bugid = Int32.Parse(bugid_string); Url += "?bugid=" + Convert.ToString(bugid); write_line ("updating existing bug " + Convert.ToString(bugid)); } catch(Exception e) { write_line("bugid not numeric " + e.Message); } } } string post_data = "username=" + HttpUtility.UrlEncode(ServiceUsername) + "&password=" + HttpUtility.UrlEncode(ServicePassword) + "&projectid=" + Convert.ToString(projectid) + "&from=" + HttpUtility.UrlEncode(from) + "&short_desc=" + HttpUtility.UrlEncode(subject) + "&message=" + HttpUtility.UrlEncode(message); byte[] bytes = Encoding.UTF8.GetBytes(post_data); // send request to web server HttpWebResponse res = null; try { HttpWebRequest req = (HttpWebRequest) System.Net.WebRequest.Create(Url); req.Method = "POST"; req.ContentType= "application/x-www-form-urlencoded"; req.ContentLength=bytes.Length; Stream request_stream = req.GetRequestStream(); request_stream.Write(bytes,0,bytes.Length); request_stream.Close(); res = (HttpWebResponse) req.GetResponse(); } catch (Exception e) { write_line("HttpWebRequest error url=" + Url); write_line(e); } // examine response if (res != null) { int http_status = (int) res.StatusCode; write_line (Convert.ToString(http_status)); string http_response_header = res.Headers["BTNET"]; res.Close(); if (http_response_header != null) { write_line (http_response_header); // only delete message from pop3 server if we // know we stored in on the web server ok if (MessageInputFile == "" && http_status == 200 && DeleteMessagesOnServer == "1" && http_response_header.IndexOf("OK") == 0) { write_line ("sending POP3 command DELE"); write_line (client.DELE (message_number)); } } else { write_line("BTNET HTTP header not found. Skipping the delete of the email from the server."); write_line("Incrementing total error count"); total_error_count++; } } else { write_line("No response from web server. Skipping the delete of the email from the server."); write_line("Incrementing total error count"); total_error_count++; } if (total_error_count > TotalErrorsAllowed) { write_line("Stopping because total error count > TotalErrorsAllowed"); stop(); } } // end for each message if (MessageInputFile == "") { write_line ("\nsending POP3 command QUIT"); write_line (client.QUIT ()); } else { write_line ("\nclosing input file " + MessageInputFile); } } /////////////////////////////////////////////////////////////////////// protected DataSet get_dataset(string sql) { DataSet ds = new DataSet(); SqlConnection conn = new SqlConnection(ConnectionString); conn.Open(); SqlDataAdapter da = new SqlDataAdapter(sql, conn); da.Fill(ds); return ds; } };