Saturday, April 19, 2008

Uploading an EML file to a Mailbox with Exchange Web Services

This is a handy little sample to have in your bag of tricks when you need to exchange messages between disparate messaging systems. This code sample is relatively straight forward in that it uses a file stream to read the byte stream of the EML file which is already in MIME format and then uses a createitem operation and posts the MIME content from the data stream to the Exchange Message Store. The only trick that comes in is when you create the Item in whatever folder you want you need to set the message flags property PR_Message_Flags so the message will appear as a normal Sent Mail in Outlook (otherwise it will just appear as a draft). Also you need to set the flag value so it will also appear as Read (unless you want to have it as unread.)

The sample itself uploads an eml file from the c: drive to a users sent items folder

I’ve put a download copy of the code here the code itself looks like

[warning this code sample may cause global warming if you don't configure your pc to shutdown when not in use thats what those power saving options are for in the control panel]

static void Main(string[] args)
{
String emFileName = @"c:\emchk1.eml";
FileStream fsFileStream = new FileStream(emFileName, FileMode.Open, FileAccess.Read);
byte[] bdBinaryData1 = ReadFully(fsFileStream,fsFileStream.Length);
ExchangeServiceBinding esb = new ExchangeServiceBinding();
esb.Credentials = new NetworkCredential("username", "password", "domain");
esb.Url = @"http://servername/EWS/exchange.asmx";
CreateItemType ciCreateItemRequest = new CreateItemType();
ciCreateItemRequest.MessageDisposition = MessageDispositionType.SaveOnly;
ciCreateItemRequest.MessageDispositionSpecified = true;
ciCreateItemRequest.SavedItemFolderId = new TargetFolderIdType();
DistinguishedFolderIdType sfSenditems = new DistinguishedFolderIdType();
sfSenditems.Id = DistinguishedFolderIdNameType.sentitems;
ciCreateItemRequest.SavedItemFolderId.Item = sfSenditems;
ciCreateItemRequest.Items = new NonEmptyArrayOfAllItemsType();
MessageType wsMessage = new MessageType();
MimeContentType mcMimeContnet = new MimeContentType();
mcMimeContnet.Value = Convert.ToBase64String(bdBinaryData1);
wsMessage.MimeContent = mcMimeContnet;
ExtendedPropertyType sfSentFlag = new ExtendedPropertyType();
PathToExtendedFieldType epExPath = new PathToExtendedFieldType();
epExPath.PropertyTag = "0x0E07";
epExPath.PropertyType = MapiPropertyTypeType.Integer;
sfSentFlag.ExtendedFieldURI = epExPath;
sfSentFlag.Item = "1";
wsMessage.IsRead = true;
wsMessage.ExtendedProperty = new ExtendedPropertyType[1];
wsMessage.ExtendedProperty[0] = sfSentFlag;
ciCreateItemRequest.Items.Items = new ItemType[1];
ciCreateItemRequest.Items.Items[0] = wsMessage;
CreateItemResponseType crCreateItemResponse = esb.CreateItem(ciCreateItemRequest);
if (crCreateItemResponse.ResponseMessages.Items[0].ResponseClass == ResponseClassType.Error)
{
throw new Exception(crCreateItemResponse.ResponseMessages.Items[0].MessageText);
}
else
{
}
}
public static byte[] ReadFully(Stream stream, long initialLength)
{
// ref Function from http://www.yoda.arachsys.com/csharp/readbinary.html
// If we've been passed an unhelpful initial length, just
// use 32K.
if (initialLength < initiallength =" 32768;" buffer =" new" read =" 0;" chunk =" stream.Read(buffer,"> 0)
{
read += chunk;

// If we've reached the end of our buffer, check to see if there's
// any more information
if (read == buffer.Length)
{
int nextByte = stream.ReadByte();

// End of stream? If so, we're done
if (nextByte == -1)
{
return buffer;
}

// Nope. Resize the buffer, put in the byte we've just
// read, and continue
byte[] newBuffer = new byte[buffer.Length * 2];
Array.Copy(buffer, newBuffer, buffer.Length);
newBuffer[read] = (byte)nextByte;
buffer = newBuffer;
read++;
}
}
// Buffer is now too big. Shrink it.
byte[] ret = new byte[read];
Array.Copy(buffer, ret, read);
return ret;
}
}
}

14 comments:

Anony\ said...

Glen ,

I am working on a similar problem where I want to read an eml and assign it to a MimeContentType . But , the code doesn't work if the message is a Tnef encoded one. I have tried decoding using the Base64Encoder available in E2k7 libraries , which has worked if the message has no attachment. But , it fails if the TNEF message has got attachments . Please provide your suggestions .

Anonymous said...

I am struggling with itemType of message. When I use MessageType, everything works fine, however with ItemType It looks this kind of command isn't allowed. The message is as follows - With this kinds of item, mime conversion isn't supported.

Please give me any idea about this.
- eunjung from Seoul, Korea

Glen said...

What type of item are you trying to updload why are you using Itemtype ??

Cheers
Glen

Ravi said...

Glen, is it possible to create TaskItem in this way with MimeContent(eml file for Task)? I need to create Task with Task eml file(tnef encoded)..please help me. thanks in advance,
Regards,
Ravi Nalla

Glen said...

No Task,calendar items are rich item types with many Mapi properties so just uploading the RFC message stream wont work.

cheers
Glen

Ravi said...

Hi Glen, is it possible to read body and attachments from TNEF stream(tnef encoded mime) by programatically using tnefreader?
Do you have any idea on this?

Glen said...

In a Transport Agent yes see http://blogs.msdn.com/mstehle/archive/2009/01/13/howto-sample-transport-agent-add-headers-categories-mapi-props-even-uses-a-fork.aspx .In a EWS application there's not much point because you can actualy just access them directly. If your trying to read MSG files it probably better to use a third party library like redemption to do this with mapi.

Cheers
Glen

Ravi said...

Hi Glen,
I got sample code to read body and attcahment from tnef stream at http://social.technet.microsoft.com/forums/en-US/exchangesvrdevelopment/thread/7015cfc6-9345-460d-a14b-83d42fc1e9e7/
But it is working only for normal attchments, if attachemnt is again message(emebedded message), then it is courrpting. Do you have any idea why this is happening....

Thanks in advance...
Regards,
Ravi Nalla

Glen said...

No but i've had problems with embeeded messages as well in router agents i never really solved the problem and i was very frustrated with the lack of documentation. Sorry i cant help but please post back if you ever find the solution

Cheers
Glen

Ravi said...

Hi Glen,

Do you have any idea how to convert storeid into hexadecimal format(in C#)? Actually Exchange Web services ConvertID() is giving Entryid is in hexadecimal format(IdFormatType.HexEntryId), but storeid is not.

Thanks in advance,
Regards,
Ravi Nalla

Glen said...

You wont be able to do that with convertID because it will just return the StoreID in the EWS format. You could be better using a Get-Folder or Get-Item and specify the extended property this will return the the ID in Hex format.

Cheers
Glen

Bhuban said...

Hello Glen,

I am trying to implement the same with EWS Managed API. Can you please shed some light on marking the EmailMessage Item as Sent with EWS API.

Regards,
Bhuban

Glen said...

Have a look at http://social.technet.microsoft.com/Forums/en/exchangesvrdevelopment/thread/8fce117a-c8b9-4b58-9ba7-a08401da907c you need to set the sentflag using the extended property.

Cheers
Glen

Anonymous said...

Thx Glen! Just had to do exactly the same task via the Java EWS API and your post was very helpful for that!