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;

