Outlook 2010 C++ add-in - HTML email body retrieval - c++

I'm writing an outlook 2010 add-in, my add-in appends a footer to emails according to each email format (plain text/HTML), now I just discovered a problem that happens when I enable the outlook "Read emails as plain text" feature (http://support.microsoft.com/kb/831607), what happens is my add-in receives HTML emails as plain text, so it adds the footer and saves them accordingly which is a problem because after saving as plain text, the actual HTML version of the email is completely lost from outlook.
Here's my code (edited for brevity):
void CConnect::OnNewItem(IDispatch* item)
{
if (item)
{
CComQIPtr<Outlook::_MailItem> mailItem(item);
OlBodyFormat bodyFormat;
mailItem->get_BodyFormat(&bodyFormat);
CComBSTR body;
if(bodyFormat == olFormatPlain)
mailItem->get_Body(&body);
if(bodyFormat == olFormatHTML)
mailItem->get_HTMLBody(&body);
//append the footer here
if(bodyFormat == olFormatPlain)
mailItem->put_Body(newBody);
if(bodyFormat == olFormatHTML)
mailItem->put_HTMLBody(newBody);
mailItem->Save();
mailItem->Release();
}
}
I also tried getting the HTML body of emails no matter what their format actually is, but I get a generated HTML version of the plain text version, so all the original CSS and formatting is lost.
What I need is a way to be able to retrieve the original HTML version when the outlook "Read emails as plain text" feature is on, Any help would be greatly appreciated.

A bit dated, but just saw this now...
You are probably going to need to drop down to the MAPI level to do this properly. Something like:
LPMESSAGE MapiMessage;
HRESULT ResultCode = HrGetOneProp(MapiMessage, PR_HTML, &PropertiesValues);
LPSBinary HtmlBodyProperty = (LPSBinary)&PropertiesValues->Value.bin;
Integrating MAPI support into your addin is not as painful as some people might think. A good starting point is "Using MAPI to Create Outlook 2007 Items".

Related

Open a c++ application installed on computer with a custom url in browser [duplicate]

