最近更新: 2009-04-15

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開發工具補遺

初始化

  1. 配置記憶體資源。透過 talloc library 管理記憶體。
  2. 配置會期資源,並開啟指定的帳號簡介資料檔。
  3. 使用者登入。
  4. 建構預設的回傳訊息封包。
    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 則是一個集大成的範例,極具實用性。

樂多舊網址: http://blog.roodo.com/rocksaying/archives/8722399.html