Using Redemption, How can I add a default Signature to an outgoing Email Message? - outlook-redemption

Here is the code that I am using. I have spent some time looking at the Redemption objects, but, nothing jumps out at me:
public static bool PopEmail(string domainUserName, string mSubject, string mBody, string mTo, string mCc = "", string mBcc = "", List<String> fileAttachments = null)
{
log.Info("Starting to Pop Outlook Email Message");
RDOSession oSession = new RDOSession();
try
{
oSession.LogonExchangeMailbox(domainUserName, string.Empty);
if (oSession.LoggedOn)
{
RDOMail oMail = oSession.GetDefaultFolder(rdoDefaultFolders.olFolderOutbox).Items.Add("IPM.Note");
oMail.Subject = mSubject;
oMail.Body = mBody;
oMail.To = mTo;
oMail.CC = mCc;
oMail.BCC = mBcc;
if (fileAttachments != null)
{
foreach (string file in fileAttachments)
{
object newFile = file;
oMail.Attachments.Add(newFile, Type.Missing, Type.Missing, Type.Missing);
newFile = null;
}
}
oMail.Display();
Marshal.FinalReleaseComObject(oMail);
oMail = null;
}
oSession.Logoff();
Marshal.FinalReleaseComObject(oSession);
oSession = null;
GC.Collect();
GC.WaitForPendingFinalizers();
log.Info("Outlook Email has been Popped.");
return true;
}
catch (Exception)
{
log.Error("Outlook Pop Email Failed.");
throw;
}
}
Thank you,

