Openchange/libmapi API 用例
在 libmapi 中,主要的封包結構是 mapi_object_t
。使用 mapi_object_init()
建構內容。
主要的函數回傳值是 enum MAPISTATUS
,並以 MAPI_E_SUCCESS
表示成功執行。
通常在呼叫 libmapi API 後使用 if (retval != MAPI_E_SUCCESS) return false;
判斷程式流程。
就設計架構而言, libmapi 運作時會管理記憶體資源。使用 MAPIFreeBuffer(), mapi_object_release()
便可釋放閒置的記憶體空間。
但就我實測結果顯示,它目前存有 memory lack 問題。以擷取連絡人清單為例,在擁有 256MB 實際記憶體與 384MB Swap 空間的 GNU/Linux 系統上,
大約在擷取200筆訊息後,就會因為記憶體不足而被系統中止程序。
openchangeclient -p rock --fetch-items=contact
這是目前使用 libmapi 開發大型客戶軟體時必須注意之處。其他Bug可以參考 Openchange開發工具補遺。
初始化
- 配置記憶體資源。透過 talloc library 管理記憶體。
- 配置會期資源,並開啟指定的帳號簡介資料檔。
- 使用者登入。
- 建構預設的回傳訊息封包。
TALLOC_CTX *mem_ctx;
enum MAPISTATUS retval; // MAPI回傳值。
struct mapi_session *session = NULL;
mapi_object_t obj_store; // 預設回傳訊息封包儲存區。
mem_ctx = talloc_named(NULL, 0, "Name of this memory resource");
// 狀態初始化,指定 profile 資料檔。
// DEFAULT_PROFDB = "profiles.ldb"
retval = MAPIInitialize(DEFAULT_PROFDB);
IS_RETVAL_ERROR("MAPIInitialize", retval);
// 使用者登入,指定 profile 識別名。
// DEFAULT_PROFILE = "Administrator"
retval = MapiLogonEx(&session, DEFAULT_PROFILE, NULL);
IS_RETVAL_ERROR("MapiLogonEx", retval);
// 呼叫 mapi_object_init 建構 mapi_object_t 個體。
mapi_object_init(&obj_store);
// 建構預設回傳訊息封包。
OpenMsgStore(&obj_store);
/*
Some thing you want to do.
*/
// 呼叫 mapi_object_init 釋放 mapi_object_t 個體。
mapi_object_release(&obj_stroe);
MAPIUninitialize();
talloc_free(mem_ctx);
開啟信箱資料夾或指定功能的預設資料夾: Mailbox
enum MAPISTATUS retval;
uint32_t type_of_folder; //指定要抓取的資料夾型態
mapi_id_t folder_id; //儲存取得的資料夾id (數值型態)
mapi_object_t obj_store; //儲存取得的訊息封包
mapi_object_init(&obj_store);
type_of_folder = olFolderTopInformationStore; //Root of mailbox
retval = GetDefaultFolder(&obj_store, &folder_id, type_of_folder);
if (retval != MAPI_E_SUCCESS) return false;
mapi_object_t obj_folder; //儲存取得的資料夾封包
mapi_object_init(&obj_folder);
retval = OpenFolder(&obj_store, folder_id, &obj_folder);
// obj_store 是 父資料夾的封包 (parent folder)
if (retval != MAPI_E_SUCCESS) return false;
/*
The following types of folders are supported:
* olFolderTopInformationStore
* olFolderDeletedItems
* olFolderOutbox
* olFolderSentMail
* olFolderInbox
* olFolderCommonView
* olFolderCalendar
* olFolderContacts
* olFolderJournal
* olFolderNotes
* olFolderTasks
* olFolderDrafts
* olFolderReminders
* olFolderFinder
*/
取得訊息 (Message) 的屬性內容
在 Exchange 中,包含電子郵件、行事曆、連絡人等等各類文件,都被視為「訊息(message)」,訊息的內容則歸屬於「訊息屬性(message property)」。
故要取得訊息內容時,就要以 GetProps(), GetPropsAll()
等取得指定的屬性清單。再以 get_SPropValue()
自屬性清單中取得指定的屬性值。
enum MAPISTATUS retval;
struct SPropTagArray *SPropTagArray;
struct SPropValue *lpProps;
uint32_t count;
char *name;
uint64_t *fid;
SPropTagArray = set_SPropTagArray(mem_ctx,
0x2, // 要讀取的屬性數 (依後面接著的參數數量而定)
PR_DISPLAY_NAME,
PR_FID
);
retval = GetProps(obj_folder, SPropTagArray, &lpProps, &count);
MAPIFreeBuffer(SPropTagArray);
if (retval != MAPI_E_SUCCESS) return;
fid = (uint64_t *)get_SPropValue(lpProps, PR_FID);
name = CONVERT((char *)get_SPropValue(lpProps, PR_DISPLAY_NAME));
MAPIFreeBuffer(lpProps);
取得資料夾或階層式個體的屬性內容
以 QueryRows() 或 SeekRow()
讀取項目的屬性內容。以 find_SPropValue_data()
讀取 SRow_t aRow 結構的內容。
mapi_object_t obj_htable;
mapi_object_t obj_table;
struct SPropTagArray *SPropTagArray;
struct SRowSet rowset;
uint32_t start_position, end_position, current_position, seekRowResult;
mapi_object_init(&obj_htable);
retval = GetHierarchyTable(obj_folder, &obj_htable, 0, NULL);
if (retval != MAPI_E_SUCCESS) return false;
/**
* 設定要取得的屬性。
* 以 SQL 的概念說明的話,這是在設定 SELECT 的欄位。
* 此處的設定會影嚮接下來的 QueryRows() 與 SeekRow() 的結果。
*/
SPropTagArray = set_SPropTagArray(mem_ctx, 0x6,
PR_DISPLAY_NAME,
PR_FID,
PR_COMMENT,
PR_CONTENT_UNREAD,
PR_CONTENT_COUNT,
PR_FOLDER_CHILD_COUNT);
retval = SetColumns(&obj_htable, SPropTagArray);
MAPIFreeBuffer(SPropTagArray);
if (retval != MAPI_E_SUCCESS) return false;
while (((retval = QueryRows(&obj_htable, 0x32, TBL_ADVANCE, &rowset))
!= MAPI_E_NOT_FOUND) && rowset.cRows)
{
for (index = 0; index < rowset.cRows; index++) {
fid = (const uint64_t *)find_SPropValue_data(&rowset.aRow[index], PR_FID);
name = (const char *)find_SPropValue_data(&rowset.aRow[index], PR_DISPLAY_NAME);
}
}
mapi_object_init(&obj_table);
for (current_position = start_position - 1;
current_position < end_position;
++current_position)
{
SeekRow(&obj_table, BOOKMARK_BEGINNING, current_position, &seekRowResult);
//SeekRow(&obj_table, BOOKMARK_CURRENT, 0, &seekRowResult);
retval = QueryRows(&obj_table, 1, TBL_ADVANCE, &rowset);
if (retval == MAPI_E_NOT_FOUND || rowset.cRows == 0)
break;
}
輸出屬性值
可參考 libmapi/mapidump.c 的程式碼。
/**
* 輸出資料型態為字串的屬性。
*/
void put_str(struct mapi_SPropValue_array *properties,
uint32_t mapitag,
const char *label,
const char *pad)
{
const uchar_t *p;
p = (const uchar_t *)find_mapi_SPropValue_data(properties, mapitag);
if ( !p )
return;
printf("%s%s: %s\n", pad, label, p);
}
/**
* 輸出資料型態為日期的屬性。
*/
void put_datetime(TALLOC_CTX *mem_ctx,
struct mapi_SPropValue_array *properties,
uint32_t mapitag,
const char*label,
const char*pad)
{
NTTIME time;
const struct FILETIME *filetime;
char *date;
filetime = (const struct FILETIME *) find_mapi_SPropValue_data(properties, mapitag);
if (filetime) {
time = filetime->dwHighDateTime;
time = time << 32;
time |= filetime->dwLowDateTime;
date = (char*)nt_time_string(mem_ctx, time);
printf("%s%s: %s\n", pad, label, date);
talloc_free(date);
}
return;
}
/**
* 輸出資料型態為無號整數的屬性。
*/
void put_uint32(struct mapi_SPropValue_array *properties,
uint32_t mapitag,
const char *label,
const char *pad)
{
const uint32_t *p;
p = (const uint32_t *)find_mapi_SPropValue_data(properties, mapitag);
if ( !p )
return;
printf("%s%s: %u\n", pad, label, *p);
}
/**
* 輸出資料型態為實數的屬性。
*/
void put_double(struct mapi_SPropValue_array *properties,
uint32_t mapitag,
const char *label,
const char *pad)
{
const double *p;
p = (const double *)find_mapi_SPropValue_data(properties, mapitag);
if ( !p )
return;
printf("%s%s: %f\n", pad, label, *p);
}
/**
* 以十六進位格式輸出資料型態為長整數的屬性。
*/
void put_longhex(struct mapi_SPropValue_array *properties,
uint32_t mapitag,
const char *label,
const char *pad)
{
const uint64_t *p;
p = (const uint64_t *)find_mapi_SPropValue_data(properties, mapitag);
if ( !p )
return;
printf("%s%s: %"PRIx64"\n", pad, label, *p);
}
Openchange官方範例: examples@Openchange. 而 libmapi/openchangeclient.c 則是一個集大成的範例,極具實用性。