Monday, November 01, 2004

Finding what Resources are being used from a Meeting Request

I've blogged about resources in meetings before but this question came up last week about how do you tell which resources have been booked from a calendar request message(meeting invitation). When someone creates a meeting the resources that are booked (or attempting to be booked) with the meeting get stored in the BCC field of the appointment object. In the calendar request messages (invitations) that go out to the users (and resource mailboxes if your using some sort of auto accept system) the resources aren't included in the calendar request message (not even in the recipients collection even when the resource mailbox is receiving the calendar request). If you use Outlook to do the booking it does copy the resources display name into the location property but with OWA the location field is left blank unless the user specifically enters something . So from a calendar request message you received from a meeting organized by OWA how could you tell which resources where being booked with that meeting.

Here are a couple of methods that may help

The Mapi property 0x8167001E lists all the displaynames of attendees of a meeting and this seems to include all the resources as well. So what you can do is grab this property do a split on it to get the entries into an array. Loop though the array and check if each address is in the To or CC field of the appointment and if its not then its a resource. eg

Set iCalMsg = CreateObject("CDO.Message") "itemurl"
recplist =
recparray = split(recplist,";",-1,1)
for i = lbound(recparray) to ubound(recparray)
if instr(iCalMsg.fields(""),recparray(I)) then
if instr(iCalMsg.fields(""),recparray(I)) then
wscript.echo recparray(I)
end if
end if

Another method you can use is if you loop though the attendees collection of the appointment associated with the calendar request you can find the organizer of the appointment. Once you know the organizer of the appointment you can then use this along with the urn:schemas:calendar:uid field which uniquely identifies each appointment to call the GetAssociatedItem method of the calendar item. This will (if you have the rights to) connect to the organizers mailbox and then retrieve the master appointment object which will contain resources in the attendees collection . This all uses CDOEX and CDOEXM and the file URL scheme so for this to work the resource mailbox and organizers mailbox has have be on the same server.

Set iCalMsg = CreateObject("CDO.CalendarMessage") "itemurl"
For Each iCalPart In iCalMsg.CalendarParts
Set iAppt = iCalPart.GetUpdatedItem
cuid1 = iAppt.fields("urn:schemas:calendar:uid")
for each attend in iAppt.Attendees
if attend.IsOrganizer <> 0 then
Set Person = CreateObject("CDO.Person")
strURL = attend.address
Person.DataSource.Open strURL
Set Mailbox = Person.GetInterface("IMailbox")
set iAppt1 = iCalPart.GetAssociatedItem(Mailbox.calendar)
for each attend1 in iAppt1.Attendees
 wscript.echo attend1.address
 wscript.echo attend1.role
 wscript.echo attend1.status
 wscript.echo attend1.type
end if

If you wanted to use the second method but the mailboxes are on separate servers then something similar can be done using three webDAV queries and some ADSI. The following example first grabs the calendar message using a WebDAV GET and parses the organizer of the meeting out of the vCalendar body part and also the calendar UID. An ADSI query is then performed using the SMTP address of the organizer to retrieve the msExchHomeServerName property which tells you which server the mailbox is on. A second WebDAV search is then done of the organizers calendar based on the Calender UID of the appointment. This should then locate the original appointment which is then retrieved using a WebDAV GET. The resource mailbox (or mailbox's) SMTP address's are then parsed out of the vCalendar body part.
I've posted a copy of all the scripts from this post here

set Req = createobject("Microsoft.XMLHTTP") "GET","http://server/exchange/mailbox/inbox/calandermessage.EML",false
Req.setRequestHeader "Translate","f"
attendeearry = split(req.responsetext,"ORGANIZER;",-1,1)
for i = 1 to ubound(attendeearry)
string1 = vbcrlf & " "
stparse = replace(attendeearry(i),string1,"")
attaddress = mid(stparse,(instr(stparse,"MAILTO:")+7),instr(stparse,chr(13)))
attaddress = mid(attaddress,1,(instr(attaddress,vbcrlf)-1))
uidarry = mid(req.responsetext,instr(req.responsetext,"UID:")+3,len(req.responsetext))
string1 = vbcrlf & " "
stparse = replace(uidarry,string1,"")
uidprop = mid(stparse,2,instr(stparse,vbcrlf))
uidprop = replace(uidprop,vbcrlf,"")
CUserID = replace(attaddress," ","")
Set objDNS = CreateObject("ADSystemInfo")
DomainName = LCase(objDNS.DomainDNSName)
Set oRoot = GetObject("LDAP://" & DomainName & "/rootDSE")
strDefaultNamingContext = oRoot.get("defaultNamingContext")
GALQueryFilter = "(&(&(&(& (mailnickname=*) (|
)))(objectCategory=user)(mail=" & CUserID & ")))"
strQuery = "<LDAP://" & DomainName & "/" & strDefaultNamingContext & ">;" &
GALQueryFilter &
Set oConn = CreateObject("ADODB.Connection") 'Create an ADO Connection
oConn.Provider = "ADsDSOOBJECT" ' ADSI OLE-DB provider
oConn.Open "ADs Provider"

Set oComm = CreateObject("ADODB.Command") ' Create an ADO Command
oComm.ActiveConnection = oConn
oComm.Properties("Page Size") = 1000
oComm.CommandText = strQuery

Set rs = oComm.Execute

server =
mailbox = attaddress
strURL = "http://" & server & "/exchange/" & mailbox & "/calendar/"
strQuery = "<?xml version=""1.0""?><D:searchrequest xmlns:D = ""DAV:"" >"
strQuery = strQuery & "<D:sql>SELECT ""DAV:href"" FROM scope('shallow traversal
of """
strQuery = strQuery & strURL & """') Where ""urn:schemas:calendar:uid"" = '" &
uidprop & "'</D:sql></D:searchrequest>"
set req = createobject("microsoft.xmlhttp") "SEARCH", strURL, false
req.setrequestheader "Content-Type", "text/xml"
req.setRequestHeader "Translate","f"
req.send strQuery
If req.status >= 500 Then
wscript.echo "Status: " & req.status
wscript.echo "Status text: An error occurred on the server."
ElseIf req.status = 207 Then
set oResponseDoc = req.responseXML
set oNodeList = oResponseDoc.getElementsByTagName("a:href")
For i = 0 To (oNodeList.length -2)
set oNode = oNodeList.nextNode
wscript.echo "Status: " & req.status
wscript.echo "Status text: " & req.statustext
wscript.echo "Response text: " & req.responsetext
End If

sub proccalmess(objhref) "GET", objhref, false
Req.setRequestHeader "Translate","f"
attendeearry = split(req.responsetext,"ATTENDEE;",-1,1)
for i = 1 to ubound(attendeearry)
string1 = vbcrlf & " "
stparse = replace(attendeearry(i),string1,"")
attaddress = mid(stparse,(instr(stparse,"MAILTO:")+7),instr(stparse,chr(13)))
attaddress = mid(attaddress,1,instr(attaddress,vbcrlf))
if instr(stparse,"=RESOURCE") then
wscript.echo attaddress
end if

end sub