Wednesday, July 27, 2005

Display, Add and Remove Public folder replica’s using WebDAV via script

Someone asked last week for some help with a WebDAV script that shows public folders replicas. Now this is one of the cool things you can do using PfdavAdmin which also uses WebDAV to do the same thing. So this makes it rife for scripting the thing to remember when you want to change these types of settings that pfdavadmin is modifying is that you need to use the Administrative Virtual Root .

The property that holds the replica list is http://schemas.microsoft.com/mapi/proptag/0x66980102 which when you working in WebDAV is Base64 encoded null terminated strings. So to successfully display the replicas in the list you first need to decode the base 64 and then parse out and replace the null terminators so it can be something meaning in VB. The method I’ve used is just to replace the nulls with Line Feeds this makes it firstly easy to display the value and also the Line Feeds can also act as placeholders when you need to build the new replica list to post back to the server when you want to add or remove replicas.

There are a few different subs and functions in this script which all perform different tasks the flow of the script itself works like this.

You run it from the command-line with 3 command-line parameters the first is the action you want to take the valid actions are display, add or remove. The next parameter you need is the server you want to add or remove the replica from and the third parameter is the folder path from the root. So say you want to add a replica of the folder “Shared/Company Info” to the server called server1 you would use cscript pubrep.vbs add server1 “Shared/Company Info”. The one thing that you do need to change to make this script work properly is the path to the administrative virtual root which is in the following line

admnamefold = "http://" & servername & "/ExAdmin/Admin/YouDomain.com /public folders/" & foldername & "/"

The domain name should be your default SMTP domain in your default Recipient policy. The other easy way to find this out is have a look at ExAdmin directory with IIS Admin on any mail server (or use Pfdavadmin and the property editior)

The Octenttostring function takes the base64 encoded value decodes into one string variable also removing the null termination characters and replacing them with Line feeds.

The Repaction sub then runs and look at the action requested if its display it just outputs the replica’s. If its add or remove it then calls the add or remove sub. The add and remove subs work essentially the same way they build the new replica list be either adding or removing a public store from it.

Because an Exchange server can have multiple public stores but only one MAPI Public Folder tree the Publicfolder function take the netbios name of the server and finds DN of the server in the configuration partition. It then finds the default MAPI folder tree by querying for all the PF folder trees with the msExchPFTreeType property = 1. These two pieces of information are then used to find the public folder store in the configuration partition for that server that hosts the MAPI Public Folder tree and then retrieves the Exchange legacy DN of this store which is needed to construct or remove the server from the replica list. The rest of the sub either removes or adds the new public folder MDB url to replica list replaces the LF with nulls again and then calls the proppatchrep function which first does a few tricks to encode the new replica list in base64 it first does a binary conversion using an ADODB.Stream (thanks to Paul Randall who I picked this up from) and then uses a temporary XML document to do the base64 encoding. I could have used the XML document to create the XML for the Proppatch but at the end of the day its simpler just to use construct a string manually.

The only other thing to note about using the administrative Virtual Root is that you need to have admin rights to use it. I’ve posted a download copy of the script here the code look like.

action = wscript.arguments(0)
servername = wscript.arguments(1)
foldername = wscript.arguments(2)
public admnamefold
admnamefold = "http://" & servername & "/ExAdmin/Admin/domain.com/public
folders/" & foldername & "/"
Set objX = CreateObject("Microsoft.XMLHTTP")
objX.Open "PROPFIND", admnamefold, FALSE, "", ""
strR = "<?xml version='1.0'?>"
strR = strR & "<a:propfind xmlns:a='DAV:' xmlns:e='http://schemas.microsoft.com/mapi/proptag/'>"
strR = strR & "<a:prop><e:x66980102/></a:prop></a:propfind>"
objX.SetRequestHeader "Content-type:", "text/xml"
objX.SetRequestHeader "Depth", "0"
objX.send(strR)

