Become a Columnist Microsoft Exchange Site Microsoft Support SiteMSDN Exchange Site

   

Subscribe to OutlookExchange
Anderson Patricio
Ann Mc Donough
Bob Spurzem
Brian Veal
Catherine Creary
Cherry Beado
Colin Janssen
Collins Timothy Mutesaria
Drew Nicholson
Fred Volking
Glen Scales
Goran Husman
Guy Thomas
Henrik Walther
Jason Sherry
Jayme Bowers
John Young
Joyce Tang
Justin Braun
Konstantin Zheludev
Kristina Waters
Kuang Zhang
Mahmoud Magdy
Martin Tuip
Michael Dong
Michele Deo
Mitch Tulloch
Nicolas Blank
Pavel Nagaev
Ragnar Harper
Ricardo Silva
Richard Wakeman
Russ Iuliano
Santhosh Hanumanthappa
Steve Bryant
Steve Craig
Todd Walker
Tracey J. Rosenblath
 
 

Attachment Archiving to a File Server Script      Download Script

Introduction

The following script builds on the concepts I introduced in my other article but uses them in a slightly different way. This script loops though a folder (or multiple folders) in your mailbox for emails with attachments and then saves the attachment of those emails to a file server and then deletes the attachments from the email and replaces them with hyperlinks of where that attachments where downloaded to. For details on the pros and cons please read my other article. I would also like to restate my warning from this article as well.

A Warning

Before you look at using these scripts you need to understand one Major point "these scripts are designed to modify the contents of email that is sent to a user". No matter how much testing you do on something there is no guarantees that this will work or work in an adverse way that may cause data loss on your server. Before even considering running these type of scripts make sure you have full backups of your email server and know how to do a restore.  If an email is in Rich Text these scripts will convert the email into HTML because there is no way i know of to append to an RTF email and keep the formatting (this doesn't mean its not possible though it just one of things I couldn't work out how to do). So use at your own risk and if you don't understand how these scripts work its probably better not to use them at all (or use it as an excuse to learn VBS or rewrite them as you own)

How it works

I have two versions of this script the first version only works on one folder eg for example if you want to run it on someone's sent items folder if you are using the event sinks from my other article. The other version uses the ideas from my mailbox crawl article and runs through all items in a mailbox and archives them if necessary it also creates a mailbox folder hierarchy on the file server share.

To start with some ADO connections are setup to retrieve all the items in the configured folder that have a attachment and are mail items

Set objArgs = WScript.Arguments
For I = 0 to objArgs.Count - 1
   if I = 0 then
	inbstr = objArgs(I)
   else
	inbstr = inbstr & " " & objArgs(I)
   end if
Next
on error resume next
Set Conn = CreateObject("ADODB.Connection")
Set Rec = CreateObject("ADODB.Record")
Set Rs = CreateObject("ADODB.Recordset")
Set fso = CreateObject("Scripting.FileSystemObject")
Conn.Provider = "ExOLEDB.DataSource"
rooturl = "file://./backofficestorage/yourdomain/MBX/" & inbstr
Conn.Open rooturl, ,3  
workfolder = rooturl & "/inbox"
rec.open workfolder
strView = "SELECT ""DAV:href"" "
strView = strView & "FROM scope ('shallow traversal of """& workfolder & """') "
strview = strview & " WHERE ""DAV:isfolder"" = false and ""urn:schemas:httpmail:hasattachment"" = True "
strview = strview & """DAV:contentclass"" = 'urn:content-classes:message' "
rs.CursorLocation = 3 'adUseServer = 2, adUseClient = 3
rs.CursorType = 3
rs.open strview, Rec.ActiveConnection, 2
if rs.recordcount <> 0 then
	rs.movefirst
	while not rs.eof
		err.clear
		from1 = rs.fields("DAV:href").value
		wfile.write from1 & vbCrLf
		Set msgobj = CreateObject("CDO.Message")
		msgobj.DataSource.Open rs.fields("DAV:href").value, ,3
		attachcount = msgobj.attachments.count
		savepath = "c:\temp\newb2\"  - You need to customise this bit to your own file server share
		redim attarray(attachcount)
		redim attnarray(attachcount)
		arrcnt = 1
 

The recordset is then looped through and each email is opened up and any attachments that are on that message are saved to a file share with the attachment name as the start of the file name and the received time as the end of the filename. This is done to ensure that the filenames are unique. In this part of the code you need to customize the savepath variable to what you want it to be eg (usershomedrive\attachments). An array is used to record each of the attachment paths that will be used later in the code.

		For Each objAttachment In msgobj.Attachments
			wfile.write objAttachment.filename & vbCrLf
			attext = mid(objAttachment.filename ,(instr((len(objAttachment.filename)-3),objAttachment.filename,".")+1) ,len(objAttachment.filename))
			attname = mid(objAttachment.filename , 1,instr((len(objAttachment.filename)-3),objAttachment.filename,".")-1)
			rtime = replace(msgobj.ReceivedTime,"/","-")
			rtime = replace(rtime,":","-")
			savefile = savepath
			savefile = savefile & attname & "_" & rtime & "." & attext
			objAttachment.SaveToFile savefile
			attarray(arrcnt) = savefile
			attnarray(arrcnt) = objAttachment.filename
			arrcnt = arrcnt + 1
		next
		if err.number = 0 then