How do i set up a custom protocol handler in chrome? Something like:
myprotocol://testfile
I would need this to send a request to http://example.com?query=testfile, then send the httpresponse to my extension.
The following method registers an application to a URI Scheme. So, you can use mycustproto: in your HTML code to trigger a local application. It works on a Google Chrome Version 51.0.2704.79 m (64-bit).
I mainly used this method for printing document silently without the print dialog popping up. The result is pretty good and is a seamless solution to integrate the external application with the browser.
HTML code (simple):
Click Me
HTML code (alternative):
<input id="DealerName" />
<button id="PrintBtn"></button>
$('#PrintBtn').on('click', function(event){
event.preventDefault();
window.location.href = 'mycustproto:dealer ' + $('#DealerName').val();
});
URI Scheme will look like this:
You can create the URI Scheme manually in registry, or run the "mycustproto.reg" file (see below).
HKEY_CURRENT_USER\Software\Classes
mycustproto
(Default) = "URL:MyCustProto Protocol"
URL Protocol = ""
DefaultIcon
(Default) = "myprogram.exe,1"
shell
open
command
(Default) = "C:\Program Files\MyProgram\myprogram.exe" "%1"
mycustproto.reg example:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Classes\mycustproto]
"URL Protocol"="\"\""
#="\"URL:MyCustProto Protocol\""
[HKEY_CURRENT_USER\Software\Classes\mycustproto\DefaultIcon]
#="\"mycustproto.exe,1\""
[HKEY_CURRENT_USER\Software\Classes\mycustproto\shell]
[HKEY_CURRENT_USER\Software\Classes\mycustproto\shell\open]
[HKEY_CURRENT_USER\Software\Classes\mycustproto\shell\open\command]
#="\"C:\\Program Files\\MyProgram\\myprogram.exe\" \"%1\""
C# console application - myprogram.exe:
using System;
using System.Collections.Generic;
using System.Text;
namespace myprogram
{
class Program
{
static string ProcessInput(string s)
{
// TODO Verify and validate the input
// string as appropriate for your application.
return s;
}
static void Main(string[] args)
{
Console.WriteLine("Raw command-line: \n\t" + Environment.CommandLine);
Console.WriteLine("\n\nArguments:\n");
foreach (string s in args)
{
Console.WriteLine("\t" + ProcessInput(s));
}
Console.WriteLine("\nPress any key to continue...");
Console.ReadKey();
}
}
}
Try to run the program first to make sure the program has been placed in the correct path:
cmd> "C:\Program Files\MyProgram\myprogram.exe" "mycustproto:Hello World"
Click the link on your HTML page:
You will see a warning window popup for the first time.
To reset the external protocol handler setting in Chrome:
If you have ever accepted the custom protocol in Chrome and would like to reset the setting, do this (currently, there is no UI in Chrome to change the setting):
Edit "Local State" this file under this path:
C:\Users\Username\AppData\Local\Google\Chrome\User Data\
or Simply go to:
%USERPROFILE%\AppData\Local\Google\Chrome\User Data\
Then, search for this string: protocol_handler
You will see the custom protocol from there.
Note: Please close your Google Chrome before editing the file. Otherwise, the change you have made will be overwritten by Chrome.
Reference:
https://msdn.microsoft.com/en-us/library/aa767914(v=vs.85).aspx
Chrome 13 now supports the navigator.registerProtocolHandler API. For example,
navigator.registerProtocolHandler(
'web+custom', 'http://example.com/rph?q=%s', 'My App');
Note that your protocol name has to start with web+, with a few exceptions for common ones (like mailto, etc). For more details, see: http://updates.html5rocks.com/2011/06/Registering-a-custom-protocol-handler
This question is old now, but there's been a recent update to Chrome (at least where packaged apps are concerned)...
http://developer.chrome.com/apps/manifest/url_handlers
and
https://github.com/GoogleChrome/chrome-extensions-samples/blob/e716678b67fd30a5876a552b9665e9f847d6d84b/apps/samples/url-handler/README.md
It allows you to register a handler for a URL (as long as you own it). Sadly no myprotocol:// but at least you can do http://myprotocol.mysite.com and can create a webpage there that points people to the app in the app store.
This is how I did it. Your app would need to install a few reg keys on installation, then in any browser you can just link to foo:\anythingHere.txt and it will open your app and pass it that value.
This is not my code, just something I found on the web when searching the same question. Just change all "foo" in the text below to the protocol name you want and change the path to your exe as well.
(put this in to a text file as save as foo.reg on your desktop, then double click it to install the keys)
-----Below this line goes into the .reg file (NOT including this line)------
REGEDIT4
[HKEY_CLASSES_ROOT\foo]
#="URL:foo Protocol"
"URL Protocol"=""
[HKEY_CLASSES_ROOT\foo\shell]
[HKEY_CLASSES_ROOT\foo\shell\open]
[HKEY_CLASSES_ROOT\foo\shell\open\command]
#="\"C:\\Program Files (x86)\\Notepad++\\notepad++.exe\" \"%1\""
Not sure whether this is the right place for my answer, but as I found very few helpful threads and this was one of them, I am posting my solution here.
Problem: I wanted Linux Mint 19.2 Cinnamon to open Evolution when clicking on mailto links in Chromium. Gmail was registered as default handler in chrome://settings/handlers and I could not choose any other handler.
Solution:
Use the xdg-settings in the console
xdg-settings set default-url-scheme-handler mailto org.gnome.Evolution.desktop
Solution was found here https://alt.os.linux.ubuntu.narkive.com/U3Gy7inF/kubuntu-mailto-links-in-chrome-doesn-t-open-evolution and adapted for my case.
I've found the solution by Jun Hsieh and MuffinMan generally works when it comes to clicking links on pages in Chrome or pasting into the URL bar, but it doesn't seem to work in a specific case of passing the string on the command line.
For example, both of the following commands open a blank Chrome window which then does nothing.
"c:\Program Files (x86)\Google\Chrome\Application\chrome.exe" "foo://C:/test.txt"
"c:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --new-window "foo://C:/test.txt"
For comparison, feeding Chrome an http or https URL with either of these commands causes the web page to be opened.
This became apparent because one of our customers reported that clicking links for our product from a PDF being displayed within Adobe Reader fails to invoke our product when Chrome is the default browser. (It works fine with MSIE and Firefox as default, but not when either Chrome or Edge are default.)
I'm guessing that instead of just telling Windows to invoke the URL and letting Windows figure things out, the Adobe product is finding the default browser, which is Chrome in this case, and then passing the URL on the command line.
I'd be interested if anyone knows of Chrome security or other settings which might be relevant here so that Chrome will fully handle a protocol handler, even if it's provided via the command line. I've been looking but so far haven't found anything.
I've been testing this against Chrome 88.0.4324.182.
open
C:\Users\<Username>\AppData\Local\Google\Chrome\User Data\Default
open Preferences then search for excluded_schemes you will find it in 'protocol_handler' delete this excluded scheme(s) to reset chrome to open url with default application