set docback = objX.responseXML
Dim objNodeList
Set objNodeList = docback.getElementsByTagName("d:x66980102")
For i = 0 TO (objNodeList.length -1)
Set objNode = objNodeList.nextNode
strrep = Octenttostring(objNode.nodeTypedValue)
call Repaction(strrep,action,servername)
Next


sub Repaction(strrep,action,servername)

select case action
case "display" wscript.echo "Current Replica's"
wscript.echo
wscript.echo strrep
case "add" call addrep(strrep,servername)
case "remove" call deleterep(strrep,servername)
case else Wscript.echo "Invalid Commmand use add,remove or display"
end select

end sub

sub addrep(strrep,servername)
aexist = 0
reparray = split(strrep,chr(10),-1,1)
pubfolderLDN = PublicfolderLDN(Servername)
for i = lbound(reparray) to ubound(reparray)
if ucase(reparray(i)) = ucase(pubfolderLDN) then aexist = 1
next
if aexist = 1 then
wscript.echo "Replica already Exist for this server"
call Repaction(strrep,"display",servername)
else
strrep = replace(strrep,chr(10),chr(0)) & pubfolderLDN & chr(0)
call proppatchrep(strrep)
end if
end sub

sub deleterep(strrep,servername)
aexist = 0
nstr = ""
reparray = split(strrep,chr(10),-1,1)
pubfolderLDN = PublicfolderLDN(Servername)
if ubound(reparray) > 1 then
for i = lbound(reparray) to ubound(reparray)-1
if ucase(reparray(i)) = ucase(pubfolderLDN) then
aexist = 1
else
nstr = reparray(i) & chr(0)
end if
next
else
Wscript.echo "You cant delete the only replica of this folder !"
end if
if aexist = 1 then
call proppatchrep(nstr)
else
wscript.echo "NO Replica Exists for this server"
call Repaction(strrep,"display",servername)
end if
end sub

sub proppatchrep(strrep)

set convobj = CreateObject("Msxml2.DOMDocument.4.0")
Set oRoot = convobj.createElement("test")
oRoot.dataType = "bin.base64"
OriginalLocale = SetLocale(1033)
set stm = CreateObject("ADODB.Stream")
stm.Type = 2
stm.Charset = "x-ansi"
stm.Open
stm.WriteText strrep
stm.Position = 0
stm.Type = 1
oRoot.nodeTypedValue = stm.Read
SetLocale(OriginalLocale)
xmlstring = "<?xml version=""1.0"" encoding=""utf-8""?>"
xmlstring = xmlstring & "<d:propertyupdate xmlns:d=""DAV:""
xmlns:m=""http://schemas.microsoft.com/mapi/proptag/""" _
& " xmlns:b=""urn:schemas-microsoft-com:datatypes/"">" & vbcrlf & "<d:set>" &
vbcrlf & "<d:prop>" & vbcrlf & "<m:0x66980102 b:dt=""bin.base64"">"
xmlstring = xmlstring & oRoot.text & vbcrlf
xmlstring = xmlstring & "</m:0x66980102>" & vbcrlf & "</d:prop>" & vbcrlf &
"</d:set>" & vbcrlf & "</d:propertyupdate>"
Set xmlReq = CreateObject("Microsoft.XMLHTTP")
xmlReq.open "PROPPATCH",admnamefold, False
xmlReq.setRequestHeader "Content-Type", "text/xml"
xmlReq.send xmlstring
If xmlReq.status >= 500 Then
wscript.echo "Status: " & xmlReq.status
wscript.echo "Status text: An error occurred on the server."
ElseIf xmlReq.status = 207 Then
wscript.echo "Replica Set sussessfully"
Else
wscript.echo "Status: " & xmlReq.status
wscript.echo "Status text: " & xmlReq.statustext
wscript.echo "Response text: " & xmlReq.responsetext
End If

end sub

