/// <summary> /// Initialise form with a condition to update /// </summary> /// <param name="_conditionToUpdate"></param> public Condition_Editor(int _conditionToUpdate) { isUpdating = true; conditionToUpdate = _conditionToUpdate; //Initialise form options Initialise(); //Set form element values from condition info Core.Condition c = EditorConditionsList.conditions[conditionToUpdate]; conditionNameTextBox.Text = c.conditionName; emailPropertyComboBox.Text = c.emailProperty.ToString(); recieverEmailTextBox.Text = string.Join(Environment.NewLine, c.recieverEmails.ToArray()); alertTypeComboBox.Text = c.alertType.ToString().Replace('_', ' '); foreach (Core.Trigger trigger in c.triggers.Values) { triggerDataGridView.Rows.Add(); DataGridViewRow newRow = triggerDataGridView.Rows[triggerDataGridView.Rows.Count - 2]; DataGridViewComboBoxCell comboBoxCell = (DataGridViewComboBoxCell)(newRow.Cells[0]); foreach (Core.vrsProperty property in Enum.GetValues(typeof(Core.vrsProperty))) { comboBoxCell.Items.Add(property.ToString()); } newRow.Cells[0].Value = trigger.Property.ToString(); newRow.Cells[1].Value = trigger.ComparisonType; newRow.Cells[2].Value = trigger.Value; } }
/// <summary> /// Load conditions /// </summary> public static void LoadConditions() { try { //Clear conditions and active matches Core.conditions.Clear(); Core.activeMatches.Clear(); //Parse conditions file JObject conditionJson; using (FileStream filestream = new FileStream("conditions.json", FileMode.Open)) using (StreamReader reader = new StreamReader(filestream)) using (JsonTextReader jsonreader = new JsonTextReader(reader)) conditionJson = JsonSerializer.Create().Deserialize <JObject>(jsonreader); if (conditionJson == null) { return; } //Iterate parsed conditions for (int conditionid = 0; conditionid < conditionJson.Count; conditionid++) { JToken condition = conditionJson[conditionid.ToString()]; //Create condition and copy values Core.Condition newCondition = new Core.Condition { conditionName = condition["conditionName"].ToString(), emailProperty = (Core.vrsProperty)Enum.Parse(typeof(Core.vrsProperty), condition["emailProperty"].ToString()), alertType = (Core.AlertType)Enum.Parse(typeof(Core.AlertType), condition["alertType"].ToString()), ignoreFollowing = (bool)condition["ignoreFollowing"] }; List <string> emailsArray = new List <string>(); foreach (JToken email in condition["recieverEmails"]) { emailsArray.Add(email.ToString()); } newCondition.recieverEmails = emailsArray; foreach (JToken trigger in condition["triggers"].Values()) { newCondition.triggers.Add(newCondition.triggers.Count, new Core.Trigger((Core.vrsProperty)Enum.Parse(typeof(Core.vrsProperty), trigger["Property"].ToString()), trigger["Value"].ToString(), trigger["ComparisonType"].ToString())); } //Add condition to list Core.conditions.Add(conditionid, newCondition); } //Try to clean up json parsing conditionJson.RemoveAll(); conditionJson = null; //Update condition list Core.UI.Invoke((MethodInvoker)(() => { Core.UI.updateConditionList(); })); Core.UI.conditionTreeView.Nodes[0].Expand(); //Log to UI Core.UI.writeToConsole("Conditions Loaded", Color.White); //Restart threads if (ThreadManager.threadStatus != ThreadManager.CheckerStatus.WaitingForLoad) { ThreadManager.StartOrRestart(); } } catch (Exception e) { MessageBox.Show(e.Message + "\n\n" + e.StackTrace); } }
/// <summary> /// Update condition list /// </summary> public void updateConditionList() { conditionEditorTreeView.Nodes.Clear(); foreach (int conditionid in EditorConditionsList.conditions.Keys) { Core.Condition condition = EditorConditionsList.conditions[conditionid]; TreeNode conditionNode = conditionEditorTreeView.Nodes.Add(conditionid + ": " + condition.conditionName); conditionNode.Tag = conditionid; conditionNode.Nodes.Add("Id: " + conditionid); conditionNode.Nodes.Add("Email Parameter: " + condition.emailProperty.ToString()); conditionNode.Nodes.Add("Reciever Emails: " + string.Join(", ", condition.recieverEmails.ToArray())); conditionNode.Nodes.Add("Alert Type: " + condition.alertType); TreeNode triggersNode = conditionNode.Nodes.Add("Condition Triggers"); foreach (Core.Trigger trigger in condition.triggers.Values) { triggersNode.Nodes.Add(trigger.Property.ToString() + " " + trigger.ComparisonType + " " + trigger.Value); } } }
/// <summary> /// Updates the condition list /// </summary> public void updateConditionList() { conditionTreeView.Nodes[0].Nodes.Clear(); foreach (int conditionid in Core.conditions.Keys) { TreeNode conditionNode; Core.Condition c = Core.conditions[conditionid]; conditionNode = conditionTreeView.Nodes[0].Nodes.Add("Name: " + c.conditionName); conditionNode.Nodes.Add("Id: " + conditionid); conditionNode.Nodes.Add("Email Parameter: " + c.emailProperty.ToString()); conditionNode.Nodes.Add("Reciever Email: " + string.Join(", ", c.recieverEmails.ToArray())); conditionNode.Nodes.Add("Alert Type: " + c.alertType); conditionNode.Nodes.Add("Emails Sent: " + c.emailsThisSession.ToString()); TreeNode triggersNode = conditionNode.Nodes.Add("Condition Triggers"); foreach (Core.Trigger trigger in c.triggers.Values) { triggersNode.Nodes.Add(trigger.Property.ToString() + " " + trigger.ComparisonType + " " + trigger.Value); } } }
public ConditionEditor() { //Initialise form elements InitializeComponent(); //Load conditions if (File.Exists("conditions.json")) { EditorConditionsList.conditions.Clear(); //Parse json string conditionsJsonText = File.ReadAllText("conditions.json"); JObject conditionJson = (JObject)JsonConvert.DeserializeObject(conditionsJsonText); if (conditionJson != null) { //Iterate parsed conditions for (int conditionid = 0; conditionid < conditionJson.Count; conditionid++) { JToken condition = conditionJson[conditionid.ToString()]; //Create condition from parsed json Core.Condition newCondition = new Core.Condition(); newCondition.conditionName = condition["conditionName"].ToString(); newCondition.emailProperty = (Core.vrsProperty)Enum.Parse(typeof(Core.vrsProperty), condition["emailProperty"].ToString()); newCondition.alertType = (Core.AlertType)Enum.Parse(typeof(Core.AlertType), condition["alertType"].ToString()); newCondition.ignoreFollowing = (bool)condition["ignoreFollowing"]; List <string> emailsArray = new List <string>(); foreach (JToken email in condition["recieverEmails"]) { emailsArray.Add(email.ToString()); } newCondition.recieverEmails = emailsArray; foreach (JToken trigger in condition["triggers"].Values()) { newCondition.triggers.Add(newCondition.triggers.Count, new Core.Trigger((Core.vrsProperty)Enum.Parse(typeof(Core.vrsProperty), trigger["Property"].ToString()), trigger["Value"].ToString(), trigger["ComparisonType"].ToString())); } //Add condition to list EditorConditionsList.conditions.Add(conditionid, newCondition); } } } updateConditionList(); }
/// <summary> /// Move down button /// </summary> /// <param name="sender">Sender</param> /// <param name="e">Event Args</param> private void moveDownButton_Click(object sender, EventArgs e) { //Cancel if selected node is invalid if (conditionEditorTreeView.SelectedNode == null || conditionEditorTreeView.SelectedNode.Tag == null || conditionEditorTreeView.SelectedNode.Tag.ToString() == "") { return; } //Swap conditions then update condition list int conditionid = Convert.ToInt32(conditionEditorTreeView.SelectedNode.Tag); if (conditionid == EditorConditionsList.conditions.Count - 1) { return; } Core.Condition c1 = EditorConditionsList.conditions[conditionid]; Core.Condition c2 = EditorConditionsList.conditions[conditionid + 1]; EditorConditionsList.conditions.Remove(conditionid + 1); EditorConditionsList.conditions.Remove(conditionid); EditorConditionsList.conditions.Add(conditionid + 1, c1); EditorConditionsList.conditions.Add(conditionid, c2); updateConditionList(); }
/// <summary> /// Save button click /// </summary> /// <param name="sender">Sender</param> /// <param name="e">Event Args</param> void SaveButtonClick(object sender, EventArgs e) { //Check if values are empty/invalid bool cancelSave = false; if (conditionNameTextBox.Text == "") { conditionNameLabel.ForeColor = Color.Red; cancelSave = true; } else { conditionNameLabel.ForeColor = SystemColors.ControlText; } if (emailPropertyComboBox.Text == "") { emailPropertyLabel.ForeColor = Color.Red; cancelSave = true; } else { emailPropertyLabel.ForeColor = SystemColors.ControlText; } if (recieverEmailTextBox.Text == "") { emailToSendToLabel.ForeColor = Color.Red; cancelSave = true; } else { emailToSendToLabel.ForeColor = SystemColors.ControlText; } if (alertTypeComboBox.Text == "") { alertTypeLabel.ForeColor = Color.Red; cancelSave = true; } else { alertTypeLabel.ForeColor = SystemColors.ControlText; } if (triggerDataGridView.Rows.Count == 1) { triggerDataGridView.BackgroundColor = Color.Red; cancelSave = true; } else { triggerDataGridView.BackgroundColor = SystemColors.AppWorkspace; } //Check if emails are valid foreach (string line in recieverEmailTextBox.Lines) { try { new System.Net.Mail.MailAddress(line); } catch (FormatException) { emailToSendToLabel.ForeColor = Color.Red; cancelSave = true; break; } } //Cancel if values are invalid if (cancelSave) { return; } //If condition is being updated, remove the old one if (isUpdating) { EditorConditionsList.conditions.Remove(conditionToUpdate); } //Sort conditions List <int> list = EditorConditionsList.conditions.Keys.ToList(); SortedDictionary <int, Core.Condition> sortedConditions = new SortedDictionary <int, Core.Condition>(); list.Sort(); foreach (var key in list) { sortedConditions.Add(key, EditorConditionsList.conditions[key]); } EditorConditionsList.conditions = sortedConditions; //Create new condition Core.Condition newCondition = new Core.Condition { conditionName = conditionNameTextBox.Text, emailProperty = (Core.vrsProperty)Enum.Parse(typeof(Core.vrsProperty), emailPropertyComboBox.Text), recieverEmails = recieverEmailTextBox.Text.Split(new string[] { Environment.NewLine }, StringSplitOptions.None).ToList(), alertType = (Core.AlertType)Enum.Parse(typeof(Core.AlertType), alertTypeComboBox.Text.Replace(' ', '_')), ignoreFollowing = ignoreFollowingCheckbox.Checked }; if (triggerDataGridView.Rows.Count != 0) { foreach (DataGridViewRow row in triggerDataGridView.Rows) { if (row.Index != triggerDataGridView.Rows.Count - 1) { foreach (Core.vrsProperty property in Enum.GetValues(typeof(Core.vrsProperty))) { if (property.ToString() == row.Cells[0].Value.ToString()) { newCondition.triggers.Add(newCondition.triggers.Count, new Core.Trigger(property, row.Cells[2].Value.ToString(), row.Cells[1].Value.ToString())); break; } } } } } //Add condition to condition list if (isUpdating) { EditorConditionsList.conditions.Add(conditionToUpdate, newCondition); } else { EditorConditionsList.conditions.Add(EditorConditionsList.conditions.Count, newCondition); } //Close form Close(); }
/// <summary> /// Send alert email /// </summary> /// <param name="emailaddress">Email address to send to</param> /// <param name="message">Message to send</param> /// <param name="condition">Condition that triggered alert</param> /// <param name="aircraft">Aircraft information for matched aircraft</param> /// <param name="recieverName">Name of receiver that got the last aircraft information</param> /// <param name="emailPropertyInfo">String for displaying email property value</param> /// <param name="isDetection">Is this a detection, not a removal?</param> public static void SendEmail(string emailaddress, MailMessage message, Core.Condition condition, Core.Aircraft aircraft, string recieverName, string emailPropertyInfo, bool isDetection) { //Array to store position trail Dictionary <int, string[]> pathArray = new Dictionary <int, string[]>(); //Google status map for position trail's url string staticMapUrl = ""; //Google maps url string googleMapsUrl = ""; //Transponder type from aircraft info string transponderName = ""; //Types of transponders string[] transponderTypes = new string[] { "Unknown", "Mode-S", "ADS-B", "ADS-Bv1", "ADS-Bv2" }; //Aircraft image urls string[] imageLinks = new string[2]; //Table for displaying aircraft property values string aircraftTable = "<table style='border: 2px solid;border-radius: 10px;border-spacing: 0px;' id='acTable'>"; //HTML for aircraft images string imageHTML = ""; //Airframes.org url string airframesUrl = ""; //Report url string reportUrl = ""; //Set message type to html message.IsBodyHtml = true; //Add email to message receiver list try { message.To.Clear(); message.To.Add(emailaddress); } catch { Core.UI.writeToConsole("ERROR: Email to send to is invalid (" + emailaddress + ")", Color.Red); return; } //Set sender email to the one set in settings try { message.From = new MailAddress(Settings.senderEmail, "PlaneAlerter Alerts"); } catch { Core.UI.writeToConsole("ERROR: Email to send from is invalid (" + Settings.senderEmail + ")", Color.Red); return; } if (Settings.EmailContentConfig.TwitterOptimised) { string typestring = "First Contact"; string regostring = "No rego, "; string callsignstring = "No callsign, "; string operatorstring = "No operator, "; string emailpropertystring = "No " + condition.emailProperty.ToString(); if (!isDetection) { typestring = "Last Contact"; } if (aircraft.GetProperty("Reg") != null) { regostring = aircraft.GetProperty("Reg") + ", "; } if (aircraft.GetProperty("Call") != null) { callsignstring = aircraft.GetProperty("Call") + ", "; } if (aircraft.GetProperty("OpIcao") != null) { operatorstring = aircraft.GetProperty("OpIcao") + ", "; } string emailpropertyvalue = aircraft.GetProperty(Core.vrsPropertyData[condition.emailProperty][2]); if (emailpropertyvalue != null) { emailpropertystring = emailpropertyvalue; } message.Body = "[" + typestring + "] " + condition.conditionName + " (" + emailpropertystring + "), " + regostring + operatorstring + aircraft.GetProperty("Type") + ", " + callsignstring + Settings.radarUrl; //[Last Contact] USAF (RCH9701), 79-1951, RCH, DC10, RCH9701 http://seqldradar.net } else { //Create request for aircraft image urls HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Settings.acListUrl.Substring(0, Settings.acListUrl.LastIndexOf("/") + 1) + "AirportDataThumbnails.json?icao=" + aircraft.ICAO + "&numThumbs=" + imageLinks.Length); request.Method = "GET"; request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip; request.Timeout = 5000; //If vrs authentication is used, add credentials to request if (Settings.VRSAuthenticate) { string encoded = Convert.ToBase64String(Encoding.GetEncoding("ISO-8859-1").GetBytes(Settings.VRSUsr + ":" + Settings.VRSPwd)); request.Headers.Add("Authorization", "Basic " + encoded); } //Send request and parse response try { //Get response HttpWebResponse imageResponse = (HttpWebResponse)request.GetResponse(); byte[] imageResponseBytes = new byte[32768]; imageResponse.GetResponseStream().Read(imageResponseBytes, 0, 32768); string imageResponseText = Encoding.ASCII.GetString(imageResponseBytes); //Parse json JObject imageResponseJson = (JObject)JsonConvert.DeserializeObject(imageResponseText); //If status is not 404, add images to image HTML if (imageResponseJson["status"].ToString() != "404") { foreach (JObject image in imageResponseJson["data"]) { imageHTML += "<img style='margin: 5px;border: 2px solid;border-radius: 10px;' src='" + image + "' />"; } } imageHTML += "<br>"; } catch (Exception) { } //If aircraft has a position, generate a google map url if (aircraft.GetProperty("Lat") != null) { if (aircraft.Trail.Count() != 3) { staticMapUrl = "http://maps.googleapis.com/maps/api/staticmap?center=" + aircraft.GetProperty("Lat") + "," + aircraft.GetProperty("Long") + "&size=800x800&markers=" + aircraft.GetProperty("Lat") + "," + aircraft.GetProperty("Long") + "&key=AIzaSyCJxiyiDWBHiYSMm7sjSTJkQubuo3XuR7s&path=color:0x000000|"; } else { staticMapUrl = "http://maps.googleapis.com/maps/api/staticmap?center=" + aircraft.GetProperty("Lat") + "," + aircraft.GetProperty("Long") + "&size=800x800&zoom=8&markers=" + aircraft.GetProperty("Lat") + "," + aircraft.GetProperty("Long") + "&key=AIzaSyCJxiyiDWBHiYSMm7sjSTJkQubuo3XuR7s&path=color:0x000000|"; } } //Process aircraft trail for (int i = 0; i < aircraft.Trail.Count() / 3; i++) { //Get coordinate string[] coord = new string[] { aircraft.Trail[i * 3].ToString(), aircraft.Trail[i * 3 + 1].ToString(), aircraft.Trail[i * 3 + 2].ToString() }; //Add coordinate to google map url staticMapUrl += coord[0] + "," + coord[1]; //If this is not the last coordinate, add a separator if (i != (aircraft.Trail.Count() / 3) - 1) { staticMapUrl += "|"; } } //Generate google maps url googleMapsUrl = "https://www.google.com.au/maps/search/" + aircraft.GetProperty("Lat") + "," + aircraft.GetProperty("Long"); //Generate airframes.org url if (aircraft.GetProperty("Reg") != null && aircraft.GetProperty("Reg") != "") { airframesUrl = "<h3><a style='text-decoration: none;' href='http://www.airframes.org/reg/" + aircraft.GetProperty("Reg").Replace("-", "").ToUpper() + "'>Airframes.org Lookup</a></h3>"; } //Generate radar url try { if (Settings.radarUrl[Settings.radarUrl.Length - 1] == '/') { reportUrl = Settings.radarUrl + "desktopReport.html?icao-Q=" + aircraft.ICAO; } else { reportUrl = Settings.radarUrl + "/desktopReport.html?icao-Q=" + aircraft.ICAO; } } catch { Core.UI.writeToConsole("ERROR: No radar url specified.", Color.Red); ThreadManager.StartOrRestart(); } //Get name of transponder type transponderName = transponderTypes[Convert.ToInt32(aircraft.GetProperty("Trt")) - 1]; //Write to log and UI Core.logFileSW.WriteLine(DateTime.Now.ToLongTimeString() + " | SENDING ALERT: " + Environment.NewLine + aircraft.ToString() + Environment.NewLine + Environment.NewLine); Core.UI.writeToConsole(DateTime.Now.ToLongTimeString() + " | SENDING | " + aircraft.ICAO + " | Condition: " + condition.conditionName + " (" + emailPropertyInfo + ")", Color.LightBlue); //Generate aircraft property value table bool isAlternateStyle = false; foreach (string child in aircraft.GetPropertyKeys()) { //TODO ADD TO VRSPROPERTIES string parameter = "UNKNOWN_PARAMETER"; //Set parameter to a readable name if it's not included in vrs property info switch (child.ToString()) { case "CNum": parameter = "Aircraft_Serial"; break; case "EngMount": parameter = "Engine_Mount"; break; case "FSeen": parameter = "First_Seen"; break; case "HasPic": parameter = "Has_Picture"; break; case "Id": parameter = "Id"; break; case "Lat": parameter = "Latitude"; break; case "Long": parameter = "Longitude"; break; case "PosTime": parameter = "Position_Time"; break; case "ResetTrail": parameter = "Reset_Trail"; break; case "Tisb": parameter = "TIS-B"; break; case "Trak": parameter = "Track"; break; case "TrkH": parameter = "Is_Track_Heading"; break; case "Year": parameter = "Year"; break; } //If parameter is set (not in vrs property info list) and property list type is set to essentials, skip this property if (parameter != "UNKNOWN_PARAMETER" && Settings.EmailContentConfig.PropertyList == Core.PropertyListType.Essentials) { continue; } //Get parameter information from vrs property info if (parameter == "UNKNOWN_PARAMETER") { foreach (Core.vrsProperty property in Core.vrsPropertyData.Keys) { if (Core.vrsPropertyData[property][2] == child.ToString()) { //If property list type is essentials and this property is not in the list of essentials, leave this property as unknown so it can be skipped if (Settings.EmailContentConfig.PropertyList == Core.PropertyListType.Essentials && !Core.essentialProperties.Contains(property)) { continue; } parameter = property.ToString(); } } if (parameter == "UNKNOWN_PARAMETER") { continue; } } //Add html for property if (isAlternateStyle) { aircraftTable += "<tr style='background-color:#CCC'>"; } else { aircraftTable += "<tr>"; } aircraftTable += "<td style='padding: 3px;font-weight:bold;'>" + parameter + "</td>"; aircraftTable += "<td style='padding: 3px'>" + aircraft.GetProperty(child) + "</td>"; aircraftTable += "</tr>"; isAlternateStyle = !isAlternateStyle; } aircraftTable += "</table>"; //Create the main html document message.Body = "<!DOCTYPE html PUBLIC ' -W3CDTD XHTML 1.0 TransitionalEN' 'http:www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'><html><body>"; if (isDetection) { message.Body += "<h1>Plane Alert - Last Contact</h1>"; } else { message.Body += "<h1>Plane Alert - First Contact</h1>"; } //Condition name + email property message.Body += "<h2 style='margin: 0px;margin-bottom: 2px;margin-left: 10px;'>Condition: " + condition.conditionName + " (" + emailPropertyInfo + ")</h2>"; //Receiver name if (Settings.EmailContentConfig.ReceiverName) { message.Body += "<h2 style='margin: 0px;margin-bottom: 2px;margin-left: 10px;'>Reciever: " + recieverName + "</h2>"; } //Transponder type if (Settings.EmailContentConfig.TransponderType) { message.Body += "<h2 style='margin: 0px;margin-bottom: 2px;margin-left: 10px;'>Transponder: " + transponderName + "</h2>"; } //Radar url if (Settings.EmailContentConfig.RadarLink) { message.Body += "<h3><a style='text-decoration: none;' href='" + Settings.radarUrl + "'>Goto Radar</a></h3>"; } //Report url if (Settings.EmailContentConfig.ReportLink) { message.Body += "<h3><a style='text-decoration: none;' href='" + reportUrl + "'>VRS Report Lookup</a></h3>"; } //Airframes.org url if (Settings.EmailContentConfig.AfLookup) { message.Body += airframesUrl; } message.Body += "<table><tr><td>"; //Property list if (Settings.EmailContentConfig.PropertyList != Core.PropertyListType.Hidden) { message.Body += aircraftTable; } message.Body += "</td><td style='padding: 10px;vertical-align: top;'>"; //Aircraft photos if (Settings.EmailContentConfig.AircraftPhotos) { message.Body += imageHTML; } //Map if (Settings.EmailContentConfig.Map && aircraft.GetProperty("Lat") != null) { message.Body += "<h3 style='margin: 0px'><a style='text-decoration: none' href=" + googleMapsUrl + ">Open in Google Maps</a></h3><br />" + "<img style='border: 2px solid;border-radius: 10px;' alt='Loading Map...' src='" + staticMapUrl + "' />"; } message.Body += "</td></tr></table></body></html>"; } //Send the alert try { mailClient.Send(message); } catch (SmtpException e) { if (e.InnerException != null) { Core.UI.writeToConsole("SMTP ERROR: " + e.Message + " (" + e.InnerException.Message + ")", Color.Red); return; } Core.UI.writeToConsole("SMTP ERROR: " + e.Message, Color.Red); return; } catch (InvalidOperationException e) { if (e.InnerException != null) { Core.UI.writeToConsole("SMTP ERROR: " + e.Message + " (" + e.InnerException.Message + ")", Color.Red); return; } Core.UI.writeToConsole("SMTP ERROR: " + e.Message, Color.Red); return; } //Increase sent emails for condition and update stats condition.increaseSentEmails(); Stats.updateStats(); //Log to UI Core.UI.writeToConsole(DateTime.Now.ToLongTimeString() + " | SENT | " + aircraft.ICAO + " | Condition: " + condition.conditionName + " (" + emailPropertyInfo + ")", Color.LightBlue); }