Tuesday, January 04, 2005

Copying email from the Inbox to eml files on the file system

Somebody asked how to copy email from a mailbox or public folder to the file system using WebDAV. One way of doing this is to grab the email as stream using a WebDAV get as illustrated in the Exchange SDK here
Once you have the email as a stream you can then use an ADO stream object's savetofile method to save it as an eml file. If you combine that with a webdav search you can then select all the emails in a certain folder and export this to a folder on the file system. The following example exports all the mail in a inbox to a folder on the c: drive called exp. I've posted a downloadable copy of this code here.

server = "servername"
mailbox = "mailbox"
set fso = createobject("Scripting.FileSystemObject")
strURL = "http://" & server & "/exchange/" & mailbox & "/inbox/"
strURL1 = "http://" & server & "/exchange/" & mailbox & "/sent items/"
strQuery = ""
strQuery = strQuery & "SELECT ""DAV:displayname"", ""urn:schemas:httpmail:subject"""
strQuery = strQuery & " FROM scope('shallow traversal of """
strQuery = strQuery & strURL & """') Where ""DAV:ishidden"" = False AND ""DAV:isfolder"" = False AND "
strQuery = strQuery & """DAV:contentclass"" = 'urn:content-classes:message'
set req = createobject("microsoft.xmlhttp")
req.open "SEARCH", strURL, false
req.setrequestheader "Content-Type", "text/xml"
rem req.setrequestheader "Range", "rows=0-100"
req.setRequestHeader "Translate","f"
req.send strQuery
If req.status >= 500 Then
ElseIf req.status = 207 Then
set oResponseDoc = req.responseXML
set oNodeList = oResponseDoc.getElementsByTagName("a:displayname")
set oNodeList1 = oResponseDoc.getElementsByTagName("a:href")
For i = 0 To (oNodeList.length -1)
set oNode = oNodeList.nextNode
set oNode1 = oNodeList1.nextNode
df = expmail(oNode.Text,oNode1.text)
End If

function expmail(displayname,href)
req.open "GET", href, false
req.setRequestHeader "Translate","f"
fname = replace(replace(replace(replace(replace(displayname,":","-"),"\",""),"/",""),"?",""),chr(34),"")
fname = replace(replace(replace(replace(replace(replace(fname,"<",""),">",""),chr(11),""),"*",""),"|",""),"(","")
fname = replace(replace(replace(fname,")",""),chr(12),""),chr(15),"")
fname = "c:\exp\" & fname
wscript.echo fname
set stm = createobject("ADODB.Stream")
msgstring = req.responsetext
stm.type = 2
stm.Charset = "x-ansi"
stm.writetext msgstring,0
stm.Position = 0
stm.type = 1
stm.savetofile fname
set stm = nothing

end function


Anonymous said...

This is excellent, thanks very much. I am curious if I can save HTML email easily as a .HTML file.. or if that is just a difference of extension and the .msg contents will be well-formed HTML. [?]

Glen said...

If you want just to access the Hmtl portion of an email then you would be better of using a propfind on the urn:schemas:httpmail:htmldescription property of a message. The method im using is to grab the RFC822 stream of the message which cotains a lot more the just the Htmlbody.

Maybe the best example i have is this http://gsexdev.blogspot.com/2005/07/shared-mailbox-database-public-folder.html. This is a event sink that write the Html body of a message out to a database.


RobAld said...

Clarify how to use this to export a public folder?

The public folder has a URL like this... http://bwmail.mydomain.net/public/archive%20mail/Doe,%20John%20E/inbox/

I'm trying to export the folder and all of its subfolders.

Glen said...

The script doesn't recurse it just grabs the steams of any items in the folder you configure it to run with. To make it recurse you would have to do something like is use in http://support.microsoft.com/default.aspx?scid=kb;en-us;320071

(this is a mailbox sample which you could convert to public folders)

I would be much more inclined to use Mapi to do this using something like RDO which has the option to export to EML file or a msg file which may be more important if you have rich data types you are trying to export. (eg with EML you will always loose custom mapi properties).


RobAld said...

Thanks for the suggestion; I would not want to lose the MAPI properties.

If you have any pointers to a script that can export a set of public folders to a PST file, that's what I'm after. Everywhere I look it seems the recommendation is to use Outlook. Each user has their own public-folder with old messages, so I don't want to have to run Outlook's Export feature for 1000+ times, one for each user.

Glen said...

What i would suggest is that you grab a copy of redemption http://www.dimastr.com/redemption/ and then you can use RDO. Then you can do something like

servername = "servername"
mailbox = "mailbox"
set RDOSession = CreateObject("Redemption.RDOSession")
RDOSession.LogonExchangeMailbox mailbox,servername
set PST = RDOSession.Stores.AddPSTStore("c:\temp\exppubmove.pst", 1,
"Exported Public Folders " & now())
set pubfolder = RDOSession.GetFolderFromPath("\\Public Folders\All Public
set pstroot =
pubfolder.copyto pstroot

RobAld said...

Thanks... Here's what I came up with.

option explicit
dim strServername
dim strMailbox
dim pstUnicodeFormat
dim strTitle
dim strPSTname
dim strFoldername
dim pubfolder
dim RDOSession
dim PST
dim pstroot

strTitle="pfexport - Export User Public Folder to PST file"

strServername = "mailserv1"
strMailbox = "adminuser"
pstUnicodeFormat = 2

if WScript.Arguments.Named.Exists("exportuser") then
strPSTname = WScript.Arguments.Named("exportuser")
strPSTname = InputBox("What is email alias for the PST export file? ",strTitle,"jadoe")
end if
strPSTname = "d:\pst-exports\pf-" & strPSTname & ".pst"

if WScript.Arguments.Named.Exists("foldername") then
strFoldername = WScript.Arguments.Named("foldername")
strFoldername = InputBox("What is folder name to export? ",strTitle,"Archive Mail\Doe, John A\")
end if
strFoldername = "\\Public Folders\All Public Folders\" & strFoldername

set RDOSession = CreateObject("Redemption.RDOSession")

RDOSession.LogonExchangeMailbox strMailbox,strServername

set PST = RDOSession.Stores.AddPSTStore(strPSTname, pstUnicodeFormat, "Exported Public Folders " & now())

set pubfolder = RDOSession.GetFolderFromPath(strFoldername)

set pstroot = RDOSession.GetFolderFromID(PST.IPMRootFolder.EntryID,PST.EntryID)

pubfolder.copyto pstroot

The icing on the cake would be to Rename the public folder (put an "X" in front of the name) --- and then change the public folder's permissions so the user cannot add more content.

Hannes said...

Thanks for the post about RDO - Works like a breeze