Function Octenttostring(OctenArry)
ReDim aOut(UBound(OctenArry))
For i = 1 to UBound(OctenArry) + 1
if ascb(midb(OctenArry,i,1)) <> 0 then
aOut(i-1) = chr(ascb(midb(OctenArry,i,1)))
else
aOut(i-1) = chr(10)
end if
Next
Octenttostring = join(aOUt,"")
End Function

function PublicfolderLDN(Servername)
set conn = createobject("ADODB.Connection")
set com = createobject("ADODB.Command")
set conn1 = createobject("ADODB.Connection")
Set iAdRootDSE = GetObject("LDAP://RootDSE")
strNameingContext = iAdRootDSE.Get("configurationNamingContext")
strDefaultNamingContext = iAdRootDSE.Get("defaultNamingContext")
Conn.Provider = "ADsDSOObject"
Conn.Open "ADs Provider"
svcQuery = "<LDAP://" & strNameingContext &
">;(&(objectCategory=msExchExchangeServer)(cn=" & Servername &
"));cn,name,distinguishedName,legacyExchangeDN;subtree"
Com.ActiveConnection = Conn
Com.CommandText = svcQuery
Set Rs = Com.Execute
while not rs.eof
defpf = "(&(objectCategory=msExchPFTree)(msExchPFTreeType=1))"
strQuery = "<LDAP://" & strNameingContext & ">;" & defpf &
";distinguishedName;subtree"
com.Properties("Page Size") = 100
Com.CommandText = strQuery
Set Rs1 = Com.Execute
while not rs1.eof
snameq = "(&(&(objectCategory=msExchPublicMDB)(msExchOwningPFTree=" &
rs1.fields("distinguishedName") _
& ")(msExchOwningServer=" & rs.fields("distinguishedName") & ")))"
strQuery = "<LDAP://" & strNameingContext & ">;" & snameq &
";distinguishedName,legacyExchangeDN;subtree"
com.Properties("Page Size") = 100
Com.CommandText = strQuery
Set Rs2 = Com.Execute
while not rs2.eof
PublicfolderLDN = rs2.fields("legacyExchangeDN")
rs2.movenext
wend
rs1.movenext
wend
rs.movenext
wend
end function

Friday, July 15, 2005

Displaying all the Message Size limits via Script

Message Size limits in Exchange can be set at a number of different levels to give you the flexibility that we as IT people yearn for. This flexibility does come at a cost if you have to answer that quick question about what limits you actually have in your Exchange Org. If you want to work out what the effective message size limit was for one particular user via the GUI you would first have to check

Global Setting under Message Delivery
The Setting on Each Connector the mail could flow though
The Setting on Each of the SMTP Virtual Servers mail could flow though
The Setting on the Actual user account

(This is all documented in Q322679 )

So what I’ve put together is a script that queries all these setting from active directory and outputs the results to the console. The Script logic is pretty simple if queries each of the different objects types in the configuration partition and then accesses the limit property if they have been set and display it back to console. The first query hits the Global settings the second returns all the connector the third returns the virtual servers and the last 2 query looks for all the users that have had individual message size limit configured. These last two queries may look a bit odd but by default the message size limits on a user account would not be set so doing a query where (delivContLength=*) will only return those account where this property has been set.

One thing to remember here is this shows all the native places where message limits may have been configured a lot of third party software such a AV/SPAM/Content-filtering application may also implement this functionality.

I’ve put a downloadable copy of the script here the code looks like