The signature is actually inserted by the Outlook inspector object on instantiation, so if your code is running inside an Outlook addin you could probably try saving the item and then reopening it from the OOM as a _MailItem via _Namespace.GetItemFromId and then calling its GetInspector method (you don't actually have to do anything with the returned inspector reference).
Note that I haven't tried this with an item initially created via RDO. I usually create the items in OOM and then create an RDO wrapper.
If your code is running outside of Outlook you'd have to use OLE to get a reference to its _Application object and then pull the _Namespace object from there. If you are using standalone MAPI without Outlook installed the signature functionality is completely unavailable.

I have added code to append to the oMail.HTMLBody which reads the signature from the C:\Users\UserName\AppData\Roaming\Microsoft\Signatures folder. This file is generated via a plug in written by one of our developers that reads information from Exchange to determine User Name, Title, Phone, Fax, etc.

Related

Deriving device connection string from the environment

IoT modules can be created from the environment using :
ModuleClient.CreateFromEnvironmentAsync(settings)
However, there does not seem to be an equivalent method for devices. For now, I am setting the device connection string in the program to test it out, but is there a better way to read teh connection string from iotedge/config.yaml for all the edge devices deployed out there?
Methods to do so for .NET and python would be appreciated.
You can use a yaml parse library to deserialize the document, such as YamlDotNet. In fact, you can refer to YamlDocument in iot edge. But in the class, it does not provide a method to get the key value. Please refer to following code.
public class YamlDocument
{
readonly Dictionary<object, object> root;
public YamlDocument(string input)
{
var reader = new StringReader(input);
var deserializer = new Deserializer();
this.root = (Dictionary<object, object>)deserializer.Deserialize(reader);
}
public object GetKeyValue(string key)
{
if(this.root.ContainsKey(key))
{
return this.root[key];
}
foreach(var item in this.root)
{
var subItem = item.Value as Dictionary<object, object>;
if(subItem != null && subItem.ContainsKey(key))
{
return subItem[key];
}
}
return null;
}
}
And then you can get the device connection string from the config.yaml. If you use python, you can import yaml library to analysis the file.
StreamReader sr = new StreamReader(#"C:\ProgramData\iotedge\config.yaml");
var yamlString = sr.ReadToEnd();
var yamlDoc = new YamlDocument(yamlString);
var connectionString = yamlDoc.GetKeyValue("device_connection_string");
Console.WriteLine("{0}", connectionString);
To get the config file from the host, add the following to the docker deployment file. Note that the source file is config1.yaml which is the same as config.yaml except that it has read permissions for everyone not just root.
"createOptions": "{\"HostConfig\":{\"Binds\":[\"/etc/iotedge/config1.yaml:/app/copiedConfig.yaml\"]}}"
With the above line in place, the copiedConfig.yaml file can be used in the container, along with #Michael Xu's parsing code to derive teh connection string.
Long term, one may want to use the device provisioning service anyway but hope this helps for folks using device conenction strings for whatever reason..

Outlook UI thread is blocked when convert msg to eml using Outlook redemption

Outlook UI thread is blocked when convert msg to eml using redemption
I'm developping an outlook addin to sync emails to server as eml. When the addin start, it will start a timer and check whether there's new emails and then upload them. For each email, I will convert them to eml using outlook Redemption. It works, but I found during the convertion, outlook UI would be blocked. I'm using following code to do the convertion. I also tried RDOSession.GetMessageFromID(mail.EntryID). It's the same. Anyone
public static string ToEmlFile(this Outlook.MailItem mail)
{
var msgFilename = Path.ChangeExtension(Path.GetTempFileName(), ".msg");
mail.SaveAs(msgFilename);
var item = RDOSessionMgr.GetInstance().GetMessageFromMsgFile(msgFilename);
var emlFilename = Path.ChangeExtension(Path.GetTempFileName(), ".eml");
item.SaveAs(emlFilename, Redemption.rdoSaveAsType.olRFC822);
return emlFilename;
}
public class RDOSessionMgr
{
private static Redemption.RDOSession _session;
static RDOSessionMgr()
{
_session = new Redemption.RDOSession();
_session.Logon(Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);
}
public static Redemption.RDOSession GetInstance()
{
return _session;
}
}
You should be able to run that code on a secondary thread - save the value of the Application.Session.MAPIOBJECT property in a global/class variable, then assign that value to the RDOSession.MAPIOBJECT property of the RDOSession object that you create on the secondary thread.
ON a related note, you can use that technique on the main thread as well - there is no reason to RDOSession.LOgon from inside an Outlook addin since you already have the MAPI session used by Outlook.

Subscribe to a users My Matters via SDK

Using Autonomy WorkSite 8.5 SP2 SDK, I am attempting to programmicaly add a shortcut to another users My Matters which I have been told can be done by first subscribing to the other users My Matters, add the shortcut then unsubscribe.
I am therefore attempting to subscribe to another users My Matters however I am having issues with how to subscribe, with the below code I am able to locate the user's My Matters:
Dim objSFSP As IManSubscriptionFolderSearchParameters = oDms.CreateSubscriptionFolderSearchParameters
objSFSP.Add( imFolderAttributeID.imFolderOwner, sShortcutUser )
Dim objFolders As IManFolders = oMatters.FindRootSubscriptionFoldersNotSubscribedTo(oDatabaseList, objSFSP)
And from reading the COM Reference guide I should be able to subscribe to a users My Matters with the following code:
Dim objWorkArea As IManWorkArea = oSess.WorkArea
Dim oFolderShortcuts As IManSubscriptionFolderShortcuts = objWorkArea.SubscriptionFolder.SubFolders
Dim oFolderShortcut As IManFolderShortcut = oFolderShortcuts.AddNewSubscriptionFolderShortcutInheriting(objFolders)
The problem I am encountering is AddNewSubscriptionFolderShortcutInheriting() expects an object of the type IManSubScriptionFolder where FindRootSubscriptionFoldersNotSubscribedTo() returns a IManFolders object.
Can anyone point me in the direction of what I need to do to get an instance of the users My Matters as a IManSubscriptionFolder object?
Probably my response will be a bit late for you, but I hope that it will help anybody else who will have the same issue.
Answering your question, in order to get an instance of other users My Matters as a IManSubscriptionFolder object you just need to loop through the collection of objFolders and cast each folder to the IManSubScriptionFolder type.
Please find below my working solution:
ManDMS dms = new ManDMS();
string serverName = "dms.server.com";
IManSession session = dms.Sessions.Add(serverName);
string userID = "user";
string password = "password";
session.Login(userID, password);
ManStrings dblist = new ManStrings();
dblist.Add("TargetWsDbName");
IManSubscriptionFolderSearchParameters searchParams = ndms.CreateSubscriptionFolderSearchParameters();
string folderOwner = "AnotherUser";
searchParams.Add(imFolderAttributeID.imFolderOwner, folderOwner);
IManFolders nonSubscribedRootSubscriptionFolders = session.WorkArea.SubscriptionFolder.FindRootSubscriptionFoldersNotSubscribedTo(dblist, searchParams);
foreach (var folder in nonSubscribedRootSubscriptionFolders)
{
//another user's subscription folder
var subscriptionFolder = folder as IManSubscriptionFolder;
if (subscriptionFolder != null)
{
//Current user's subscription folder shortcuts
var subscriptionFolderShortcuts = session.WorkArea.SubscriptionFolder.SubFolders as IManSubscriptionFolderShortcuts;
if (subscriptionFolderShortcuts != null)
{
subscriptionFolderShortcuts.AddNewSubscriptionFolderShortcutInheriting(subscriptionFolder);
}
}
Please note that code from above was included for reference purpose only and it is not a production code.

Handling concurrency exceptions with external API calls

I have the following POST edit action method, which mainly perform two Update actions:-
Edit the object on the external system suing API calls.
Edit the object on our system database.
[HttpPost]
public ActionResult Create(RackJoin rj, FormCollection formValues)
{string controllername = RouteData.Values["controller"].ToString();
if (ModelState.IsValid)
{ var message = "";
var status = "";
long assetid = new long();
XmlDocument doc = new XmlDocument();
using (var client = new WebClient())
{
var query = HttpUtility.ParseQueryString(string.Empty);
foreach (string key in formValues)
{
query[key] = this.Request.Form[key];
}
query["username"] = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiUserName"];
query["password"] = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiPassword"];
string apiurl = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiURL"];
var url = new UriBuilder(apiurl);
url.Query = query.ToString();
try
{
string xml = client.DownloadString(url.ToString());
doc.LoadXml(xml);
status = doc.SelectSingleNode("/operation/operationstatus").InnerText;
message = doc.SelectSingleNode("/operation/message").InnerText;
}
catch (WebException ex)
{
ModelState.AddModelError(string.Empty, "Error occurred:" + ex.InnerException);
}
}
if (status.ToUpper() == "SUCCESS")
{
repository.InsertOrUpdateRack(rj.Rack, User.Identity.Name, rj.Resource.RESOURCEID);
repository.Save();
return RedirectToAction("Index");
}
else
{
ModelState.AddModelError(string.Empty, message.ToString());
}
}
}
catch (DbUpdateConcurrencyException ex)
{
As shown in the above code I will not do a repository.save() to update the object on our system, unless the API return a “success”.
But currently I am facing the following problem:-
If the API return a “success” but a concurrency exception occurred, then the API will update the object on the external system, but the object will not be updated on our system?
So is there a way to handle this situation?
There's no easy way to solve this situation. One way to handle it would be to ask the designers of the external API expose a method allowing to commit the transaction done in a previous call. Basically your first call will make modifications to the external system but with some boolean flag indicating that those changes are still pending. Then you update your system and in case of success you would call the external API to flag the data from pending to valid.
If you have no control over the external API and it makes the changes to the data from the first call irreversible, then I am afraid that you do not have much choices left. You might remember the state of the object you are modifying on the external system before calling the API and in case of an exception on your system, revert back to the previous state by calling the API with the previous values.

Serving an iCalendar file in ASPNET MVC with authentication

I'm trying to serve an iCalendar file (.ics) in my MVC application.
So far it's working fine. I have an iPhone subscribing to the URL for the calendar but now I need to serve a personalised calendar to each user.
When subscribing to the calendar on the iPhone I can enter a username and password, but I don't know how to access these in my MVC app.
Where can I find details of how the authentication works, and how to implement it?
It turns out that Basic Authentication is what is required. I half had it working but my IIS configuration got in the way. So, simply returning a 401 response when there is no Authorization header causes the client (e.g. iPhone) to require a username/password to subscribe to the calendar.
On the authorization of the request where there is an Authorization request header, the basic authentication can be processed, retrieving the username and password from the base 64 encoded string.
Here's some useful code for MVC:
public class BasicAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
var auth = filterContext.HttpContext.Request.Headers["Authorization"];
if (!String.IsNullOrEmpty(auth))
{
var encodedDataAsBytes = Convert.FromBase64String(auth.Replace("Basic ", ""));
var value = Encoding.ASCII.GetString(encodedDataAsBytes);
var username = value.Substring(0, value.IndexOf(':'));
var password = value.Substring(value.IndexOf(':') + 1);
if (MembershipService.ValidateUser(username, password))
{
filterContext.HttpContext.User = new GenericPrincipal(new GenericIdentity(username), null);
}
else
{
filterContext.Result = new HttpStatusCodeResult(401);
}
}
else
{
if (AuthorizeCore(filterContext.HttpContext))
{
var cachePolicy = filterContext.HttpContext.Response.Cache;
cachePolicy.SetProxyMaxAge(new TimeSpan(0));
cachePolicy.AddValidationCallback(CacheValidateHandler, null);
}
else
{
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.StatusDescription = "Unauthorized";
filterContext.HttpContext.Response.AddHeader("WWW-Authenticate", "Basic realm=\"Secure Calendar\"");
filterContext.HttpContext.Response.Write("401, please authenticate");
filterContext.HttpContext.Response.StatusCode = 401;
filterContext.Result = new EmptyResult();
filterContext.HttpContext.Response.End();
}
}
}
private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
}
Then, my controller action looks like this:
[BasicAuthorize]
public ActionResult Calendar()
{
var userName = HttpContext.User.Identity.Name;
var appointments = GetAppointments(userName);
return new CalendarResult(appointments, "Appointments.ics");
}
I found this really helpful, but i hit a few problems during the development and i thought i would share some of them to help save other people some time.
I was looking to get data from my web application into the calendar for an android device and i was using discountasp as a hosting service.
The first problem i hit was that the validation did not work when uploaded to the server, stangely enough it was accepting my control panel login for discountasp but not my forms login.
The answer to this was to turn off Basic Authentication in IIS manager. This resolved the issue.
Secondly, the app i used to sync the calendar to the android device was called iCalSync2 - its a nice app and works well. But i found that it only worked properly when the file was delivered as a .ics (duh for some reason i put it as a .ical.. it must have been late) and i also had to choose the webcal option
Lastly i found i had to add webcal:// to the start of my url instead of http://
Also be careful as the code posted above ignores the roles input variable and always passes nothing so you might need to do some role based checks inside your calendar routine or modify the code above to process the roles variable.

Resources