Recently, I had someone ask whether it was possible to manually create a message in Exchange UM from a UCMA application. My first thought was to simply divert the call to an EUM mailbox, but this involved a tricky SIP transfer that required some diversion headers to be set in the INVITE. It’s probably not impossible to do (and I’m currently trying to figure it out just to prove it can be done), but it turns out that the simpler solution is just to create the message and send it to the user. After all, voicemails in EUM look a lot like emails with wma or mp3 (in 2010) attachments, right? As it turns out, it’s just a matter of setting the right headers in the message. Doing things this way also has the advantage of keeping control over the call during recording and after the message has been recorded, in case you want to do something else in your call flow.
Using the EWS Managed API, it’s relatively simple to save a message in a user’s mailbox like this:
ExchangeService service = new ExchangeService(ExchangeVersion.Exchange2007_SP1); EmailMessage message = new EmailMessage(service); Random r = new Random(); message.Subject = "Voice mail from " + r.Next(0000000, 9999999); message.From = new EmailAddress("ANI name", "9058825000"); message.ItemClass = "IPM.Note.Microsoft.Voicemail.UM.CA"; message.Sender = new EmailAddress(email@example.com); message.Body = new MessageBody(BodyType.HTML, "<HTML><body><h1>This is a voice mail.</h1></BODY></HTML>"); message.Attachments.AddFileAttachment(@"d:\TestVM.wma"); message.ToRecipients.Add(new EmailAddress("r2TestUser@rnddev.computer-talk.com")); message.Save(WellKnownFolderName.Inbox);
This message will at least get you something that has the voicemail icon, and shows the embedded player, but the player will not work. Also, while Outlook voice access will detect that there’s a message there, it won’t play the contents. Now, if you look at a voicemail that EUM has recorded you’ll see a bunch of extra headers in the message, such as:
Subject: Voice Mail from ComputerTalk (52 seconds)
X-AttachmentOrder: 9058825000 (52 seconds) Voice Mail.wma
Date: Mon, 28 Jun 2010 07:01:27 -0400
Inserting those headers in a message using the EWS managed API turns out to be a little tricky. The properties you need to set are all documented in MSDN and in the MS-OXPROPS spec. In the EWS managed API, you need to create an ExtendedPropertyDefinition for an extended property that you want to set, and unfortunately, there’s not an enum for these values. These were my first four attempts at creating the property, and while all of the properties appeared in the message, none of them showed up in Outlook:
ExtendedPropertyDefinition messageLength = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.UnifiedMessaging, "PidNameXVoiceMessageDuration", MapiPropertyType.Integer); ExtendedPropertyDefinition messageLength = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.UnifiedMessaging, 0x6801, MapiPropertyType.Integer); ExtendedPropertyDefinition messageLength = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, 0x6801, MapiPropertyType.Integer); ExtendedPropertyDefinition messageLength = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.InternetHeaders, "PidNameXVoiceMessageDuration", MapiPropertyType.Integer);
The properties are part of the unified messaging set, but when I tried creating them based on the name or ID with either the InternetHeaders of UnifiedMessaging sets, nothing got added. What did eventually work was creating the properties by ID only, and then setting them:
//PidTagVoiceMessageDuration message.SetExtendedProperty(new ExtendedPropertyDefinition(0x6801, MapiPropertyType.Integer), 3); //PidTagVoiceMessageSenderName message.SetExtendedProperty(new ExtendedPropertyDefinition(0x6803, MapiPropertyType.String), "Test Sender Name"); //PidTagSenderTelephoneNumber message.SetExtendedProperty(new ExtendedPropertyDefinition(0x6802, MapiPropertyType.String), "sip:firstname.lastname@example.org");\ //PidTagVoiceMessageAttachmentOrder message.SetExtendedProperty(new ExtendedPropertyDefinition(0x6805, MapiPropertyType.String), "TestVM.wma");
Now, when you send the message to the user, all the correct voicemail control s light up as expected. The embedded player works, play on phone works, and Outlook Voice Access can play the message back. My next thought was, if that worked, why not just use System.Net.Mail instead of EWS:
System.Net.Mail.MailMessage m = new System.Net.Mail.MailMessage("email@example.com", "firstname.lastname@example.org"); m.Sender = new System.Net.Mail.MailAddress("email@example.com"); m.Subject = "Voice Mail Test"; m.IsBodyHtml = true; m.Body = @"<HTML><body><h1>This is a voice mail.</h1></BODY></HTML>"; m.Headers.Add("Content-Type", @"application/ms-tnef; name=""winmail.dat"""); m.Headers.Add("Content-Transfer-Encoding", "binary"); m.Headers.Add("X-VoiceMessageDuration", "3"); m.Headers.Add("Thread-Topic", "Voice mail Test"); m.Headers.Add("Content-Class", "voice"); m.Headers.Add("X-CallingTelephoneNumber", "sip:firstname.lastname@example.org"); m.Headers.Add("X-VoiceMessageSenderName", "Test Sender Name"); m.Headers.Add("X-AttachmentOrder", "TestVM.wma"); m.Attachments.Add(new System.Net.Mail.Attachment(@"d:\TestVM.wma")); System.Net.Mail.SmtpClient c = new System.Net.Mail.SmtpClient("exchangeServerAddress"); c.Send(m);
This appears to work just as well, and as a bonus the code to set the custom headers is much simpler than it was with the exchange APIs.
Of course, as with any solution like this there’s a catch, and the biggest one so far seems to be that with Exchange 2010, there’s no way to get voicemail preview on these messages. This is understandable, since I believe this is part of the EUM inbound call flow (and not an offline process on all voicemails), but I’m working on clarifying this. Otherwise, this method appears to work, and requires no extra SIP peer configuration on the EUM server.