set conn = createobject("ADODB.Connection")
set com = createobject("ADODB.Command")
Set iAdRootDSE = GetObject("LDAP://RootDSE")
strNameingContext = iAdRootDSE.Get("configurationNamingContext")
strDefNamingContext = iAdRootDSE.Get("defaultNamingContext")
Conn.Provider = "ADsDSOObject"
Conn.Open "ADs Provider"
gsQuery = "<LDAP://" & strNameingContext & ">;(objectCategory=msExchMessageDeliveryConfig);name,distinguishedName;subtree"
Com.ActiveConnection = Conn
Com.CommandText = gsQuery
Set Rs = Com.Execute
Wscript.echo "Global Settings - Message Delivery Properties"
Wscript.echo
While Not Rs.EOF
strconfcont = "LDAP://" & rs.fields("distinguishedName")
set ccConfig = getobject(strconfcont)
wscript.echo "Sending Message Size Limit: " & ccConfig.submissionContLength & "
KB"
wscript.echo "Recieving Message Size Limit: " & ccConfig.delivContLength & " KB"
wscript.echo "Recipient Limits: " & ccConfig.msExchRecipLimit
rs.movenext
wend
Wscript.echo
Wscript.echo "Connector Settings"
wscript.echo
vsQuery = "<LDAP://" & strNameingContext &
">;(objectCategory=msExchRoutingSMTPConnector);name,distinguishedName;subtree"
Com.ActiveConnection = Conn
Com.CommandText = vsQuery
Set Rs = Com.Execute
While Not Rs.EOF
strconnect = "LDAP://" & rs.fields("distinguishedName")
set cnCconnect = getobject(strconnect)
wscript.echo "Connector Name:" & cnCconnect.cn
wscript.echo "Max Message Size Limit:" & cnCconnect.delivContLength & " KB"
wscript.echo
rs.movenext
wend
Wscript.echo
Wscript.echo "SMTP Virtual Server Settings"
wscript.echo
vsQuery = "<LDAP://" & strNameingContext &
">;(objectCategory=protocolCfgSMTPServer);name,distinguishedName;subtree"
Com.ActiveConnection = Conn
Com.CommandText = vsQuery
Set Rs = Com.Execute
While Not Rs.EOF
strstmsrv = "LDAP://" & rs.fields("distinguishedName")
set svsSmtpserver = getobject(strstmsrv)
wscript.echo "ServerName:" &
mid(svsSmtpserver.distinguishedName,instr(svsSmtpserver.distinguishedName,"CN=Protocols,")+16,instr(svsSmtpserver.distinguishedName,",CN=Servers")-(instr(svsSmtpserver.distinguishedName,"CN=Protocols,")+16))
wscript.echo "Virtual Server Name:" & svsSmtpserver.adminDisplayName
wscript.echo "Max Message Size Limit:" &
svsSmtpserver.msExchSmtpMaxMessageSize/1024 & " KB"
wscript.echo "Recipient Limits:" & svsSmtpserver.msExchSmtpMaxRecipients
wscript.echo
rs.movenext
wend
Wscript.echo
Wscript.echo "Users with Sending Limits"
wscript.echo
srquery = "<LDAP://" & strDefNamingContext &
">;(&(&(objectCategory=Person)(objectclass=user)(submissionContLength=*)));name,displayname,distinguishedName
,submissionContLength;subtree"
Com.ActiveConnection = Conn
Com.CommandText = srquery
Set Rs = Com.Execute
While Not Rs.EOF
wscript.echo "User:" & rs.fields("displayname")
wscript.echo "Sending Message Size Limit:" & rs.fields("submissionContLength") &
" KB"
wscript.echo
rs.movenext
wend
Wscript.echo
Wscript.echo "Users with Receiving Limits"
wscript.echo
srquery = "<LDAP://" & strDefNamingContext &
">;(&(&(objectCategory=Person)(objectclass=user)(delivContLength=*)));name,displayname,distinguishedName,delivContLength;subtree"
Com.ActiveConnection = Conn
Com.CommandText = srquery
Set Rs = Com.Execute
While Not Rs.EOF
wscript.echo "User:" & rs.fields("displayname")
wscript.echo "Receiving Message Size Limit:" & rs.fields("delivContLength") & "
KB"
wscript.echo
rs.movenext
wend

Shared Mailbox – Database – Public Folder Replacement