The next part of the code deletes all the attachments on a messages by calling the deleteall method of the message object (note its not possible as far as I know to delete a single attachment on an email) After the attachment has been deleted it creates a hyperlink at the end of the message the points to the location where the attachment was downloaded to, for multiple attachment emails is loops back through the array of attachment paths to create multiple hyperlinks for these attachments. For this part a HTML body and Text body is provided. Note if the email is in Rich Text this email will be converted to HTML. (Its possible that some RTF formatting will be lost in this conversion)

			msgobj.attachments.deleteall
			if msgobj.HTMLBody <> "" Then
			if Instr(1,msgobj.HTMLBody,"</BODY") > 0 Then
				sHTMLBody = Left(msgobj.HTMLBody,Instr(1,msgobj.HTMLBody,"</BODY>")-1)
			Else
				sHTMLBody = msgobj.HTMLBody
			End if
			end if
			For I = 1 to attachcount
				msgobj.textbody = msgobj.textbody & vbCRLF & "****** Attachment: " & attnarray(i) & " <file://" & attarray(i) & ">"
				sHTMLBody = sHTMLBody & vbCRLF & "<BR><B><DIV><FONT face=Arial color=#004000 size=2>****** Attachment: <A href=" & chr(34) & " file://" & attarray(i) & chr(34) & "> " & attnarray(i) & "</A></DIV>"
			next
			msgobj.HTMLBody = sHTMLBody & "<BR><BR></BODY></HTML>"
			msgobj.textbody = msgobj.textbody & vbCRLF & vbCRLF
			msgobj.fields("urn:schemas:mailheader:keywords").value = "Attachment"
			msgobj.fields.update
			msgobj.DataSource.Save
		end if
	  	set msgobj = nothing
		rs.movenext	
	wend
end if
rs.close
df = msgbox("done")

Query options

There are a few options with this query you can use to change the characteristics of this code. Say you only want to archive attachments of messages that are over a year old in this case you could change the SQL query section of the code to include an extra part in the where clause eg

strView = "SELECT ""DAV:href"" "
strView = strView & "FROM scope ('shallow traversal of """& workfolder & """') "
strview = strview & " WHERE ""DAV:isfolder"" = false and ""urn:schemas:httpmail:hasattachment"" = True and "
strview = strview & """DAV:contentclass"" = 'urn:content-classes:message' and ""urn:schemas:httpmail:datereceived"" <= CAST(""2001-09-01T01:00:00Z"" as 'dateTime')"

Mailbox Crawl version

The mailbox crawl version of the code incorporates the ideas from my other article with the script described above to crawl through every object located within a user mailbox. An extra section of the code has been added to create the mailbox folder hierarchy on the file share you specify you need to customize this section of the code currently its set to a temporary directory on the mail server the root directory must exist or this code will fail.   This version is called mcattarchl.vbs and is included in the download from this page.

Running this script

Before you run this code please read and understand the warning at the top of this page this script is designed to change the contents of your mailbox store. If something goes wrong you may need to do a restore so 1 make sure you have a backup and 2 make sure you know how to do a restore in Exchange 2000. Before running you need to customise the rooturl to your own domain name if your not sure what this is have a look at the M: drive on your email server. You need to also customise the Savepath variable to a directory where you want the users attachments saved to i recommend you use some sort of UNC path or a defined mapped drive. Because this code uses Exloedb you must run the script directly on the mail server you want it to run on. For the Mailbox Crawl version of the code to run the code simply type the mailbox alias as a parameter of the script. For the single folder version of the code you must customise the code to include the users mailbox and folder you want it to run on to run this code see the code description above. Performance of the script isn't that wonderful and it may take a fair amount of time to run through a large mailbox (up to an hour on a really large mailbox). If you want you can put some logging script in the code so you can see the progress of it running please see my other article for details on how to do this

 Download Script

 

 


Disclaimer: Your use of the information contained in these pages is at your sole risk. All information on these pages is provided "as is", without any warranty, whether express or implied, of its accuracy, completeness, fitness for a particular purpose, title or non-infringement, and none of the third-party products or information mentioned in the work are authored, recommended, supported or guaranteed by Stephen Bryant or Pro Exchange. OutlookExchange.Com, Stephen Bryant and Pro Exchange shall not be liable for any damages you may sustain by using this information, whether direct, indirect, special, incidental or consequential, even if it has been advised of the possibility of such damages.

Copyright Stephen Bryant 2008