Does the WinRT Clipboard History only support text and images?

I'm making a C++/WinRT app that makes use of the Window 10 clipboard history but for some reason I can only grab text and images.
My code is a bit like this..
auto history = Clipboard::GetHistoryItemsAsync().get();
if (history.Status() == ClipboardHistoryItemsResultStatus::Success)
{
auto historyItemList = history.Items();
for (const auto& historyitem : historyItemList)
{
//Works
if (dataPackageView.Contains(StandardDataFormats::Text()))
{
GetClipboardText(dataPackageView);
}
//Works
if (dataPackageView.Contains(StandardDataFormats::Bitmap()))
{
GetClipboardBitmap(dataPackageView);
}
//Never triggers
if (dataPackageView.Contains(StandardDataFormats::Html()))
{
GetClipboardHtml(dataPackageView);
}
//Never triggers
if (dataPackageView.Contains(StandardDataFormats::StorageItems()))
{
GetClipboardStorageItems(dataPackageView);
}
//etc..
I'd like to make use of the other dataformats. EG: For StorageItems, I expected if I CTRL+C an item from my desktop or if I did it programatically with
dataPackage.SetStorageItems({ std::begin(files), std::end(files) });
Clipboard::SetContent(dataPackage);
Clipboard::Flush();
The file would end up in the clipboard history so I could handle it like in the first snippet. But the file never appears in the returned Clipboard::GetHistoryItemsAsync() container. I also don't see the file in the history viewer when you press WIN+V.
So what i'm wondering is;
Does Windows 10 clipboard history "only" support raw text and images? can I not make use of the other dataformat properties? (ApplicationLink, Html, Rtf, StorageItems, Uri, Weblink etc)
I gave StorageItems as an example but it happens with the other formats too. If I CTRL+C a weblink (such as: https://stackoverflow.com/questions/ask), I expected dataPackageView.Contains(StandardDataFormats::WebLink()) Or Uri to trigger, but they don't
If the clipboard history can support other formats, how do you actually make use of it?
Edit:
Forgot to add. When I programatically added a StorageItem to the clipboard I also tried it like below. Don't know why I can't add strings to an IVector though.
auto options = ClipboardContentOptions();
auto formats = Windows::Foundation::Collections::IVector<hstring>();
formats.Append(StandardDataFormats::StorageItems()); //Unhandled Exception
options.IsAllowedInHistory(true);
options.IsRoamable(true);
options.HistoryFormats() = formats;
options.RoamingFormats() = formats;
Clipboard::Clipboard::Clear();
Clipboard::SetContentWithOptions(dataPackage, options);
Clipboard::Flush();
As observed by my esteemed colleague Faywang - MSFT, Windows' clipboard history and cloud clipboard sync implementation supports only a limited number of clipboard data formats, and this is reflected in the UWP clipboard API in the Windows Runtime Clipboard class.
It's important to remember that what clipboard formats are supported by clipboard history is not a contractual detail of clipboard history. Please keep that in mind when you read on:
As of Windows 10 version 2004 (aka build 19041, aka the May 2020 update), clipboard history supports the following clipboard data formats defined in StandardDataFormats:
Plain text: StandardDataFormats.Text
Various URI formats: StandardDataFormats.Uri, StandardDataFormats.WebLink, and StandardDataFormats.ApplicationLink
HTML clipboard format: StandardDataFormats.Html
Bitmaps: StandardDataFormats.Bitmap
For compatibility with classic desktop applications (aka Win32 apps), clipboard history also supports a few clipboard data formats whose names are not in StandardDataFormats, but which the Windows system can auto-convert to and from the above-listed StandardDataFormats formats when needed by an app trying to paste a particular format.
Clipboard history does not support any other custom or well-known clipboard formats.
Given the above, the only thing I see in your results that seems strange is that your app doesn't appear to be reading the HTML format. I don't know of any Windows implementation bugs in this area - try fiddling with your code some more, and if you still have trouble, I'd definitely encourage you to post another question, or contact us at Microsoft another way (such as through Feedback Hub).
I do observe that the Microsoft documentation around clipboard history from a conceptual technical perspective is lacking. I'd also encourage you to file GitHub issues against the places where clipboard history is documented, such as https://github.com/MicrosoftDocs/winrt-api - we're always listening.

Need to check the inbox of an Exchange server with C++

I'm working on a project in C++ to backup and restore email on Microsoft Exchange servers, I'm trying to write automated tests for the restore function. Right now I can create test users, databases, and mailboxes, and can send email between users through the Exchange Admin Powershell. However, Exchange doesn't have commandlets to view or delete emails (as far as I can tell). Is there a way to do that with straight Exchange commandlets?
I haven't found a way, so instead I'm looking for an IMAP API that I can add to the project to enable viewing and deleting emails. Free would be ideal, but it can't be licensed with GPL. Is there an IMAP API for C++ that doesn't have GPL? Is there an avenue to programatically view and delete emails I haven't tried yet?
EDIT: Honestly I'm not too fussy on how it gets done, I just need a way to do it. I'm open to any suggestions.
https://technet.microsoft.com/en-us/library/ff459253(v=exchg.150).aspx
Search-Mailbox can delete messages.
https://technet.microsoft.com/en-us/library/ee633455(v=exchg.150).aspx
ExportMailboxRequest and ImportMailboxRequest cmdlets do most of the heavy lifting for importing and exporting data.
Why do you need to read messages? Powershell can also do client side scripts using Outlook library commands.
EDIT
Put a 'magic phrase' in your test email. "MagicRainbowUnicorn".
Delete a message
Search-Mailbox -Identity "TestMailbox" -SearchQuery 'MagicRainbowUnicorn' -DeleteContent
Test for message
Switch ((Search-Mailbox -Identity "TestMailbox" -SearchQuery 'MagicRainbowUnicorn').count) {
0 { "No Results Found" }
1 { "One Result" }
default { "More than One, or some other strange Result" }
}
I don't have enough reputation to comment, but could you consume Exchange Web Services with C++? I wrote a few programs in C# that use EWS for monitoring mailboxes. I had the ability to view and delete messages from Exchange.
[Edit]
This is a sample of what I used in C#, if you decide to use that instead of C++. Or maybe it'll help steer you in a good direction.
using Microsoft.Exchange.WebServices.Data;
ExchangeService svc = new ExchangeService(ExchangeVersion.Exchange2010);
svc.Credentials = new WebCredentials("user#domain.com", "password");
svc.AutodiscoverUrl("user#domain.com");
// loop through messages in Inbox
foreach (EmailMessage msg in svc.FindItems(WellKnownFolderName.Inbox, new ItemView(int.MaxValue)))
{
// do stuff with message
}

Sitecore file download works only for admin user

I have a Sitecore 6.4 setup where an editor can click a button to generate a Word doc. I was adding the file to the media library and that was working fine. The editor would click the button in the content editor, the file was generated, media item was generated, then the content editor would show the new item in the media library and the editor could click the "download" button on the ribbon or the item to download it. However, my media library was getting unnecessarily filled up so I am trying to bypass the media library.
Instead of making the file in an arbitrary location as before, I am putting it in the temp directory like this:
wordOutputPath = Sitecore.IO.FileUtil.GetWorkFilename(Sitecore.Configuration.Settings.TempFolderPath, printItem.Name, ".docx");
File.Copy(wordTemplatePath, wordOutputPath);
WordprocessingDocument doc = WordprocessingDocument.Open(wordOutputPath, true);
After I "fill in" the file with content, I do this:
Sitecore.Context.ClientPage.ClientResponse.Download(wordFilePath);
Now if I am logged in as a Sitecore admin I get the browser's download dialog and can download the file. However, if I am logged in as a non-admin user, I get a little clicking and whirring, so to speak, and the file is generated, but the save file dialog never comes up in the browser. I can go in through the file system and see & open the Word doc, and it looks fine.
I found something in the Sitecore release notes for 6.6:
Released Sitecore CMS and DMS 6.6.0 rev. 130111 (6.6.0 Update-3)
[...]
Miscellaneous
Only Administrators were allowed to download files. (316774, 348557)
This was a problem in several areas of the system, for example the Package Generator and the Export Language Wizard in the CMS. It also affected the Export Users Wizard in the ECM module.
So I tried using SecurityDisabler (no longer have the code handy) and UserSwitcher like this:
using (new Sitecore.Security.Accounts.UserSwitcher(Sitecore.Security.Accounts.User.FromName("sitecore\admin", false)))
{
Sitecore.Context.ClientPage.ClientResponse.Download(wordFilePath);
}
The IUSR and IIS_IUSRS accounts both have read, list & read/execute permissions on the temp folder and the individual files show read & read/execute permissions for those two accounts as well.
What can I do to allow non-admin users to download these files? Does the bug that was fixed in 6.6 have anything to do with it?
Thank you.
Calling Sitecore.Context.ClientPage.ClientResponse.Download() method will result in a Download command sent back to the Sitecore Client in the Web browser, which in turn will make a call back to the server to execute it. This will be why using a security switcher (or a security disabler for that matter) before calling the Sitecore.Context.ClientPage.ClientResponse.Download() method has no effect, as the admin only restriction is being applied to the download else where.
If you search through sitecore\shell directory in your webroot, you will find that there is a file called download.aspx. This should be the actual page the download request is sent to. This web form page inherits the Sitecore.Shell.DownloadPage class, so if you take a look at that class's implementation, you will find that the OnLoad method is implemented as such:
protected override void OnLoad(EventArgs e)
{
Assert.ArgumentNotNull(e, "e");
base.OnLoad(e);
string fileHandle = StringUtil.GetString(new string[] { base.Request.QueryString["file"] });
bool flag = false;
string filename = FileHandle.GetFilename(fileHandle);
if (!string.IsNullOrEmpty(filename))
{
fileHandle = filename;
flag = true;
}
if (!string.IsNullOrEmpty(fileHandle))
{
if (MediaManager.IsMediaUrl(fileHandle))
{
this.DownloadMediaFile(fileHandle);
}
else if (fileHandle.IndexOf("id=", StringComparison.OrdinalIgnoreCase) >= 0)
{
this.DownloadMediaById(fileHandle);
}
else if (flag || Context.IsAdministrator)
{
this.DownloadFile(fileHandle);
}
}
}
As you can see, the last if block has an IsAdministrator check which would be where your non-admin users are getting blocked.
Although I haven't 100% verified that this is where the problem lies, the download restriction issue can be resolved, by creating a new class that inherits Sitecore.Shell.DownloadPage and overrides the OnLoad implementation with some extra security checks as required. I recommend that you don't remove the IsAdministrator check altogether as it is there for a good reason. Update the Inherits attribute in the download.aspx to make it use it instead.

Playing a mp3 file using an Embed tag where the source is ashx file and byte array from database

I currently store text to speech mp3 files as varbinary(max) in the database. what I want to do is play those audio files using the embed tag where the source is ashx file that will recieve the id of the database record and write the byte array.
My ashx file has the following code
byte[] byteArray = ttsMessage.MessageContents;
context.Response.Buffer = true;
context.Response.Clear();
context.Response.ClearContent();
context.Response.ClearHeaders();
context.Response.ContentType = "audio/mpeg";
context.Response.OutputStream.Write(byteArray, 0, byteArray.Length);
context.Response.End();
The call from the aspx page is as follows
Panel5.Controls.Add(new LiteralControl(String.Format("<embed src='/TestArea/PreviewWav.ashx?source={0}' type='audio/mpeg' height='60px' width='144px'/>", ttsMessage.Id.ToString())));
I have gotten this to work with the following
Panel5.Controls.Add(new LiteralControl(String.Format("<audio controls='controls' autoplay='autoplay'><source src='/TestArea/PreviewWav.ashx?source={0}' type='audio/x-wav' /></audio>", ttsMessage.Id.ToString())));
Using the audio tag but cannot seem to get it to work with the embed tag.
I am using IE9/VS2010
Any ideas?
I think the wrong thing with embed tag is that...
Embed tag call a plugin like winmediaplayer ocx than handler firstly called from web page than media plugin get the ashx url than it started to call handler.
But web page's request and mediaplayer plugin's requests are diffent so if you check users Authentication or some other header information it fails.
You can easily see that on fiddler utility. On fiddler top-right side shows the request info. there is a user-agent part. Look it carefully.
How many requests happen from your handler,notice them. What are the differs. for each reqs.
If you have this issue,
You may use a ticket system or redirect a safety area for download without header or other request checks. Sadly web page requst cannot complately transfer media player and others.
hope helps