There’s been a bit of buzz lately about the uncertain future of public folders. This got me thinking about how I use public folders sometimes to achieve certain things and how I might go about replacing this. One of things I use public folders for sometimes is to act as a shared mailbox like Mark has described in http://www.msexchange.org/articles/MF021.html . This is a quick easy way to achieve a shared mailbox and works fairly well.

There are a few ways you could go about replacing something like this because I have a database server and a few IIS servers already in my network with spare capacity this seemed like it could offer an alternative. So I started with a basic framework of a mailbox that would receive email for my shared mailbox’s email address this would then fire an Event sink attached to the mailbox which would then parse the message and the attachments out and store these is a database. I’ve then got an ASP.NET web application that can then read and display a list of messages and attachments. When I started this I was focused more on attachments more then the content of the messages because I have a shared mailbox that receives a lot of attachments. So I wanted something that would allow me to download the attachments on the messages from the “inbox view” without having to look at the content of each message. This is the type of View I came up with http://bluetack.aspxconnection.com/images/listview.jpg

After I had the attachment download going it seemed a shame just to leave it there so I included some more forms to show the message content and then lastly to respond to the message.

The Database

Storing email in a database creates a lot of fairly unique challenges essentially email is bunch of blobs of data which you then need to store in structured relational tables. I’ve had one go at this before which had mixed results the main problem is when you start to stored a lot of data in flat tables the table and database can start to become a little unweilding. So for my second attempt I’ve gone for three tables and one view this time.

The inbox table: the first table stores the major email header fields and the html message body which is stored in a text field in the database.

The Attachments table: This stores all the attachments from a message it’s related to the inbox table based on the PR_Entry_ID from the Exchange Store. To store the attachments what I decided to do is store them in the RFC 2045 MIME encoded body part format that can accessed using CDOEX via the event sink. Within the ASP.NET app that allows the download of the attachment I’ve used CDOEX as well (you could also use CDOSYS) it basically uploads the attachments you want to download into a temporary message object and then decodes the MIME and sends it back to the user via the browser response stream. The message Attachment’s MIME body part is stored in a text field in the attachments table.

The Sent table stores sent mail I’ve used the System.web.Mail class to send mail in my web application via SMTP this helps if the mailbox the sink is on has a different primary address to that you want to use for replies this way you bypass the Exchange submission URI the bad part is this is that mail doesn’t get stored in the sent items of the mailbox so this is the reason for the database insert.

The vwViewMessages view is a database view that links both the inbox and attachments table together via an Outer join to display all the attachments of a message in a single database view.

I’ve included a SQL script in the download for this article with all the table definitions I used

The Event sink

Currently I’ve used a script based async onsave event sink the sink itself is relatively straight forward. It basically takes the header fields and message body and then parses out any single quotes (‘) and escapes them with another quote to ensure that they can be inserted into a database successfully. It also loops though all the attachments in the message and then populates the attachment table with any attachments from the message. For embedded messages which are those attachments with a content type of RFC/822 the whole embedded message is stored in the attachments table. There some code to decode the PR_EntryID into a hex value which is then used to uniquly referance each mail

The Web Application

The Web application itself consists of four different forms which all perform distinct functions

Listmsg.aspx This form displays a list of messages and attachments using the vwViewMessages view. As I said previously to allow for attachments to be downloaded directly from this view I’ve come up with some custom code to create a custom view of the inbox messages to make it more readable I’m using the ItemDataBound event of the Datagrid to delete duplicated field data that isn’t required this gives a more hierarchal type view of the messages. The ItemDataBound event is also used to add ahref and build querystrings to pass in the entryid and database names to the other forms.

