Copyright © 2024 IMIBO. Privacy Statement
Extended MAPI in DELPHI
Experimental # 4
Public Free Busy
Do you know that:
– Free/busy data is derived from calendar data and falls into four categories: Free, Busy, Tentative and Out of Office (OOF).
– It is not necessary to „enumerate“ each Mailbox to get those data
– For an arbitrary user, you can generally access free/busy data in the Public Folders. You can to access the information directly from the IMAPIFolder Contents Table from the Public Folder.
The Public Folder Based Free/Busy data assumes that the server is configured to support public folders.
function GetFreeBusyForAll(const MAPISession: IMapiSession): TStrings; var PublicStore: IMsgStore; // Exchange Server Public Store Prop: PsPropValue; FreeBusyFolder: IMAPIFolder; // Free/Busy Folder ObjType: ULONG; MAPITable: IMAPITable; // Free/Busy Content Table ContentTableRowCount: ULONG; RowSet: PSRowSet; iCount: Integer; PP: PsPropValue; PropMonth, PropEvent: PsPropValue; FreeBussyInfo: PFreeBussyInfo; hr: HResult; List: TStrings absolute Result; CountMonths, MinEvents: Integer; jCount: Integer; StartDateInt: Integer; StartDate: TDateTime; yCount: Integer; _TSBinary: TSBinary; DateArrayLen: Integer; dwEnd: DWORD; const // minutes from Jan. 1, 1601 // PR_FREEBUSY_START_RANGE = (ULONG($6847) shl 16) or PT_LONG; // minutes from Jan. 1, 1601 // PR_FREEBUSY_END_RANGE = (ULONG($6848) shl 16) or PT_LONG; // e-mail of a user this message belongs to // PR_FREEBUSY_EMAIL_ADDRESS = (ULONG($6849) shl 16) or PT_TSTRING; // all events (busy, tentitative or OOF) // PR_FREEBUSY_ALL_MONTHS = (ULONG($684F) shl 16) or PT_MV_LONG; // PR_FREEBUSY_ALL_EVENTS = (ULONG($6850) shl 16) or PT_MV_BINARY; // tentitative // PR_FREEBUSY_TENTITATIVE_MONTHS = (ULONG($6851) shl 16) or PT_MV_LONG; // PR_FREEBUSY_TENTITATIVE_EVENTS = (ULONG($6852) shl 16) or PT_MV_BINARY; // busy // PR_FREEBUSY_BUSY_MONTHS = (ULONG($6853) shl 16) or PT_MV_LONG; // PR_FREEBUSY_BUSY_EVENTS = (ULONG($6854) shl 16) or PT_MV_BINARY; // Out of office // PR_FREEBUSY_OOO_MONTHS = (ULONG($6855) shl 16) or PT_MV_LONG; // PR_FREEBUSY_OOO_EVENTS = (ULONG($6856) shl 16) or PT_MV_BINARY; ColumnPropTagArray: record cValues: ULONG; aulPropTag: array [0 .. 10] of ULONG; end = (cValues: 11; aulPropTag: (PR_FREEBUSY_EMAIL_ADDRESS, PR_SUBJECT, PR_FREEBUSY_START_RANGE, PR_FREEBUSY_END_RANGE, PR_FREEBUSY_ALL_MONTHS, PR_FREEBUSY_ALL_EVENTS, PR_FREEBUSY_TENTATIVE_MONTHS, PR_FREEBUSY_TENTATIVE_EVENTS, PR_FREEBUSY_OOF_MONTHS, PR_FREEBUSY_OOF_EVENTS, PR_FREEBUSY_LAST_MODIFIED)); function RightStr(const Value: String; const VCount: Integer): String; begin Result := Copy(Value, Length(Value) + 1 - VCount, VCount); end; begin List := nil; Prop := nil; RowSet := nil; List := TStringList.Create; // Find and Open Exchange Public Store hr := HrOpenExchangePublicStore(MAPISession, PublicStore); if failed(hr) then raise EMAPIError.CreateMAPI(MAPISession, hr); try // Request PR_FREE_BUSY_FOR_LOCAL_SITE_ENTRYID hr := HrGetOneProp(PublicStore, PR_FREE_BUSY_FOR_LOCAL_SITE_ENTRYID, Prop); if failed(hr) then raise EMAPIError.CreateMAPI(PublicStore, hr); if Prop.ulPropTag <> PR_FREE_BUSY_FOR_LOCAL_SITE_ENTRYID then Exit; // Open FREE_BUSY_FOR_LOCAL_SITE Folder hr := PublicStore.OpenEntry(Prop.Value.bin.cb, PENTRYID(Prop.Value.bin.lpb), nil, MAPI_BEST_ACCESS, ObjType, IUnknown(FreeBusyFolder)); if failed(hr) then raise EMAPIError.CreateMAPI(PublicStore, hr); // Get MAPI Contents Folder hr := FreeBusyFolder.GetContentsTable(0, MAPITable); If failed(hr) Then Raise EMAPIError.CreateMAPI(FreeBusyFolder, hr); ContentTableRowCount := 0; hr := MAPITable.GetRowCount(0, ContentTableRowCount); If failed(hr) Then Raise EMAPIError.CreateMAPI(MAPITable, hr); If ContentTableRowCount > 0 Then Begin hr := MAPITable.SetColumns(@ColumnPropTagArray, TBL_BATCH); If failed(hr) Then Raise EMAPIError.CreateMAPI(MAPITable, hr); While True Do Begin RowSet := nil; hr := MAPITable.QueryRows(25, 0, RowSet); if failed(hr) then Exit; If Assigned(RowSet) Then If RowSet.cRows = 0 Then Begin If Assigned(RowSet) Then FreePRows(RowSet); RowSet := nil; Break; End Else Begin For iCount := 0 To RowSet.cRows - 1 Do Begin New(FreeBussyInfo); PP := PpropFindProp(RowSet.aRow[iCount].lpProps, RowSet.aRow[iCount].cValues, PR_FREEBUSY_EMAIL_ADDRESS); // Construct Free/Busy Info if Assigned(PP) Then FreeBussyInfo.EMailAddress := PP.Value.lpsz else begin PP := PpropFindProp(RowSet.aRow[iCount].lpProps, RowSet.aRow[iCount].cValues, PR_SUBJECT); if Assigned(PP) Then FreeBussyInfo.EMailAddress := PP.Value.lpsz end; // (* we will get User Alias from E-mail Address /CN=.../CN=..... FreeBussyInfo.EMailAddress := UPPERCASE(RightStr(FreeBussyInfo.EMailAddress, Length(FreeBussyInfo.EMailAddress) - LastDelimiter('/', FreeBussyInfo.EMailAddress) - 3)); // *) PP := PpropFindProp(RowSet.aRow[iCount].lpProps, RowSet.aRow[iCount].cValues, PR_FREEBUSY_START_RANGE); if Assigned(PP) Then FreeBussyInfo.StartDate := DateTimeToLocalDateTime(MinutesToDateTime(PP.Value.l)); PP := PpropFindProp(RowSet.aRow[iCount].lpProps, RowSet.aRow[iCount].cValues, PR_FREEBUSY_END_RANGE); if Assigned(PP) Then FreeBussyInfo.EndDate := DateTimeToLocalDateTime (MinutesToDateTime(PP.Value.l)); CountMonths := 0; MinEvents := 0; PropMonth := PpropFindProp(RowSet.aRow[iCount].lpProps, RowSet.aRow[iCount].cValues, PR_FREEBUSY_ALL_MONTHS); if Assigned(PropMonth) Then CountMonths := PropMonth.Value.MVl.cValues; PropEvent := PpropFindProp(RowSet.aRow[iCount].lpProps, RowSet.aRow[iCount].cValues, PR_FREEBUSY_ALL_EVENTS); if Assigned(PropEvent) Then MinEvents := PropEvent.Value.MVbin.cValues; for jCount := 0 to Min(CountMonths, MinEvents) - 1 do begin StartDateInt := PIntegerArray(PropMonth.Value.MVl.lpl)[jCount]; StartDate := DateTimeToLocalDateTime (EncodeDate(StartDateInt shr 4, StartDateInt and $F, 1)); _TSBinary := PSBinArray(PropEvent.Value.MVbin.lpbin)[jCount]; DateArrayLen := Length(FreeBussyInfo.BusyInfo); SetLength(FreeBussyInfo.BusyInfo, DateArrayLen + Integer(_TSBinary.cb div 4)); for yCount := 0 to (_TSBinary.cb div 4) - 1 do begin dwEnd := PDWORDArray(_TSBinary.lpb)[yCount]; FreeBussyInfo.BusyInfo[DateArrayLen + yCount].StartTime := MAPIDateUtils.IncMinute(StartDate, LoWord(dwEnd)); FreeBussyInfo.BusyInfo[DateArrayLen + yCount].EndTime := MAPIDateUtils.IncMinute(StartDate, HiWord(dwEnd)); end; end; PropEvent := PpropFindProp(RowSet.aRow[iCount].lpProps, RowSet.aRow[iCount].cValues, PR_FREEBUSY_LAST_MODIFIED); if Assigned(PropEvent) Then FreeBussyInfo.LastModified := DateTimeFromFileTime(PropEvent.Value.ft); List.AddObject(FreeBussyInfo.EMailAddress, TObject(FreeBussyInfo)); End; If Assigned(RowSet) Then FreePRows(RowSet); RowSet := nil; End; End; end; finally FreeBusyFolder := nil; PublicStore := nil; If Assigned(RowSet) Then FreePRows(RowSet); if Assigned(Prop) then MAPIFreeBuffer(Prop); end; end;