// Occurs before the form region is displayed. // Use this.OutlookItem to get a reference to the current Outlook item. // Use this.OutlookFormRegion to get a reference to the form region. private void DynamicReadingPaneFormRegionShowing(object sender, EventArgs e) { const string SOURCE = CLASS_NAME + "Showing"; Cursor = Cursors.WaitCursor; try { //need this event handler to capture space key winFormHtmlEditor1.KeyDown += HTMLEditor1KeyDown; btnEdit.Text = Resources.label_edit_content; btnDelete.Text = Resources.label_delete_content; var item = (MailItem)OutlookItem; Logger.Verbose(SOURCE, "loading " + item.Subject); var folder = (MAPIFolder)item.Parent; _outlookFolderName = folder.Name; _plainText = (item.BodyFormat == OlBodyFormat.olFormatPlain); _senderName = item.SenderName; _sentDate = string.Format("{0} {1}", item.SentOn.ToString("ddd"), item.SentOn.ToString("g")); _toRecip = item.To; _ccRecip = item.CC; _subject = item.Subject; LoadMessageHeader(); //hide preview handler previewHandlerControl.Visible = false; //read props from the header var safMail = RedemptionLoader.new_SafeMailItem(); safMail.Item = item; _recordKey = Utils.GetRecordKey(safMail); //get primary SMTP of sender _senderAddress = safMail.Sender.SMTPAddress; if (string.IsNullOrEmpty(_senderAddress)) { Logger.Warning(SOURCE, "unable to identify senderAddress for " + item.Subject); } else { Logger.Verbose(SOURCE, "found senderAddress:" + _senderAddress); } if (Utils.IsSpoofed(_senderName, _senderAddress)) { Logger.Warning(SOURCE, string.Format("blocking content fetch for probable spoofed sender {0}:{1}", _senderName, _senderAddress)); MessageBox.Show(this, Resources.spoofed_sender_address, Resources.product_name, MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } Utils.ReadHeaders(item, ref _pointerString, ref _serverName, ref _serverPort, ref _encryptKey, ref _encryptKey2, ref _duration, ref _userAgent, ref _allowForwarding); ThisAddIn.IsMailAllowForwarding = _allowForwarding; ThisAddIn._pointerString = _pointerString; // check for missing/incomplete configuration first //Logger.Verbose(SOURCE,string.Format("checking for store address; store: {0}, supplying {1}", // store.DisplayName, store.StoreID)); var storeAddress = Globals.ThisAddIn.GetStoreAddress(folder.StoreID); if (string.IsNullOrEmpty(storeAddress)) { Logger.Info(SOURCE, "using InternetAccountName as storeAddress"); storeAddress = item.InternetAccountName(); } //Logger.Info(SOURCE,string.Format("store address:{0}, len(StoreID): {1}", // storeAddress, folder.StoreID.Length)); _account = ThisAddIn.GetAccount(storeAddress); if (_account == null && !ThisAddIn.Initialized) { //pause for 1 second and try again Thread.Sleep(1000); _account = ThisAddIn.GetAccount(storeAddress); } _configuration = ThisAddIn.GetMatchingConfiguration(storeAddress, _serverName, _serverPort, true); if (_configuration == null) { //no matching configuration //only raise an alert if the reading pane is not visible //otherwise it will just be a duplicate MessageBox.Show(this, Resources.unknown_server, Resources.product_name, MessageBoxButtons.OK, MessageBoxIcon.Warning); ShowContent("<body/>"); EnableEdits(false); return; } else if (string.IsNullOrEmpty(_configuration.Password)) { Logger.Warning(SOURCE,string.Format("raising missing password alert for {0}; config.Key:{1}, server:{2}, port:{3}, account address: {4}", _subject, _configuration.Key, _serverName, _serverPort, _account == null ? "" : _account.SMTPAddress)); //incomplete configuration MessageBox.Show(this, string.Format( Resources.missing_settings, Environment.NewLine, "Password"), Resources.product_name, MessageBoxButtons.OK, MessageBoxIcon.Warning); ShowContent("<body/>"); EnableEdits(false); return; } //security checks next //sender registration string registrationAlert; var registered = ThisAddIn.CheckSenderRegistration(_senderAddress, _account, out registrationAlert); if (registered != ThisAddIn.RegistrationState.Registered) { ShowContent("<body/>"); EnableEdits(false); string alert; switch (registered) { case ThisAddIn.RegistrationState.NotRegistered: alert = string.Format( Resources.sender_not_registered, _senderAddress); break; case ThisAddIn.RegistrationState.BadCredentials: alert = string.Format(Resources.error_checking_registration, _senderAddress, Resources.invalid_password); break; default: alert = string.Format(Resources.error_checking_registration, _senderAddress, registrationAlert); break; } MessageBox.Show(this, alert, Resources.product_name, MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } //server licensed if (!ThisAddIn.IsServerLicensed(_serverName, _serverPort)) { Logger.Error(SOURCE, string.Format("blocking content fetch for invalid server/port {0}:{1}", _serverName, _serverPort)); MessageBox.Show(this, Resources.invalid_server, Resources.product_name, MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } _entryId = item.EntryID; btnMessage.PanelClick += BtnMessageClick; if (ThisAddIn.NoPreviewer) { tableLayoutPanelMain.Click += BtnMessageClick; foreach (Control ctl in tableLayoutPanelMain.Controls) { if (ctl is Label || ctl is TableLayoutPanel) { ctl.Click += BtnMessageClick; } } winFormHtmlEditor1.Click += HTMLEditor1Click; Click += BtnMessageClick; } var attachments = item.Attachments; Logger.Verbose(SOURCE, string.Format( "found {0} attachments on {1}", attachments.Count, item.Subject)); //add handler for contextmenu.show event mnuAttach.Opening += MnuAttachOpening; _editable = ThisAddIn.IsEditable(item, _senderAddress, _serverName, _serverPort, storeAddress); //Ephemeral if (!string.IsNullOrEmpty(_duration) && _duration != "0" && !_editable) { Content = Resources.ephemeral_content; EnableEdits(false); return; } if (_editable) { _account = ThisAddIn.Accounts[_senderAddress]; openToolStripMenuItem.Text = Resources.open_for_editing; useDefaultApplicationToolStripMenuItem.Visible = true; browseForEditorToolStripMenuItem.Visible = true; } else { openToolStripMenuItem.Text = Resources.open_menu_option; useDefaultApplicationToolStripMenuItem.Visible = false; browseForEditorToolStripMenuItem.Visible = false; } if (Pointers == null) { Logger.Warning(SOURCE, string.Format( "failed to return pointers for {0}", item.Subject)); EnableEdits(false); return; } if (_account == null) { Logger.Warning(SOURCE, string.Format( "unable to locate account to retrieve content for {0}", item.Subject)); return; } _configuration = _account.Configurations.Values. First(config => config.Server == _serverName); if (_configuration == null) { Logger.Warning(SOURCE, string.Format( "unable to locate valid configuration to retrieve content for {0}", item.Subject)); return; } Logger.Verbose(SOURCE, string.Format( "found {0} pointers for {1}", Pointers.Length, item.Subject)); //body string content; string error; ContentHandler.FetchContent(_account.SMTPAddress, _configuration, _senderAddress, Pointers[0], ServerName, ServerPort, !string.IsNullOrEmpty(EncryptKey2), out content, out error); if (string.IsNullOrEmpty(content)) { ThisAddIn.IsCurrentItemHasContent = false; Logger.Warning(SOURCE, string.Format( "FetchContent request for {0} from {1} returned {2}", Pointers[0], _senderAddress, error)); ShowContent("<body/>"); MessageBox.Show(this, string.Format( Resources.error_fetching_content, Environment.NewLine, error), Resources.product_name, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } //self-destruct //if (!string.IsNullOrEmpty(_duration)) //{ // Logger.Info(SOURCE, string.Format("found Duration: {0} on {1}", // _duration, item.Subject)); //always instantiate timer //_selfDestructTimer = new Timer(SelfDestructTimerTick, // "content", // Convert.ToInt32(_duration)*1000, // Timeout.Infinite); //if (!string.IsNullOrEmpty(content) && _senderAddress != _account.SMTPAddress) //{ // //post REMOVE RECIPIENT // ContentHandler.RemoveRecipient(_account.SMTPAddress, _configuration, // _senderAddress, Pointers[0], ServerName, ServerPort, out error); //} //} var embeddedFileNames = new List<string>(); if (!string.IsNullOrEmpty(content)) { ThisAddIn.IsCurrentItemHasContent = true; if (string.IsNullOrEmpty(EncryptKey + EncryptKey2)) { Logger.Verbose(SOURCE, string.Format( "content length: {0}", content.Length)); } else { Logger.Verbose(SOURCE, string.Format( "raw content length: {0}", content.Length)); //decrypt content - use EncryptKey2 if we have it if (!string.IsNullOrEmpty(EncryptKey2)) { //content is still base64 - decode first byte[] encrypted = Convert.FromBase64String(content); //if user-agent field have value then decrypt with CBC mode or decrypt with ECB mode (earlier solution) if (!string.IsNullOrEmpty(UserAgent)) { content = Encoding.UTF8.GetString( AES_JS.DecryptCBC(encrypted, EncryptKey2)); } else { content = Encoding.UTF8.GetString( AES_JS.Decrypt(encrypted, EncryptKey2)); } } else { content = Cryptography.DecryptAES(content, EncryptKey); } } strHtmlEditorBaseUrl = Path.Combine( Path.GetTempPath(), "ChiaraMail", _recordKey); winFormHtmlEditor1.BaseUrl = Path.Combine( Path.GetTempPath(), "ChiaraMail", _recordKey); var imageMap = Utils.MapAttachments(Pointers, attachments); //fix any paths to embedded images var imageLinks = Utils.GetImageFileLinks(content, "src"); if (imageLinks.Count > 0) { content = Utils.FetchEmbeddedFileImages(content, imageLinks, imageMap, strHtmlEditorBaseUrl, _account, _configuration, _senderAddress, ServerName, ServerPort, EncryptKey2, UserAgent, ref embeddedFileNames); } else { //if sent with 'include content' we need to handle regular cid: links content = Utils.LoadEmbeddedImageAttachments(item, content); } } if (attachments.Count.Equals(0)) { tableLayoutAttach.Visible = false; } else { //create or get a temp folder to store the attachments Utils.CreateTempFolder(_recordKey); //int index = 0; int upperWidth = 0; int upperHeight = 0; panelAttach.AutoScroll = false; Utils.LoadAttachments(item, Pointers, strHtmlEditorBaseUrl, _account, _senderAddress, ServerName, ServerPort, EncryptKey, EncryptKey2, embeddedFileNames, ref _attachList, ref _embedded, ref panelAttach, ref upperWidth, ref upperHeight); if (ThisAddIn.NoPreviewer) { btnMessage.Caption = "Attachments:"; btnMessage.HideImage(ThisAddIn.AppVersion < 14 ? Color.CornflowerBlue : Color.DarkGray); panelVertLine.Visible = false; previewToolStripMenuItem.Visible = false; mnuSep1.Visible = false; } tableLayoutAttach.Visible = (panelAttach.Controls.Count > 1); panelVertLine.Height = btnMessage.Height; //adjust all AttachPanel buttons to same (upper) Width for (var i = 1; i < panelAttach.Controls.Count; i++) { var btn = (AttachPanel)panelAttach.Controls[i]; //hook up the event handlers btn.PanelClick += SelectAttachment; btn.PanelDblClick += AttachmentDoubleClick; if (!btn.Pointer.StartsWith("embedded")) { btn.ContextMenuStrip = mnuAttach; } if (btn.Width >= upperWidth) continue; btn.AutoSize = false; btn.Width = upperWidth; btn.Height = upperHeight; } //handle emnbedded video var videoLinks = Utils.GetVideoLinks(content); if (videoLinks.Count > 0) { content = Utils.LoadEmbeddedVideos(content, videoLinks, _attachList, strHtmlEditorBaseUrl, _account, _configuration, _senderAddress, ServerName, ServerPort, _encryptKey2, _userAgent); } } if(!string.IsNullOrEmpty(content)) Content = content; EnableEdits(_editable); } catch (Exception ex) { Logger.Error(SOURCE, ex.ToString()); } finally { Cursor = Cursors.Default; } }
private void BtnOKClick(object sender, EventArgs e) { //must have Description if (string.IsNullOrEmpty(txtDescription.Text)) { MessageBox.Show(Resources.prompt_config_missing_name, Resources.product_name, MessageBoxButtons.OK, MessageBoxIcon.Warning); DialogResult = DialogResult.None; return; } //must have server if (string.IsNullOrEmpty(txtServer.Text)) { MessageBox.Show(Resources.prompt_config_missing_server, Resources.product_name, MessageBoxButtons.OK, MessageBoxIcon.Warning); DialogResult = DialogResult.None; return; } //check for invalid server var regex = new Regex(@"\A(https?://)?[\w-]+(\.[\w-]+)+\Z", RegexOptions.IgnoreCase); if (!regex.IsMatch(txtServer.Text)) { MessageBox.Show(string.Format( Resources.prompt_config_invalid_server_name, txtServer.Text), Resources.product_name, MessageBoxButtons.OK, MessageBoxIcon.Warning); DialogResult = DialogResult.None; return; } //disallow dupes for description if (Key != 0) { var host = txtServer.Text + Convert.ToString(udPort.Value).ToLower(); foreach (var config in Account.Configurations.Values) { if (config.Key == Key) continue; if (config.Description.Equals(txtDescription.Text, StringComparison.CurrentCultureIgnoreCase)) { MessageBox.Show(string.Format( Resources.prompt_config_duplicate_name, txtDescription.Text), Resources.product_name, MessageBoxButtons.OK, MessageBoxIcon.Warning); DialogResult = DialogResult.None; return; } //disallow dupes for server+port if (!host.Equals(config.Server + config.Port, StringComparison.CurrentCultureIgnoreCase)) continue; MessageBox.Show(string.Format( Resources.prompt_config_duplicate_host, txtServer.Text + ":" + udPort.Value, Environment.NewLine), Resources.product_name, MessageBoxButtons.OK, MessageBoxIcon.Warning); DialogResult = DialogResult.None; return; } } var configuration = new EcsConfiguration { Key = Key, Description = txtDescription.Text, Server = txtServer.Text, Port = Convert.ToInt32(udPort.Value).ToString("d"), Password = txtPassword.Text, DefaultOn = chkDefaultOn.Checked, Encrypt = chkEncrypt.Checked, NoPlaceholder = chkNoPlaceholder.Checked, AllowForwarding = chkAllowForwarding.Checked }; if (!Account.Configurations.ContainsKey(Key)) { Account.Configurations.Add(Key, configuration); } else { Account.Configurations[Key] = configuration; } DialogResult = DialogResult.OK; }
private void DeletePointers(List<string> pointers, Account account, EcsConfiguration configuration) { var source = CLASS_NAME + "DeletePointers"; Logger.Info(source, String.Format( "deleting {0} pointers after failed attachment upload", pointers.Count)); foreach (var pointer in pointers) { string error; ContentHandler.DeleteContent(account.SMTPAddress, configuration, pointer, out error, true); } }
private bool PostAttachments(Account account, EcsConfiguration configuration, string encryptKey, string recips, ref List<string> pointers) { var source = CLASS_NAME + "PostAttachments"; try { //get the bytes for the placeholder text var placeholder = Encoding.UTF8.GetBytes(Resources.placeholder_text); SafeMailItem safMail; try { safMail = RedemptionLoader.new_SafeMailItem(); } catch (Exception ex) { Logger.Error("", String.Format( "unable to work with attachments for {0}, failed to instantiate SafeMailItem: {1}", MailItem.Subject, ex.Message)); return false; } //need to save the item first before we can work with the SafeMailItem MailItem.Save(); safMail.Item = MailItem; var colAttach = safMail.Attachments; /* Outlook will move any embedded images to the head of the attachments table * if that's the case then we need to remove and re-add the other attachments * so that the pointer list will match the finished order */ var hidden = false; string contentId; var savedAttach = new Dictionary<int, byte[]>(); //do we have any embedded images? foreach (Redemption.Attachment rdoAttach in colAttach) { Utils.GetAttachProps(rdoAttach, out contentId, out hidden); if (hidden) break; } if (hidden) { //walk through in reverse order //delete and reattach each non-hidden attachment for (var i = colAttach.Count; i > 0; i--) { Redemption.Attachment rdoAttach = colAttach[i]; Utils.GetAttachProps(rdoAttach, out contentId, out hidden); if (hidden) continue; if (rdoAttach.Type.Equals(5)) //embedded { var msg = rdoAttach.EmbeddedMsg; rdoAttach.Delete(); colAttach.Add(msg, 5); } else { var path = Path.Combine(Path.GetTempPath(), "ChiaraMail", rdoAttach.FileName); var displayName = rdoAttach.DisplayName; if (File.Exists(path)) File.Delete(path); rdoAttach.SaveAsFile(path); rdoAttach.Delete(); rdoAttach = colAttach.Add(path, 1, Type.Missing, displayName); //get the bytes and drop those in the dictionary, linked to the current index savedAttach.Add(rdoAttach.Index, File.ReadAllBytes(path)); File.Delete(path); } } } //now loop through and collect the content (except for embedded messages) var attachList = new List<Attachment>(); bool showForm = false; foreach (Redemption.Attachment rdoAttach in colAttach) { var attach = new Attachment { Type = rdoAttach.Type }; switch (rdoAttach.Type) { case (int)OlAttachmentType.olEmbeddeditem: //is this an ECS attachment? var msg = rdoAttach.EmbeddedMsg; if (Utils.HasChiaraHeader(msg)) { ForwardEmbeddedECS(msg, recips, account); } //always add attachList.Add(attach); break; case (int)OlAttachmentType.olByReference: case (int)OlAttachmentType.olOLE: attachList.Add(attach); break; case (int)OlAttachmentType.olByValue: showForm = true; //we may have already gotten the bytes if (savedAttach.Count > 0 && savedAttach.ContainsKey(rdoAttach.Index)) { attach.Content = savedAttach[rdoAttach.Index]; } if (attach.Content == null || attach.Content.Length == 0) { //try just read the bytes from the binary property //this could fail if the attachment is too big try { attach.Content = rdoAttach.AsArray != null ? rdoAttach.AsArray as byte[] : null;//.Fields[ThisAddIn.PR_ATTACH_DATA_BIN]); } catch { attach.Content = null; } } if (attach.Content == null) { //save to disk then get the bytes var path = Path.Combine(Path.GetTempPath(), "ChiaraMail", rdoAttach.FileName); if (File.Exists(path)) File.Delete(path); rdoAttach.SaveAsFile(path); attach.Content = File.ReadAllBytes(path); File.Delete(path); } if (attach.Content != null) { attach.Index = rdoAttach.Index; attach.Name = rdoAttach.DisplayName; attachList.Add(attach); } else { Logger.Warning(source, "aborting: failed to retrieve content for " + rdoAttach.DisplayName); MessageBox.Show(String.Format( "Unable to retrieve original content from {0}", rdoAttach.DisplayName), Resources.product_name, MessageBoxButtons.OK, MessageBoxIcon.Error); return false; } break; } } if (!showForm) { pointers.AddRange(attachList.Select(attach => attach.Pointer)); return true; } //use the WaitForm to upload the attachments var win = new OutlookWin32Window(Inspector, Inspector.IsWordMail()); var form = new WaitForm { Attachments = attachList, Account = account, Configuration = configuration, Recips = recips, EncryptKey2 = encryptKey }; //use encryptKey2 for new post if (form.ShowDialog(win) == DialogResult.OK) { //post succeeded for all attachments //get the pointers pointers.AddRange(form.Attachments.Select(attach => attach.Pointer)); //don't replace attachment bytes if we are sending content if (NoPlaceholder) return true; //loop back through to replace the original content with the placeholder bytes foreach (Redemption.Attachment rdoAttach in colAttach) { if (rdoAttach.Type.Equals(1)) //OlAttachmentType.olByValue) { rdoAttach.Fields[ThisAddIn.PR_ATTACH_DATA_BIN] = placeholder; } } return true; } //get the pointer list anyway so we can delete the items that got posted pointers.AddRange(form.Attachments .TakeWhile(attach => !String.IsNullOrEmpty(attach.Pointer)) .Select(attach => attach.Pointer)); } catch (Exception ex) { Logger.Error(source, ex.ToString()); } return false; }
public void LoadMsg(MessageItem item, string parentKey, string index, string parentId, Account account, string parentSender) { string source = _className + "LoadMsg"; _parentKey = parentKey; Key = index; _sender = item.Sender.SMTPAddress; _account = account; if (ThisAddIn.AppVersion < 15) { msgHdr14.LoadMessage(item.Subject, item.SenderName, string.Format("{0} {1}", item.SentOn.ToString("ddd"), item.SentOn.ToString("g")), item.To, item.CC); } else { msgHdr15.LoadMessage(item.Subject, item.SenderName, string.Format("{0} {1}", item.SentOn.ToString("ddd"), item.SentOn.ToString("g")), item.To, item.CC, false); } wb1.DocumentText = item.HTMLBody; if (Utils.HasChiaraHeader(item)) { string pointers; Utils.GetChiaraHeaders(item, out pointers, out _serverName, out _serverPort, out _encryptKey2, out _userAgent); _configuration = _account.Configurations.Values.First(config => config.Server == _serverName); if (!string.IsNullOrEmpty(pointers)) { _pointers = pointers.Split(new[] {" "}, StringSplitOptions.None); string content; string error; ContentHandler.FetchContent(account.SMTPAddress, _configuration, _sender, _pointers[0], _serverName, _serverPort, false, out content, out error); if (string.IsNullOrEmpty(error) || error == "success") { if (!string.IsNullOrEmpty(_encryptKey2)) { byte[] encrypted = Convert.FromBase64String(content); content = Encoding.UTF8.GetString( AES_JS.Decrypt(encrypted, _encryptKey2)); } wb1.DocumentText = content; } else { Logger.Warning(source,string.Format( "failed to retrieve content for {0} using pointer {1}, supplying sender {2}: {3}", item.Subject, _pointers[0], _sender, error)); } } } _attachments = item.Attachments; if (_attachments.Count.Equals(0)) { lblAttach.Visible = false; panelAttach.Visible = false; } else { //load them into the panel LoadAttachments(); } }