Attachdownld.aspx This form handles retrieving the required attachment from the attachments table decodes it and then returns it to the user via the response stream of the browser. To decode the attachment it use CDOEX, because CDOEX is usually only supported on a Exchange server you can use CDOSYS if you using this on a server that isn’t a Exchange box the only parts of CDOEX that is used is the GetDecodedContentStream which is common within both libraries. If the attachment that the user is trying to download is an embedded message the code needs to handle this differently. What it does is loads the whole embedded message via the streams interface and this provides the whole message to the user via the browser stream. This does bring up one issue if the embedded message has attachments of its own. In this case if you try to download the attachment to the file system it will fail but if you try to open it up in an email application directly it will succeed okay. I’m not 100% sure why this is happening I think its something to do with having multiple content dispositions in the response stream. The workaround that I’ve used is just to tell people to open it up using Outlook or Outlook Express.

Showmsg.aspx This form is a basic message display form pretty straight forward using some query strings to work out what mail you want to open and which database to use. It offers a link to the reply form and a link to go back

Replymsg.aspx: This is the Reply form which gives the user a chance to respond to the message. As a text interface for the reply I’ve used the freetextbox control from www.freetextbox.com. This is one seriously cool .Net control and provides very rich user input functionality that would have take weeks to write if you had to do it you’re self. There's some code the puts the body of the message your responding to in the body of the response and it also creates a header and separator very similar to OWA. Once the send button is clicked on this form the System.Web.Mail class is used to send the mail via SMTP and also some ADO.NET code is used to insert the sent mail into the sent table in the database.

Installation Hard-coded bits and pieces

This code is very much a work in progress but I thought there was enough cool bits and pieces that might be of interest to some people and the reality is that a final complete version may not ever surface because of my time constraints. This is a list of hard-coded bits you need to change. You also need to create your own interops for CDOEX and ADODB (just use the PIA). I’ve create a download that contains all the relative Visual studio files and the event sink code which can be download here . To use the reply form you also need to download and install the freetextbox control from www.freetextbox.com

Event Sink
Database connection string Eg (Data Source=server;Initial Catalog=SharedMailbox;User Id=username;Password=pass)

DatabaseNames I’ve used a generic database name at the moment you’ll need to set it to the name of the database you create.

Aspx files
Database connection strings in all aspx and event sink. Eg (Data Source=server;Initial Catalog=SharedMailbox;User Id=username;Password=pass)

DatabaseNames I’ve used a generic database name at the moment you’ll need to set it to the name of the database you create.

Listmsg.aspx SQL select string needs to be set with the table name for the inbox

daSqlDataAdapter = new SqlDataAdapter("Select EntryID,DateSent,FromName,Subject,filename,attachnum from [vwViewMessages@address@domain.com]", scSQLConnection);

Itemdataboundevent database name needs to be set
string dnDBName = " address@domain.com";

Replymsg.aspx

From email address in the following lines

msMessage.From = "address@yourdomain.com";

and

string hdReplyheader = "<BR><BR><hr width=\"100%\" size=\"1\" color=\"#808080\" align=\"left\" noshade>" +
"<B>From:</B>" + drDatareader["FromName"].ToString() + "[" + drDatareader["FromEmail"].ToString() +"]" + "<BR>" +
"<B>Sent:</B>" + drDatareader["DateSent"].ToString() + "<BR>" +
"<B>To:</B>address@yourdomain.com" + "<BR>" +
"<B>Subject:</B>" + drDatareader["Subject"].ToString();

The event sink code looks like

<SCRIPT LANGUAGE="VBScript">

Sub ExStoreEvents_OnSave(pEventInfo, bstrURLItem, lFlags)

call dbinsert(bstrURLItem)

End Sub

sub dbinsert(murl)
on error resume next
dbDatabaseName = "address@domain.com"
dbAttachmentsName = "Attachments@" & dbDatabaseName
dbDatabaseName = "inbox@" & dbDatabaseName
Set Cnxn1 = CreateObject("ADODB.Connection")
strCnxn1 = "Data Source=server;Initial Catalog=SharedMailbox;User Id=username;Password=pass;"
Cnxn1.Open strCnxn1
set msg = createobject("cdo.message")
msg.datasource.open murl
eiEntryID = Octenttohex(msg.fields("http://schemas.microsoft.com/mapi/proptag/0x0FFF0102").value)
miMessageID = msg.fields("urn:schemas:mailheader:message-id").value
dhDavhref = msg.fields("DAV:Href").value
stSenttime = msg.fields("urn:schemas:httpmail:datereceived").value
fnFromName = msg.fields("urn:schemas:httpmail:fromname").value
feFromEmail = replace(replace(msg.fields("urn:schemas:httpmail:fromemail").value,"<",""),">","")
toToEmail = msg.fields("urn:schemas:mailheader:to").value
sjSubject = msg.Subject
tbTextBody = msg.fields("urn:schemas:httpmail:htmldescription")
haHasAttach = msg.fields("urn:schemas:httpmail:hasattachment").value
line_to_insert = ("'" & eiEntryID & "','" & replace(miMessageID,"'","''") &
"','" & dhDavhref & "','" & stSenttime & "','" & replace(fnFromName,"'","''") &
_
"','" & replace(feFromEmail,"'","''") & "','" & replace(toToEmail,"'","''") &
"','" & replace(sjSubject,"'","''") & _
"','" & left(replace(tbTextBody,"'","''"),255) & "','" &
replace(tbTextBody,"'","''") & "','" & haHasAttach & "'")
sqlstate1 = "insert into [" & dbDatabaseName & "] values(" & line_to_insert &
")"
Cnxn1.Execute(sqlstate1)
i = 1
set objattachments = msg.attachments
for each objattachment in objattachments
if objAttachment.ContentMediaType = "message/rfc822" then
set msg1 = createobject("cdo.message")
msg1.datasource.OpenObject objattachment, "ibodypart"
fnFileName = msg1.subject & "(" & i & ")" & ".eml"
ctContentType = "message/rfc822"
ceContentTransferEncoding = "7bit"
cdContentDisposition =
msg1.Fields("urn:schemas:mailheader:content-disposition").value
set stm = msg1.getstream
mbMessageBody = stm.readtext
line_to_insert = ("'" & eiEntryID & "','" & i & "','" &
replace(fnFileName,"'","''") & "','" & replace(ctContentType,"'","''") & _
"','" & replace(ceContentTransferEncoding,"'","''") & "','" &
replace(cdContentDisposition,"'","''") & "','" & replace(mbMessageBody,"'","''")
& "'")
sqlstate1 = "insert into [" & dbAttachmentsName & "] values(" & line_to_insert &
")"
Cnxn1.Execute(sqlstate1)
else
fnFileName = objattachment.filename
ctContentType = objattachment.ContentMediaType
ceContentTransferEncoding = objattachment.ContentTransferEncoding
cdContentDisposition =
objattachment.Fields("urn:schemas:mailheader:content-disposition").value
set stm = objAttachment.getstream
mbMessageBody = stm.readtext
line_to_insert = ("'" & eiEntryID & "','" & i & "','" &
replace(fnFileName,"'","''") & "','" & replace(ctContentType,"'","''") & _
"','" & replace(ceContentTransferEncoding,"'","''") & "','" &
replace(cdContentDisposition,"'","''") & "','" & replace(mbMessageBody,"'","''")
& "'")
sqlstate1 = "insert into [" & dbAttachmentsName & "] values(" & line_to_insert &
")"
Cnxn1.Execute(sqlstate1)
end if
i = i + 1
next
set msg = nothing

end sub

Function Octenttohex(OctenArry)
ReDim aOut(UBound(OctenArry))
For i = 1 to UBound(OctenArry) + 1
if len(hex(ascb(midb(OctenArry,i,1)))) = 1 then
aOut(i-1) = "0" & hex(ascb(midb(OctenArry,i,1)))
else
aOut(i-1) = hex(ascb(midb(OctenArry,i,1)))
end if
Next
Octenttohex = join(aOUt,"")
End Function
</SCRIPT>