Implements contact details edition and notification for outlook.

cusax-fix
Vincent Lucas 13 years ago
parent c85ca6ca0c
commit 528bdb40d5

@ -0,0 +1,173 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
#include "MAPINotification.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdio.h>
#include <Unknwn.h>
#include <mapidefs.h>
#include <mapiutil.h>
#include "../net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactSourceService.h"
#include "../net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.h"
/**
* Manages notification for the message data base (used to get the list of
* contact).
*
* @author Vincent Lucas
*/
/**
* The List of events we want to retrieve.
*/
static ULONG EVENT_MASK
= fnevObjectCreated
| fnevObjectDeleted
| fnevObjectModified
| fnevObjectMoved;
/**
* Functions called when an event is fired from the message data base.
*
* @param lpvContext A pointer to the message data base.
* @param cNotifications The number of event in this call.
* @param lpNotifications The list of notifications.
*/
LONG STDAPICALLTYPE onNotify(
LPVOID lpvContext,
ULONG cNotifications,
LPNOTIFICATION lpNotifications)
{
for(unsigned int i = 0; i < cNotifications; ++i)
{
LPUNKNOWN iUnknown = NULL;
if(lpvContext != NULL)
{
iUnknown = openEntry(
lpNotifications[i].info.obj.cbEntryID,
lpNotifications[i].info.obj.lpEntryID,
lpvContext);
}
// A contact has been created
if(lpNotifications[i].ulEventType == fnevObjectCreated)
{
if(lpNotifications[i].info.obj.ulObjType == MAPI_MESSAGE)
{
callInsertedCallbackMethod(iUnknown);
}
}
// A contact has been Modified
else if(lpNotifications[i].ulEventType == fnevObjectModified)
{
if(lpNotifications[i].info.obj.ulObjType == MAPI_MESSAGE)
{
callUpdatedCallbackMethod(iUnknown);
}
}
// A contact has been deleted.
else if(lpNotifications[i].ulEventType == fnevObjectDeleted)
{
if(lpvContext != NULL)
{
char entryIdStr[lpNotifications[i].info.obj.cbEntryID * 2 + 1];
HexFromBin(
(LPBYTE) lpNotifications[i].info.obj.lpEntryID,
lpNotifications[i].info.obj.cbEntryID,
entryIdStr);
if(lpNotifications[i].info.obj.ulObjType == MAPI_MESSAGE)
{
callDeletedCallbackMethod(entryIdStr);
}
}
}
// A contact has been deleted (moved to trash).
else if(lpNotifications[i].ulEventType == fnevObjectMoved)
{
if(lpvContext != NULL)
{
char entryIdStr[lpNotifications[i].info.obj.cbEntryID * 2 + 1];
HexFromBin(
(LPBYTE) lpNotifications[i].info.obj.lpEntryID,
lpNotifications[i].info.obj.cbEntryID,
entryIdStr);
char parentEntryIdStr[
lpNotifications[i].info.obj.cbParentID * 2 + 1];
HexFromBin(
(LPBYTE) lpNotifications[i].info.obj.lpParentID,
lpNotifications[i].info.obj.cbParentID,
parentEntryIdStr);
ULONG wasteBasketTags[] = {1, PR_IPM_WASTEBASKET_ENTRYID};
ULONG wasteBasketNbValues = 0;
LPSPropValue wasteBasketProps = NULL;
((LPMDB)lpvContext)->GetProps(
(LPSPropTagArray) wasteBasketTags,
MAPI_UNICODE,
&wasteBasketNbValues,
&wasteBasketProps);
char wasteBasketEntryIdStr[
wasteBasketProps[0].Value.bin.cb * 2 + 1];
HexFromBin(
(LPBYTE) wasteBasketProps[0].Value.bin.lpb,
wasteBasketProps[0].Value.bin.cb,
wasteBasketEntryIdStr);
openEntry(
lpNotifications[i].info.obj.cbParentID,
lpNotifications[i].info.obj.lpParentID,
lpvContext);
if(lpNotifications[i].info.obj.ulObjType == MAPI_MESSAGE
&& strcmp(parentEntryIdStr, wasteBasketEntryIdStr) == 0)
{
callDeletedCallbackMethod(entryIdStr);
}
}
}
if(iUnknown != NULL)
{
iUnknown->Release();
}
}
// A client must always return a S_OK.
return S_OK;
}
/**
* Registers to notification for the given message data base.
*
* @param iUnknown The data base to register to in order to receive events.
*
* @return A unsigned long which is a token wich must be used to call the
* unadvise function for the same message data base.
*/
ULONG registerNotifyMessageDataBase(
LPMDB iUnknown)
{
LPMAPIADVISESINK adviseSink;
HrAllocAdviseSink(
&onNotify,
iUnknown,
&adviseSink);
ULONG nbConnection = 0;
iUnknown->Advise(
0,
NULL,
EVENT_MASK,
adviseSink,
&nbConnection);
return nbConnection;
}

@ -0,0 +1,30 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
#ifndef _mapi_notification_h
#define _mapi_notification_h
#ifdef __cplusplus
extern "C" {
#endif
#include <mapidefs.h>
#include <mapix.h>
LONG STDAPICALLTYPE onNotify(
LPVOID lpvContext,
ULONG cNotifications,
LPNOTIFICATION lpNotifications);
ULONG registerNotifyMessageDataBase(
LPMDB iUnknown);
#ifdef __cplusplus
}
#endif
#endif

@ -7,15 +7,31 @@
#include "net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.h"
#include "AddrBookContactQuery.h"
#include "net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactSourceService.h"
#include "../AddrBookContactQuery.h"
#include "MsOutlookMAPI.h"
#include "MsOutlookMAPIHResultException.h"
#include "lib/MAPINotification.h"
#include <stdio.h>
#include <string.h>
#include <mapiutil.h>
#define PR_ATTACHMENT_CONTACTPHOTO PROP_TAG(PT_BOOLEAN, 0x7FFF)
static ULONG openEntryUlFlags = MAPI_BEST_ACCESS;
LPUNKNOWN openEntryId(const char* entryId);
ULONG registerNotifyTable(
LPMAPITABLE iUnknown);
LONG STDAPICALLTYPE tableChanged(
LPVOID lpvContext,
ULONG cNotifications,
LPNOTIFICATION lpNotifications);
typedef
jboolean (*MsOutlookAddrBookContactQuery_ForeachRowInTableCallback)
(LPUNKNOWN iUnknown,
@ -24,7 +40,7 @@ typedef
jstring query,
jobject callback, jmethodID callbackMethodID);
static HRESULT HrGetOneProp
static HRESULT MsOutlookAddrBookContactQuery_HrGetOneProp
(LPMAPIPROP mapiProp, ULONG propTag, LPSPropValue *prop);
static jboolean MsOutlookAddrBookContactQuery_foreachContactInMsgStoresTable
@ -76,26 +92,23 @@ static jbyteArray MsOutlookAddrBookContactQuery_readAttachment
(LPMESSAGE message, LONG method, ULONG num, JNIEnv *jniEnv, ULONG cond);
JNIEXPORT void JNICALL
Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_foreachMailUser
(JNIEnv *jniEnv, jclass clazz, jstring query, jobject callback)
Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_foreachMailUser(
JNIEnv *jniEnv,
jclass clazz,
jstring query,
jobject callback)
{
jmethodID callbackMethodID;
HRESULT hResult;
LPMAPISESSION mapiSession = NULL;
LPMAPISESSION mapiSession
= MsOutlookAddrBookContactSourceService_getMapiSession();
callbackMethodID
= AddrBookContactQuery_getPtrCallbackMethodID(jniEnv, callback);
if (!callbackMethodID || jniEnv->ExceptionCheck())
return;
hResult
= MAPILogonEx(
0,
NULL, NULL,
MAPI_EXTENDED | MAPI_NO_MAIL | MAPI_USE_DEFAULT,
&mapiSession);
if (HR_SUCCEEDED(hResult) && mapiSession)
if (mapiSession)
{
jboolean proceed
= MsOutlookAddrBookContactQuery_foreachContactInMsgStoresTable(
@ -113,28 +126,16 @@ Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContac
callback, callbackMethodID);
}
/*
* XXX MAPILogonEx has been redefined to return a shared mapiSession
* which is logged off and released upon uninitializing MAPI. The reason
* for the redefinition is that logging on, off and releasing multiple
* times leads to a crash eventually.
*/
// mapiSession->Logoff(0, 0, 0);
// mapiSession->Release();
}
else
{
MsOutlookMAPIHResultException_throwNew(
jniEnv,
hResult,
__FILE__, __LINE__);
}
}
JNIEXPORT jobjectArray JNICALL
Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_IMAPIProp_1GetProps
(JNIEnv *jniEnv, jclass clazz,
jlong mapiProp, jlongArray propIds, jlong flags)
Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_IMAPIProp_1GetProps(
JNIEnv *jniEnv,
jclass clazz,
jlong mapiProp,
jlongArray propIds,
jlong flags)
{
jsize propIdCount;
LPSPropTagArray propTagArray;
@ -309,6 +310,28 @@ Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContac
}
break;
}
case PT_BINARY:
{
char entryIdStr[prop->Value.bin.cb * 2 + 1];
HexFromBin(
prop->Value.bin.lpb,
prop->Value.bin.cb,
entryIdStr);
jstring value;
value = jniEnv->NewStringUTF(entryIdStr);
if(value)
{
jniEnv->SetObjectArrayElement(
props,
j, value);
if (jniEnv->ExceptionCheck())
props = NULL;
}
break;
}
}
}
propArray++;
@ -336,7 +359,10 @@ Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContac
}
static HRESULT
HrGetOneProp(LPMAPIPROP mapiProp, ULONG propTag, LPSPropValue *prop)
MsOutlookAddrBookContactQuery_HrGetOneProp(
LPMAPIPROP mapiProp,
ULONG propTag,
LPSPropValue *prop)
{
SPropTagArray propTagArray;
HRESULT hResult;
@ -490,7 +516,14 @@ MsOutlookAddrBookContactQuery_foreachMailUserInAddressBook
ULONG objType;
LPUNKNOWN iUnknown;
hResult = adrBook->OpenEntry(0, NULL, NULL, 0, &objType, &iUnknown);
hResult = adrBook->OpenEntry(
0,
NULL,
NULL,
openEntryUlFlags,
&objType,
&iUnknown);
if (HR_SUCCEEDED(hResult))
{
proceed
@ -754,18 +787,20 @@ MsOutlookAddrBookContactQuery_getContactsFolderEntryID
ULONG objType;
LPUNKNOWN folder;
hResult
= msgStore->OpenEntry(
folderEntryIDByteCount, folderEntryID,
NULL,
0,
&objType, &folder);
hResult = msgStore->OpenEntry(
folderEntryIDByteCount,
folderEntryID,
NULL,
openEntryUlFlags,
&objType,
&folder);
if (HR_SUCCEEDED(hResult))
{
LPSPropValue prop;
hResult
= HrGetOneProp(
= MsOutlookAddrBookContactQuery_HrGetOneProp(
(LPMAPIPROP) folder,
0x36D10102 /* PR_IPM_CONTACT_ENTRYID */,
&prop);
@ -845,7 +880,7 @@ MsOutlookAddrBookContactQuery_onForeachContactInMsgStoresTableRow
0,
entryIDByteCount, entryID,
NULL,
MDB_NO_MAIL,
MDB_NO_MAIL | openEntryUlFlags,
&msgStore);
if (HR_SUCCEEDED(hResult))
{
@ -883,10 +918,10 @@ MsOutlookAddrBookContactQuery_onForeachContactInMsgStoresTableRow
hResult
= msgStore->OpenEntry(
contactsFolderEntryIDByteCount, contactsFolderEntryID,
NULL,
0,
&contactsFolderObjType, &contactsFolder);
contactsFolderEntryIDByteCount, contactsFolderEntryID,
NULL,
openEntryUlFlags,
&contactsFolderObjType, &contactsFolder);
if (HR_SUCCEEDED(hResult))
{
proceed
@ -932,11 +967,12 @@ MsOutlookAddrBookContactQuery_onForeachMailUserInContainerTableRow
LPUNKNOWN iUnknown;
jboolean proceed;
// Make write failed and image load.
hResult
= ((LPMAPICONTAINER) mapiContainer)->OpenEntry(
entryIDByteCount, entryID,
NULL,
0,
openEntryUlFlags,
&objType, &iUnknown);
if (HR_SUCCEEDED(hResult))
{
@ -974,7 +1010,10 @@ MsOutlookAddrBookContactQuery_readAttachment
{
LPSPropValue condValue;
hResult = HrGetOneProp((LPMAPIPROP) attach, cond, &condValue);
hResult = MsOutlookAddrBookContactQuery_HrGetOneProp(
(LPMAPIPROP) attach,
cond,
&condValue);
if (HR_SUCCEEDED(hResult))
{
if ((PT_BOOLEAN != PROP_TYPE(condValue->ulPropTag))
@ -1035,3 +1074,489 @@ MsOutlookAddrBookContactQuery_readAttachment
}
return attachment;
}
/**
* Saves one contact property.
*/
JNIEXPORT jboolean JNICALL
Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_IMAPIProp_1SetPropString
(JNIEnv *jniEnv, jclass clazz, jlong propId, jstring value,
jstring entryId)
{
HRESULT hResult;
const char *nativeEntryId = jniEnv->GetStringUTFChars(entryId, NULL);
LPUNKNOWN mapiProp;
if((mapiProp = openEntryId(nativeEntryId)) == NULL)
{
return JNI_FALSE;
}
jniEnv->ReleaseStringUTFChars(entryId, nativeEntryId);
const char *nativeValue = jniEnv->GetStringUTFChars(value, NULL);
size_t valueLength = strlen(nativeValue);
wchar_t wCharValue[valueLength + 1];
if(mbstowcs(wCharValue, nativeValue, valueLength + 1)
!= valueLength)
{
fprintf(stderr,
"setPropUnicode (addrbook/MsOutlookAddrBookContactQuery.c): \
\n\tmbstowcs\n");
fflush(stderr);
jniEnv->ReleaseStringUTFChars(value, nativeValue);
return JNI_FALSE;
}
jniEnv->ReleaseStringUTFChars(value, nativeValue);
ULONG baseGroupEntryIdProp = 0;
switch(propId)
{
case 0x00008083: // dispidEmail1EmailAddress
baseGroupEntryIdProp = 0x00008080;
break;
case 0x00008093: // dispidEmail2EmailAddress
baseGroupEntryIdProp = 0x00008090;
break;
case 0x000080A3: // dispidEmail3EmailAddress
baseGroupEntryIdProp = 0x000080A0;
break;
}
// If this is a special entry (for email only), then updates all the
// corresponding properties to make it work.
if(baseGroupEntryIdProp != 0)
{
ULONG nbProps = 7;
ULONG propIds[] =
{
0x8028, // PidLidAddressBookProviderEmailList
0x8029, // PidLidAddressBookProviderArrayType
(baseGroupEntryIdProp + 0), //0x8080 PidLidEmail1DisplayName
(baseGroupEntryIdProp + 2), // 0x8082 PidLidEmail1AddressType
(baseGroupEntryIdProp + 3), // 0x8083 PidLidEmail1EmailAddress
(baseGroupEntryIdProp + 4), // 0x8084 PidLidEmail1OriginalDisplayName
(baseGroupEntryIdProp + 5) // 0x8085 PidLidEmail1OriginalEntryID
};
ULONG propTag;
ULONG propCount;
LPSPropValue propArray;
LPSPropTagArray propTagArray;
MAPIAllocateBuffer(
CbNewSPropTagArray(nbProps),
(void **) &propTagArray);
propTagArray->cValues = nbProps;
for(unsigned int i = 0; i < nbProps; ++i)
{
propTag = MsOutlookAddrBookContactQuery_getPropTagFromLid(
(LPMAPIPROP) mapiProp,
propIds[i]);
*(propTagArray->aulPropTag + i) = propTag;
}
hResult = ((LPMAPIPROP) mapiProp)->GetProps(
propTagArray,
MAPI_UNICODE,
&propCount,
&propArray);
if(SUCCEEDED(hResult))
{
propArray[2].Value.lpszW = wCharValue;
propArray[4].Value.lpszW = wCharValue;
propArray[5].Value.lpszW = wCharValue;
if(SUCCEEDED(hResult))
{
hResult = ((LPMAPIPROP) mapiProp)->SetProps(
nbProps,
propArray,
NULL);
if(SUCCEEDED(hResult))
{
hResult = ((LPMAPIPROP) mapiProp)->SaveChanges(
FORCE_SAVE | KEEP_OPEN_READWRITE);
if (HR_SUCCEEDED(hResult))
{
MAPIFreeBuffer(propTagArray);
((LPMAPIPROP) mapiProp)->Release();
return JNI_TRUE;
}
}
}
}
MAPIFreeBuffer(propTagArray);
((LPMAPIPROP) mapiProp)->Release();
return JNI_FALSE;
}
SPropValue updateValue;
updateValue.ulPropTag = PROP_TAG(PT_UNICODE, propId);
updateValue.Value.lpszW = wCharValue;
hResult = ((LPMAPIPROP) mapiProp)->SetProps(
1,
(LPSPropValue) &updateValue,
NULL);
if (HR_SUCCEEDED(hResult))
{
HRESULT hResult
= ((LPMAPIPROP) mapiProp)->SaveChanges(
FORCE_SAVE | KEEP_OPEN_READWRITE);
if (HR_SUCCEEDED(hResult))
{
((LPMAPIPROP) mapiProp)->Release();
return JNI_TRUE;
}
}
((LPMAPIPROP) mapiProp)->Release();
return JNI_FALSE;
}
/**
* Deletes one property from a contact.
*/
JNIEXPORT jboolean JNICALL
Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_IMAPIProp_1DeleteProp
(JNIEnv *jniEnv, jclass clazz, jlong propId, jstring entryId)
{
const char *nativeEntryId = jniEnv->GetStringUTFChars(entryId, NULL);
LPUNKNOWN mapiProp;
if((mapiProp = openEntryId(nativeEntryId)) == NULL)
{
return JNI_FALSE;
}
jniEnv->ReleaseStringUTFChars(entryId, nativeEntryId);
ULONG baseGroupEntryIdProp = 0;
switch(propId)
{
case 0x00008083: // dispidEmail1EmailAddress
baseGroupEntryIdProp = 0x00008080;
break;
case 0x00008093: // dispidEmail2EmailAddress
baseGroupEntryIdProp = 0x00008090;
break;
case 0x000080A3: // dispidEmail3EmailAddress
baseGroupEntryIdProp = 0x000080A0;
break;
}
// If this is a special entry (for email only), then deletes all the
// corresponding properties to make it work.
if(baseGroupEntryIdProp != 0)
{
ULONG nbProps = 5;
ULONG propIds[] =
{
(baseGroupEntryIdProp + 0), //0x8080 PidLidEmail1DisplayName
(baseGroupEntryIdProp + 2), // 0x8082 PidLidEmail1AddressType
(baseGroupEntryIdProp + 3), // 0x8083 PidLidEmail1EmailAddress
(baseGroupEntryIdProp + 4), // 0x8084 PidLidEmail1OriginalDisplayName
(baseGroupEntryIdProp + 5) // 0x8085 PidLidEmail1OriginalEntryID
};
ULONG propTag;
ULONG propCount;
LPSPropValue propArray;
LPSPropTagArray propTagArray;
MAPIAllocateBuffer(
CbNewSPropTagArray(nbProps),
(void **) &propTagArray);
propTagArray->cValues = nbProps;
for(unsigned int i = 0; i < nbProps; ++i)
{
propTag = MsOutlookAddrBookContactQuery_getPropTagFromLid(
(LPMAPIPROP) mapiProp,
propIds[i]);
*(propTagArray->aulPropTag + i) = propTag;
}
HRESULT hResult
= ((LPMAPIPROP) mapiProp)->DeleteProps(
propTagArray,
NULL);
if (HR_SUCCEEDED(hResult))
{
hResult
= ((LPMAPIPROP) mapiProp)->SaveChanges(
FORCE_SAVE | KEEP_OPEN_READWRITE);
if (HR_SUCCEEDED(hResult))
{
MAPIFreeBuffer(propTagArray);
((LPMAPIPROP) mapiProp)->Release();
return JNI_TRUE;
}
}
MAPIFreeBuffer(propTagArray);
((LPMAPIPROP) mapiProp)->Release();
return JNI_FALSE;
}
SPropTagArray propToDelete;
propToDelete.cValues = 1;
propToDelete.aulPropTag[0] = PROP_TAG(PT_UNICODE, propId);
HRESULT hResult
= ((LPMAPIPROP) mapiProp)->DeleteProps(
(LPSPropTagArray) &propToDelete,
NULL);
if (HR_SUCCEEDED(hResult))
{
hResult
= ((LPMAPIPROP) mapiProp)->SaveChanges(
FORCE_SAVE | KEEP_OPEN_READWRITE);
if (HR_SUCCEEDED(hResult))
{
((LPMAPIPROP) mapiProp)->Release();
return JNI_TRUE;
}
}
((LPMAPIPROP) mapiProp)->Release();
return JNI_FALSE;
}
/**
* Opens an object from its entry id.
*/
LPUNKNOWN openEntry(
ULONG cbEntryID,
LPENTRYID lpEntryID,
LPVOID lpvContext)
{
if(lpvContext != NULL)
{
LPUNKNOWN iUnknown;
ULONG objType;
HRESULT hResult =
((LPMDB) lpvContext)->OpenEntry(
cbEntryID,
lpEntryID,
NULL,
openEntryUlFlags,
&objType,
&iUnknown);
if (HR_SUCCEEDED(hResult))
{
return iUnknown;
}
}
return NULL;
}
static LPMAPITABLE MsOutlookAddrBookContactQuery_msgStoresTable = NULL;
static ULONG MsOutlookAddrBookContactQuery_msgStoresTableConnection = 0;
static ULONG MsOutlookAddrBookContactQuery_nbMsgStores = 0;
static LPMDB * MsOutlookAddrBookContactQuery_msgStores = NULL;
static ULONG * MsOutlookAddrBookContactQuery_msgStoresConnection = NULL;
/**
* Opens all the message store and register to notifications.
*/
void openAllMsgStores(
LPMAPISESSION mapiSession)
{
HRESULT hResult;
hResult = mapiSession->GetMsgStoresTable(
0,
&MsOutlookAddrBookContactQuery_msgStoresTable);
if(HR_SUCCEEDED(hResult) && MsOutlookAddrBookContactQuery_msgStoresTable)
{
MsOutlookAddrBookContactQuery_msgStoresTableConnection
= registerNotifyTable(MsOutlookAddrBookContactQuery_msgStoresTable);
hResult = MsOutlookAddrBookContactQuery_msgStoresTable->SeekRow(
BOOKMARK_BEGINNING,
0,
NULL);
if (HR_SUCCEEDED(hResult))
{
LPSRowSet rows;
hResult = HrQueryAllRows(
MsOutlookAddrBookContactQuery_msgStoresTable,
NULL,
NULL,
NULL,
0,
&rows);
if (HR_SUCCEEDED(hResult))
{
MsOutlookAddrBookContactQuery_nbMsgStores = rows->cRows;
MsOutlookAddrBookContactQuery_msgStores
= (LPMDB*) malloc(rows->cRows * sizeof(LPMDB));
memset(
MsOutlookAddrBookContactQuery_msgStores,
0,
rows->cRows * sizeof(LPMDB));
MsOutlookAddrBookContactQuery_msgStoresConnection
= (ULONG*) malloc(rows->cRows * sizeof(ULONG));
memset(
MsOutlookAddrBookContactQuery_msgStoresConnection,
0,
rows->cRows * sizeof(ULONG));
if(MsOutlookAddrBookContactQuery_msgStores != NULL
&& MsOutlookAddrBookContactQuery_msgStoresConnection
!= NULL)
{
for(unsigned int r = 0; r < rows->cRows; ++r)
{
SRow row = rows->aRow[r];
ULONG i;
ULONG objType = 0;
SBinary entryIDBinary = { 0, NULL };
for(i = 0; i < row.cValues; ++i)
{
LPSPropValue prop = (row.lpProps) + i;
switch (prop->ulPropTag)
{
case PR_OBJECT_TYPE:
objType = prop->Value.ul;
break;
case PR_ENTRYID:
entryIDBinary = prop->Value.bin;
break;
}
}
if(objType && entryIDBinary.cb && entryIDBinary.lpb)
{
hResult = mapiSession->OpenMsgStore(
0,
entryIDBinary.cb,
(LPENTRYID) entryIDBinary.lpb,
NULL,
MDB_NO_MAIL | openEntryUlFlags,
&MsOutlookAddrBookContactQuery_msgStores[r]
);
if (HR_SUCCEEDED(hResult))
{
MsOutlookAddrBookContactQuery_msgStoresConnection[r]
= registerNotifyMessageDataBase(
MsOutlookAddrBookContactQuery_msgStores[r]);
}
}
}
}
FreeProws(rows);
}
}
}
}
/**
* Frees all memory used to keep in mind the list of the message store and
* unregister each of them from the notifications.
*/
void freeAllMsgStores(void)
{
if(MsOutlookAddrBookContactQuery_msgStoresConnection != NULL)
{
for(unsigned int i = 0;
i < MsOutlookAddrBookContactQuery_nbMsgStores;
++i)
{
if(MsOutlookAddrBookContactQuery_msgStoresConnection[i] != 0)
{
MsOutlookAddrBookContactQuery_msgStores[i]->Unadvise(
MsOutlookAddrBookContactQuery_msgStoresConnection[i]);
}
}
free(MsOutlookAddrBookContactQuery_msgStoresConnection);
}
if(MsOutlookAddrBookContactQuery_msgStores != NULL)
{
for(unsigned int i = 0;
i < MsOutlookAddrBookContactQuery_nbMsgStores;
++i)
{
if(MsOutlookAddrBookContactQuery_msgStores[i] != NULL)
{
MsOutlookAddrBookContactQuery_msgStores[i]->Release();
}
}
free(MsOutlookAddrBookContactQuery_msgStores);
}
if(MsOutlookAddrBookContactQuery_msgStoresTable != NULL)
{
MsOutlookAddrBookContactQuery_msgStoresTable->Unadvise(
MsOutlookAddrBookContactQuery_msgStoresTableConnection);
MsOutlookAddrBookContactQuery_msgStoresTable->Release();
}
}
/**
* Opens an object based on the string representation of its entry id.
*/
LPUNKNOWN openEntryId(const char* entryId)
{
ULONG tmpEntryIdSize = strlen(entryId) / 2;
LPENTRYID tmpEntryId = (LPENTRYID) malloc(tmpEntryIdSize * sizeof(char));
if(FBinFromHex((LPSTR) entryId, (LPBYTE) tmpEntryId))
{
LPMAPISESSION mapiSession
= MsOutlookAddrBookContactSourceService_getMapiSession();
ULONG objType;
LPUNKNOWN iUnknown;
HRESULT hResult = mapiSession->OpenEntry(
tmpEntryIdSize,
tmpEntryId,
NULL,
MAPI_BEST_ACCESS,
&objType,
&iUnknown);
if(hResult == S_OK)
{
free(tmpEntryId);
return iUnknown;
}
}
free(tmpEntryId);
return NULL;
}
/**
* Registers a callback function for when the message store table changes.
*/
ULONG registerNotifyTable(
LPMAPITABLE iUnknown)
{
LPMAPIADVISESINK adviseSink;
HrAllocAdviseSink(
&tableChanged,
iUnknown,
&adviseSink);
ULONG nbConnection = 0;
iUnknown->Advise(
fnevTableModified,
adviseSink,
&nbConnection);
return nbConnection;
}
/**
* Function called when a message store table changed.
*/
LONG STDAPICALLTYPE tableChanged(
LPVOID lpvContext,
ULONG cNotifications,
LPNOTIFICATION lpNotifications)
{
if(lpNotifications->ulEventType == fnevTableModified
&& (lpNotifications->info.tab.ulTableEvent == TABLE_CHANGED
|| lpNotifications->info.tab.ulTableEvent == TABLE_ERROR
|| lpNotifications->info.tab.ulTableEvent == TABLE_RELOAD
|| lpNotifications->info.tab.ulTableEvent == TABLE_ROW_ADDED
|| lpNotifications->info.tab.ulTableEvent == TABLE_ROW_DELETED))
{
// Frees and recreates all the notification for the table.
freeAllMsgStores();
openAllMsgStores(MsOutlookAddrBookContactSourceService_getMapiSession());
}
}

@ -7,6 +7,11 @@
#ifdef __cplusplus
extern "C" {
#endif
#include <mapidefs.h>
#include <mapix.h>
#include <Unknwn.h>
/*
* Class: net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery
* Method: foreachMailUser
@ -23,6 +28,22 @@ JNIEXPORT void JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_
JNIEXPORT jobjectArray JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_IMAPIProp_1GetProps
(JNIEnv *, jclass, jlong, jlongArray, jlong);
JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_IMAPIProp_1SetPropString
(JNIEnv *, jclass, jlong, jstring, jstring);
JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_IMAPIProp_1DeleteProp
(JNIEnv *, jclass, jlong, jstring);
LPUNKNOWN openEntry(
ULONG cbEntryID,
LPENTRYID lpEntryID,
LPVOID lpvContext);
void openAllMsgStores(
LPMAPISESSION mapiSession);
void freeAllMsgStores(void);
#ifdef __cplusplus
}
#endif

@ -7,13 +7,141 @@
#include "net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactSourceService.h"
#include "net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.h"
#include "../AddrBookContactQuery.h"
#include "MsOutlookMAPI.h"
#include "MsOutlookMAPIHResultException.h"
#include "lib/MAPINotification.h"
#include <Mapiutil.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static jobject contactSourceServiceObject;
static jmethodID contactSourceServiceMethodIdInserted;
static jmethodID contactSourceServiceMethodIdUpdated;
static jmethodID contactSourceServiceMethodIdDeleted;
static JNIEnv *contactSourceServiceJniEnv;
static JavaVM *contactSourceServiceVM;
/**
* Registers java callback functions when a contact is deleted, inserted or
* updated.
*/
JNIEXPORT void JNICALL
Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactSourceService_setDelegate
(JNIEnv *jniEnv, jclass clazz, jobject callback)
{
if(jniEnv->GetJavaVM(&contactSourceServiceVM) < 0)
{
fprintf(stderr, "Failed to get the Java VM\n");
fflush(stderr);
}
contactSourceServiceJniEnv = jniEnv;
contactSourceServiceObject = jniEnv->NewGlobalRef(callback);
jclass callbackClass = jniEnv->GetObjectClass(
callback);
contactSourceServiceMethodIdInserted = jniEnv->GetMethodID(
callbackClass,
"inserted",
"(J)V");
contactSourceServiceMethodIdUpdated = jniEnv->GetMethodID(
callbackClass,
"updated",
"(J)V");
contactSourceServiceMethodIdDeleted = jniEnv->GetMethodID(
callbackClass,
"deleted",
"(Ljava/lang/String;)V");
}
/**
* Calls back the java side when a contact is inserted.
*
* @param iUnknown A pointer to the newly created contact.
*/
void callInsertedCallbackMethod(
LPUNKNOWN iUnknown)
{
JNIEnv *tmpJniEnv = contactSourceServiceJniEnv;
if(contactSourceServiceVM->GetEnv(
(void**) &tmpJniEnv,
JNI_VERSION_1_6)
!= JNI_OK)
{
contactSourceServiceVM->AttachCurrentThread((void**) &tmpJniEnv, NULL);
}
tmpJniEnv->CallBooleanMethod(
contactSourceServiceObject,
contactSourceServiceMethodIdInserted,
iUnknown);
contactSourceServiceVM->DetachCurrentThread();
}
/**
* Calls back the java side when a contact is updated.
*
* @param iUnknown A pointer to the updated contact.
*/
void callUpdatedCallbackMethod(
LPUNKNOWN iUnknown)
{
JNIEnv *tmpJniEnv = contactSourceServiceJniEnv;
if(contactSourceServiceVM->GetEnv(
(void**) &tmpJniEnv,
JNI_VERSION_1_6)
!= JNI_OK)
{
contactSourceServiceVM->AttachCurrentThread((void**) &tmpJniEnv, NULL);
}
tmpJniEnv->CallBooleanMethod(
contactSourceServiceObject,
contactSourceServiceMethodIdUpdated,
iUnknown);
contactSourceServiceVM->DetachCurrentThread();
}
/**
* Calls back the java side when a contact is deleted.
*
* @param iUnknown The string representation of the entry id of the deleted
* contact.
*/
void callDeletedCallbackMethod(
LPSTR iUnknown)
{
JNIEnv *tmpJniEnv = contactSourceServiceJniEnv;
if(contactSourceServiceVM->GetEnv(
(void**) &tmpJniEnv,
JNI_VERSION_1_6)
!= JNI_OK)
{
contactSourceServiceVM->AttachCurrentThread((void**) &tmpJniEnv, NULL);
}
jstring value;
value = tmpJniEnv->NewStringUTF(iUnknown);
tmpJniEnv->CallBooleanMethod(
contactSourceServiceObject,
contactSourceServiceMethodIdDeleted,
value);
contactSourceServiceVM->DetachCurrentThread();
}
static LPMAPIALLOCATEBUFFER
MsOutlookAddrBookContactSourceService_mapiAllocateBuffer;
static LPMAPIFREEBUFFER MsOutlookAddrBookContactSourceService_mapiFreeBuffer;
@ -317,6 +445,18 @@ Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContac
}
}
if (HR_SUCCEEDED(hResult)
&& MsOutlookAddrBookContactSourceService_mapiSession == NULL)
{
hResult = MAPILogonEx(
0,
NULL, NULL,
MAPI_EXTENDED | MAPI_NO_MAIL | MAPI_USE_DEFAULT,
&MsOutlookAddrBookContactSourceService_mapiSession);
openAllMsgStores(
MsOutlookAddrBookContactSourceService_mapiSession);
}
/* Report any possible error regardless of where it has come from. */
if (HR_FAILED(hResult))
{
@ -335,6 +475,7 @@ Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContac
&MsOutlookAddrBookContactSourceService_mapiSessionCriticalSection);
if (MsOutlookAddrBookContactSourceService_mapiSession)
{
freeAllMsgStores();
MsOutlookAddrBookContactSourceService_mapiSession->Logoff(0, 0, 0);
MsOutlookAddrBookContactSourceService_mapiSession->Release();
MsOutlookAddrBookContactSourceService_mapiSession = NULL;
@ -428,3 +569,8 @@ MsOutlookAddrBookContactSourceService_isValidDefaultMailClient
}
return validDefaultMailClient;
}
LPMAPISESSION MsOutlookAddrBookContactSourceService_getMapiSession()
{
return MsOutlookAddrBookContactSourceService_mapiSession;
}

@ -7,6 +7,11 @@
#ifdef __cplusplus
extern "C" {
#endif
#include <mapidefs.h>
#include <mapix.h>
#include <Unknwn.h>
/*
* Class: net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactSourceService
* Method: MAPIInitialize
@ -23,6 +28,21 @@ JNIEXPORT void JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_
JNIEXPORT void JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactSourceService_MAPIUninitialize
(JNIEnv *, jclass);
JNIEXPORT void JNICALL
Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactSourceService_setDelegate
(JNIEnv *jniEnv, jclass clazz, jobject callback);
void callInsertedCallbackMethod(
LPUNKNOWN iUnknown);
void callUpdatedCallbackMethod(
LPUNKNOWN iUnknown);
void callDeletedCallbackMethod(
LPSTR iUnknown);
LPMAPISESSION MsOutlookAddrBookContactSourceService_getMapiSession();
#ifdef __cplusplus
}
#endif

@ -832,5 +832,43 @@
<echo message="When compiling libjnportaudio you have to tell ant script the directory of portaudio and speex with" />
<echo message="-Dportaudio=/path/to/portaudio and -Dspeex=/path/to/speex" />
</target>
</project>
<!-- compile jnwmsoutlookaddrbook library for Windows (32-bit/64-bit) -->
<target
name="msoutlookaddrbook"
description="Build jmsoutlookaddrbook shared library for Windows"
if="is.running.windows"
depends="init-native">
<cc name="gcc"
objdir="${obj}"
outfile="${native_install_dir}/jmsoutlookaddrbook"
outtype="shared">
<compilerarg value="-D_JNI_IMPLEMENTATION_" />
<compilerarg value="-D_WIN32_WINNT=0x0502" />
<compilerarg value="-DWINVER=0x0502" />
<compilerarg value="-I${system.JAVA_HOME}/include" />
<compilerarg value="-I${system.JAVA_HOME}/include/win32" />
<compilerarg value="-O2" />
<compilerarg value="-Wall" />
<compilerarg value="-Wreturn-type" />
<compilerarg value="-m32" if="cross_32" />
<compilerarg value="-m64" if="cross_64" />
<linkerarg value="-ojmsoutlookaddrbook.dll" />
<linkerarg value="-Wl,--kill-at" />
<linkerarg value="-Wl,--subsystem,windows" />
<libset libs="mapi32" />
<libset libs="uuid" />
<linkerarg value="-m32" if="cross_32" />
<linkerarg value="-m64" if="cross_64" />
<fileset dir="${src}/native/addrbook" includes="*.c" />
<fileset dir="${src}/native/addrbook/msoutlook" includes="*.cxx" />
<fileset dir="${src}/native/addrbook/msoutlook/lib" includes="*.cxx" />
</cc>
<delete dir="${obj}" failonerror="false" />
<delete file="${native_install_dir}/history.xml" failonerror="false" />
</target>
</project>

@ -0,0 +1,111 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.plugin.addrbook.msoutlook;
import net.java.sip.communicator.service.contactsource.*;
import java.util.*;
/**
* Implements a custom <tt>ContactDetail</tt> for the Address Book of Microsoft
* Outlook.
*
* @author Vincent Lucas
*/
public class MsOutlookAddrBookContactDetail
extends EditableContactDetail
{
/**
* The source contact which contains this contact detail.
*/
private MsOutlookAddrBookSourceContact sourceContact;
/**
* The list of codes used by outlook to identify the property corresponding
* to this contact detail.
*/
private Vector<Long> outlookPropId;
/**
* Initializes a new <tt>ContactDetail</tt> instance which is to represent a
* specific contact address and which is to be optionally labeled with a
* specific set of labels.
*
* @param contactDetailValue the contact detail value to be represented by
* the new <tt>ContactDetail</tt> instance
* @param category The category of this contact detail.
* @param subCategories the set of sub categories with which the new
* <tt>ContactDetail</tt> instance is to be labeled.
* @param outlookPropId The identifier of the outlook property used to
* get/set this contact detail.
*/
public MsOutlookAddrBookContactDetail(
String contactDetailValue,
Category category,
SubCategory[] subCategories,
long outlookPropId)
{
super(contactDetailValue, category, subCategories);
this.outlookPropId = new Vector<Long>(1, 1);
this.outlookPropId.add(new Long(outlookPropId));
this.sourceContact = null;
}
/**
* Sets the source contact that contains this contact detail.
*
* @param sourceContact The source contact that contains this contact
* detail.
*/
public void setSourceContact(MsOutlookAddrBookSourceContact sourceContact)
{
this.sourceContact = sourceContact;
}
/**
* If the given contact detail is similar to the current one (same category
* and same detail value), then return true. False otherwise.
*
* @param contactDetail The contact detail to compare with.
*
* @return True, if the given contact detail is similar to the current one
* (same category and same detail value). False otherwise.
*/
public boolean match(ContactDetail contactDetail)
{
return (this.getCategory() == contactDetail.getCategory()
&& this.getDetail().equals(contactDetail.getDetail()));
}
/**
* Returns the list of outlook properties corresponding to this contact
* detail.
*
* @return The list of outlook properties corresponding to this contact
* detail.
*/
public Vector<Long> getOutlookPropId()
{
return this.outlookPropId;
}
/**
* Sets the given detail value.
*
* @param value the new value of the detail
*/
public void setDetail(String value)
{
super.setDetail(value);
if(this.sourceContact != null)
{
this.sourceContact.save();
}
}
}

@ -18,9 +18,10 @@
* Implements <tt>ContactQuery</tt> for the Address Book of Microsoft Outlook.
*
* @author Lyubomir Marinov
* @author Vincent Lucas
*/
public class MsOutlookAddrBookContactQuery
extends AsyncContactQuery<MsOutlookAddrBookContactSourceService>
extends AbstractAddrBookContactQuery<MsOutlookAddrBookContactSourceService>
{
/**
* The <tt>Logger</tt> used by the <tt>MsOutlookAddrBookContactQuery</tt>
@ -62,7 +63,19 @@ public class MsOutlookAddrBookContactQuery
0x00008083 /* dispidEmail1EmailAddress */,
0x00008093 /* dispidEmail2EmailAddress */,
0x000080A3 /* dispidEmail3EmailAddress */,
0x3A16 /* PR_COMPANY_NAME */
0x3A16 /* PR_COMPANY_NAME */,
0x0FFF /* PR_ORIGINAL_ENTRYID */,
0x3A24 /* dispidFax1EmailAddress */,
0x3A25 /* dispidFax2EmailAddress */,
0x3A23 /* dispidFax3EmailAddress */,
0x3A4F /* PR_NICKNAME */,
0x3A45 /* PR_DISPLAY_NAME_PREFIX */,
0x3A50 /* PR_PERSONAL_HOME_PAGE */,
0x3A51 /* PR_BUSINESS_HOME_PAGE */
//0x00008062 /* dispidInstMsg */, // EDITION NOT WORKING
//0x0000801A /* dispidHomeAddress */, // EDITION NOT WORKING
//0x0000801B /* dispidWorkAddress */, // EDITION NOT WORKING
//0x0000801C /* dispidOtherAddress */ // EDITION ONT WORKING
};
/**
@ -150,6 +163,67 @@ public class MsOutlookAddrBookContactQuery
*/
private static final int PR_SURNAME = 4;
/**
* The index of the id of the <tt>PR_ORIGINAL_ENTRYID</tt> property
* in {@link #MAPI_MAILUSER_PROP_IDS}.
*/
private static final int PR_ORIGINAL_ENTRYID = 15;
/**
* The index of the 1st fax telephone number (business fax).
*/
private static final int dispidFax1EmailAddress = 16;
/**
* The index of the 2nd fax telephone number (home fax).
*/
private static final int dispidFax2EmailAddress = 17;
/**
* The index of the 3rd fax telephone number (other fax).
*/
private static final int dispidFax3EmailAddress = 18;
/**
* The index of the nickname.
*/
private static final int PR_NICKNAME = 19;
/**
* The index of the name prefix.
*/
private static final int PR_DISPLAY_NAME_PREFIX = 20;
/**
* The index of the personnal home page
*/
private static final int PR_PERSONAL_HOME_PAGE = 21;
/**
* The index of the business home page
*/
private static final int PR_BUSINESS_HOME_PAGE = 22;
/**
* The index of the instant messaging address.
*/
//private static final int dispidInstMsg = 23;
/**
* The index of the home address
*/
//private static final int dispidHomeAddress = 24;
/**
* The index of the work address
*/
//private static final int dispidWorkAddress = 25;
/**
* The index of the other address
*/
//private static final int dispidOtherAddress = 26;
/**
* The indexes in {@link #MAPI_MAILUSER_PROP_IDS} of the property IDs which
* are to be represented in <tt>SourceContact</tt> as
@ -159,6 +233,9 @@ public class MsOutlookAddrBookContactQuery
= new int[]
{
PR_EMAIL_ADDRESS,
PR_GIVEN_NAME,
PR_MIDDLE_NAME,
PR_SURNAME,
PR_BUSINESS_TELEPHONE_NUMBER,
PR_BUSINESS2_TELEPHONE_NUMBER,
PR_HOME_TELEPHONE_NUMBER,
@ -166,7 +243,19 @@ public class MsOutlookAddrBookContactQuery
PR_MOBILE_TELEPHONE_NUMBER,
dispidEmail1EmailAddress,
dispidEmail2EmailAddress,
dispidEmail3EmailAddress
dispidEmail3EmailAddress,
PR_COMPANY_NAME,
dispidFax1EmailAddress,
dispidFax2EmailAddress,
dispidFax3EmailAddress,
PR_NICKNAME,
PR_DISPLAY_NAME_PREFIX,
PR_PERSONAL_HOME_PAGE,
PR_BUSINESS_HOME_PAGE
//dispidInstMsg,
//dispidHomeAddress,
//dispidWorkAddress,
//dispidOtherAddress
};
static
@ -211,7 +300,7 @@ public MsOutlookAddrBookContactQuery(
* @param callback the <tt>PtrCallback</tt> to be notified about the
* matching <tt>MAPI_MAILUSER</tt>s
*/
private static native void foreachMailUser(
public static native void foreachMailUser(
String query,
PtrCallback callback);
@ -219,6 +308,16 @@ private ContactDetail.Category getCategory(int propIndex)
{
switch (propIndex)
{
case PR_GIVEN_NAME:
case PR_MIDDLE_NAME:
case PR_SURNAME:
case PR_NICKNAME:
case PR_DISPLAY_NAME_PREFIX:
case PR_PERSONAL_HOME_PAGE:
return ContactDetail.Category.Personal;
case PR_COMPANY_NAME:
case PR_BUSINESS_HOME_PAGE:
return ContactDetail.Category.Organization;
case dispidEmail1EmailAddress:
case dispidEmail2EmailAddress:
case dispidEmail3EmailAddress:
@ -229,7 +328,16 @@ private ContactDetail.Category getCategory(int propIndex)
case PR_HOME2_TELEPHONE_NUMBER:
case PR_HOME_TELEPHONE_NUMBER:
case PR_MOBILE_TELEPHONE_NUMBER:
case dispidFax1EmailAddress:
case dispidFax2EmailAddress:
case dispidFax3EmailAddress:
return ContactDetail.Category.Phone;
//case dispidInstMsg:
// return ContactDetail.Category.InstantMessaging;
//case dispidHomeAddress:
//case dispidWorkAddress:
//case dispidOtherAddress:
// return ContactDetail.Category.Address;
default:
return null;
}
@ -248,8 +356,29 @@ private ContactDetail.SubCategory[] getSubCategories(int propIndex)
{
switch (propIndex)
{
case PR_GIVEN_NAME:
case PR_MIDDLE_NAME:
return
new ContactDetail.SubCategory[]
{
ContactDetail.SubCategory.Name
};
case PR_SURNAME:
return
new ContactDetail.SubCategory[]
{
ContactDetail.SubCategory.LastName
};
case PR_NICKNAME:
return
new ContactDetail.SubCategory[]
{
ContactDetail.SubCategory.Nickname
};
case PR_COMPANY_NAME:
case PR_BUSINESS2_TELEPHONE_NUMBER:
case PR_BUSINESS_TELEPHONE_NUMBER:
//case dispidWorkAddress:
return
new ContactDetail.SubCategory[]
{
@ -257,6 +386,7 @@ private ContactDetail.SubCategory[] getSubCategories(int propIndex)
};
case PR_HOME2_TELEPHONE_NUMBER:
case PR_HOME_TELEPHONE_NUMBER:
//case dispidHomeAddress:
return
new ContactDetail.SubCategory[]
{
@ -268,16 +398,130 @@ private ContactDetail.SubCategory[] getSubCategories(int propIndex)
{
ContactDetail.SubCategory.Mobile
};
case dispidFax1EmailAddress:
return
new ContactDetail.SubCategory[]
{
ContactDetail.SubCategory.Fax,
ContactDetail.SubCategory.Work
};
case dispidFax2EmailAddress:
return
new ContactDetail.SubCategory[]
{
ContactDetail.SubCategory.Fax,
ContactDetail.SubCategory.Home
};
case dispidFax3EmailAddress:
return
new ContactDetail.SubCategory[]
{
ContactDetail.SubCategory.Fax,
ContactDetail.SubCategory.Other
};
//case dispidOtherAddress:
// return
// new ContactDetail.SubCategory[]
// {
// ContactDetail.SubCategory.Other
// };
default:
return null;
}
}
/**
* Find the outlook property tag from category and subcategories.
*
* @param category The category.
* @param subCategories The subcategories.
*
* @return The outlook property tag corresponding to the given category and
* subcategories.
*/
public static long getProperty(
ContactDetail.Category category,
Collection<ContactDetail.SubCategory> subCategories)
{
switch(category)
{
case Personal:
if(subCategories.contains(ContactDetail.SubCategory.Name))
return MAPI_MAILUSER_PROP_IDS[PR_GIVEN_NAME];
// PR_MIDDLE_NAME:
else if(subCategories.contains(
ContactDetail.SubCategory.LastName))
return MAPI_MAILUSER_PROP_IDS[PR_SURNAME];
else if(subCategories.contains(
ContactDetail.SubCategory.Nickname))
return MAPI_MAILUSER_PROP_IDS[PR_NICKNAME];
else if(subCategories.contains(
ContactDetail.SubCategory.HomePage))
return MAPI_MAILUSER_PROP_IDS[PR_PERSONAL_HOME_PAGE];
else
return MAPI_MAILUSER_PROP_IDS[PR_DISPLAY_NAME_PREFIX];
case Organization:
if(subCategories.contains(ContactDetail.SubCategory.Work))
return MAPI_MAILUSER_PROP_IDS[PR_COMPANY_NAME];
else
return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_HOME_PAGE];
case Email:
return MAPI_MAILUSER_PROP_IDS[dispidEmail1EmailAddress];
//dispidEmail2EmailAddress:
//dispidEmail3EmailAddress:
// PR_EMAIL_ADDRESS:
case Phone:
if(subCategories.contains(ContactDetail.SubCategory.Fax))
{
if(subCategories.contains(ContactDetail.SubCategory.Work))
return MAPI_MAILUSER_PROP_IDS[dispidFax1EmailAddress];
else if(subCategories.contains(
ContactDetail.SubCategory.Home))
return MAPI_MAILUSER_PROP_IDS[dispidFax2EmailAddress];
else if(subCategories.contains(
ContactDetail.SubCategory.Other))
return MAPI_MAILUSER_PROP_IDS[dispidFax3EmailAddress];
}
else if(subCategories.contains(ContactDetail.SubCategory.Work))
return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_TELEPHONE_NUMBER];
// PR_BUSINESS2_TELEPHONE_NUMBER:
else if(subCategories.contains(ContactDetail.SubCategory.Home))
return MAPI_MAILUSER_PROP_IDS[PR_HOME_TELEPHONE_NUMBER];
// PR_HOME2_TELEPHONE_NUMBER:
else if(subCategories.contains(
ContactDetail.SubCategory.Mobile))
return MAPI_MAILUSER_PROP_IDS[PR_MOBILE_TELEPHONE_NUMBER];
break;
//case InstantMessaging:
// return MAPI_MAILUSER_PROP_IDS[dispidInstMsg];
//case Address:
// if(subCategories.contains(ContactDetail.SubCategory.Work))
// return MAPI_MAILUSER_PROP_IDS[dispidWorkAddress];
// else if(subCategories.contains(
// ContactDetail.SubCategory.Home))
// return MAPI_MAILUSER_PROP_IDS[dispidHomeAddress];
// else if(subCategories.contains(
// ContactDetail.SubCategory.Other))
// return MAPI_MAILUSER_PROP_IDS[dispidOtherAddress];
// break;
}
return -1;
}
private static native Object[] IMAPIProp_GetProps(
long mapiProp,
long[] propIds, long flags)
throws MsOutlookMAPIHResultException;
public static native boolean IMAPIProp_SetPropString(
long propId,
String value,
String entryId);
public static native boolean IMAPIProp_DeleteProp(
long propId,
String entryId);
/**
* Determines whether a specific index in {@link #MAPI_MAILUSER_PROP_IDS}
* stands for a property with a phone number value.
@ -365,43 +609,7 @@ private boolean onMailUser(long iUnknown)
}
if (matches)
{
List<Class<? extends OperationSet>> supportedOpSets
= new ArrayList<Class<? extends OperationSet>>(2);
supportedOpSets.add(OperationSetBasicTelephony.class);
// can be added as contacts
supportedOpSets.add(OperationSetPersistentPresence.class);
List<ContactDetail> contactDetails
= new LinkedList<ContactDetail>();
for (int i = 0; i < CONTACT_DETAIL_PROP_INDEXES.length; i++)
{
propIndex = CONTACT_DETAIL_PROP_INDEXES[i];
Object prop = props[propIndex];
if (prop instanceof String)
{
String stringProp = (String) prop;
if (stringProp.length() != 0)
{
if (isPhoneNumber(propIndex))
stringProp =
PhoneNumberI18nService.normalize(stringProp);
ContactDetail contactDetail
= new ContactDetail(
stringProp,
getCategory(propIndex),
getSubCategories(propIndex));
contactDetail.setSupportedOpSets(supportedOpSets);
contactDetails.add(contactDetail);
}
}
}
List<ContactDetail> contactDetails = getContactDetails(props);
/*
* What's the point of showing a contact who has no contact details?
@ -413,9 +621,10 @@ private boolean onMailUser(long iUnknown)
if ((displayName == null) || (displayName.length() == 0))
displayName = (String) props[PR_COMPANY_NAME];
GenericSourceContact sourceContact
= new GenericSourceContact(
MsOutlookAddrBookSourceContact sourceContact
= new MsOutlookAddrBookSourceContact(
getContactSource(),
(String) props[PR_ORIGINAL_ENTRYID],
displayName,
contactDetails);
@ -450,6 +659,57 @@ private boolean onMailUser(long iUnknown)
return (getStatus() == QUERY_IN_PROGRESS);
}
/**
* Gets the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt>
* which is to represent an <tt>ABPerson</tt> specified by the values of its
* {@link #ABPERSON_PROPERTIES}.
*
* @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which
* represent the <tt>ABPerson</tt> to get the <tt>contactDetails</tt> of
* @return the <tt>contactDetails</tt> to be set on a <tt>SourceContact</tt>
* which is to represent the <tt>ABPerson</tt> specified by <tt>values</tt>
*/
private List<ContactDetail> getContactDetails(Object[] values)
{
List<Class<? extends OperationSet>> supportedOpSets
= new ArrayList<Class<? extends OperationSet>>(2);
supportedOpSets.add(OperationSetBasicTelephony.class);
// can be added as contacts
supportedOpSets.add(OperationSetPersistentPresence.class);
List<ContactDetail> contactDetails = new LinkedList<ContactDetail>();
for (int i = 0; i < CONTACT_DETAIL_PROP_INDEXES.length; i++)
{
int property = CONTACT_DETAIL_PROP_INDEXES[i];
Object value = values[property];
if (value instanceof String)
{
String stringValue = (String) value;
if (stringValue.length() != 0)
{
if(isPhoneNumber(property))
stringValue
= PhoneNumberI18nService.normalize(stringValue);
MsOutlookAddrBookContactDetail contactDetail
= new MsOutlookAddrBookContactDetail(
stringValue,
getCategory(property),
getSubCategories(property),
MAPI_MAILUSER_PROP_IDS[property]);
contactDetail.setSupportedOpSets(supportedOpSets);
contactDetails.add(contactDetail);
}
}
}
return contactDetails;
}
/**
* Performs this <tt>AsyncContactQuery</tt> in a background <tt>Thread</tt>.
*
@ -487,24 +747,89 @@ public boolean callback(long iUnknown)
}
/**
* Notifies this <tt>AsyncContactQuery</tt> that it has stopped performing
* in the associated background <tt>Thread</tt>.
* Callback method when receiving notifications for inserted items.
*
* @param person The pointer to the outlook contact object.
*/
public void inserted(long person)
{
try
{
onMailUser(person);
}
catch (MsOutlookMAPIHResultException e)
{
if (logger.isDebugEnabled())
{
logger.debug(
MsOutlookAddrBookContactQuery.class.getSimpleName()
+ "#onMailUser(long)",
e);
}
}
}
/**
* Callback method when receiving notifications for updated items.
*
* @param completed <tt>true</tt> if this <tt>ContactQuery</tt> has
* successfully completed, <tt>false</tt> if an error has been encountered
* during its execution
* @see AsyncContactQuery#stopped(boolean)
* @param person The pointer to the outlook contact object.
*/
@Override
protected void stopped(boolean completed)
public void updated(long person)
{
Object[] props = null;
try
{
super.stopped(completed);
props = IMAPIProp_GetProps(
person,
MAPI_MAILUSER_PROP_IDS,
MAPI_UNICODE);
}
finally
catch (MsOutlookMAPIHResultException e)
{
getContactSource().stopped(this);
if (logger.isDebugEnabled())
{
logger.debug(
MsOutlookAddrBookContactQuery.class.getSimpleName()
+ "#IMAPIProp_GetProps(long, long[], long)",
e);
}
}
if(props[PR_ORIGINAL_ENTRYID] != null)
{
SourceContact sourceContact
= findSourceContactByID((String) props[PR_ORIGINAL_ENTRYID]);
if(sourceContact != null
&& sourceContact instanceof MsOutlookAddrBookSourceContact)
{
// let's update the the details
MsOutlookAddrBookSourceContact editableSourceContact
= (MsOutlookAddrBookSourceContact) sourceContact;
List<ContactDetail> contactDetails = getContactDetails(props);
editableSourceContact.setDetails(contactDetails);
fireContactChanged(sourceContact);
}
}
}
/**
* Callback method when receiving notifications for deleted items.
*
* @param id The outlook contact identifier.
*/
public void deleted(String id)
{
if(id != null)
{
SourceContact sourceContact = findSourceContactByID(id);
if(sourceContact != null)
{
fireContactRemoved(sourceContact);
}
}
}
}

@ -11,16 +11,26 @@
import net.java.sip.communicator.plugin.addrbook.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.util.*;
/**
* Implements <tt>ContactSourceService</tt> for the Address Book of Microsoft
* Outlook.
*
* @author Lyubomir Marinov
* @author Vincent Lucas
*/
public class MsOutlookAddrBookContactSourceService
extends AsyncContactSourceService
{
/**
* The <tt>Logger</tt> used by the
* <tt>MsOutlookAddrBookContactSourceService</tt> class and its instances
* for logging output.
*/
private static final Logger logger
= Logger.getLogger(MsOutlookAddrBookContactSourceService.class);
/**
* The outlook address book prefix.
*/
@ -31,6 +41,11 @@ public class MsOutlookAddrBookContactSourceService
private static final long MAPI_MULTITHREAD_NOTIFICATIONS = 0x00000001;
/**
* The latest query created.
*/
private MsOutlookAddrBookContactQuery latestQuery = null;
static
{
System.loadLibrary("jmsoutlookaddrbook");
@ -53,13 +68,6 @@ public class MsOutlookAddrBookContactSourceService
}
}
/**
* The <tt>List</tt> of <tt>MsOutlookAddrBookContactQuery</tt> instances
* which have been started and haven't stopped yet.
*/
private final List<MsOutlookAddrBookContactQuery> queries
= new LinkedList<MsOutlookAddrBookContactQuery>();
/**
* Initializes a new <tt>MsOutlookAddrBookContactSourceService</tt>
* instance.
@ -72,6 +80,8 @@ public MsOutlookAddrBookContactSourceService()
throws MsOutlookMAPIHResultException
{
MAPIInitialize(MAPI_INIT_VERSION, MAPI_MULTITHREAD_NOTIFICATIONS);
setDelegate(new NotificationsDelegate());
}
/**
@ -119,33 +129,13 @@ private static native void MAPIInitialize(long version, long flags)
*/
public ContactQuery queryContactSource(Pattern query)
{
MsOutlookAddrBookContactQuery msoabcq
= new MsOutlookAddrBookContactQuery(this, query);
synchronized (queries)
{
queries.add(msoabcq);
}
if(latestQuery != null)
latestQuery.clear();
boolean msoabcqHasStarted = false;
latestQuery = new MsOutlookAddrBookContactQuery(this, query);
try
{
msoabcq.start();
msoabcqHasStarted = true;
}
finally
{
if (!msoabcqHasStarted)
{
synchronized (queries)
{
if (queries.remove(msoabcq))
queries.notify();
}
}
}
return msoabcq;
latestQuery.start();
return latestQuery;
}
/**
@ -156,26 +146,11 @@ public ContactQuery queryContactSource(Pattern query)
*/
public void stop()
{
boolean interrupted = false;
synchronized (queries)
if(latestQuery != null)
{
while (!queries.isEmpty())
{
queries.get(0).cancel();
try
{
queries.wait();
}
catch (InterruptedException iex)
{
interrupted = true;
}
}
latestQuery.clear();
latestQuery = null;
}
if (interrupted)
Thread.currentThread().interrupt();
MAPIUninitialize();
}
@ -192,28 +167,48 @@ public String getPhoneNumberPrefix()
}
/**
* Notifies this <tt>MsOutlookAddrBookContactSourceService</tt> that a
* specific <tt>MsOutlookAddrBookContactQuery</tt> has stopped.
* Returns the index of the contact source in the result list.
*
* @param msoabcq the <tt>MsOutlookAddrBookContactQuery</tt> which has
* stopped
* @return the index of the contact source in the result list
*/
void stopped(MsOutlookAddrBookContactQuery msoabcq)
public int getIndex()
{
synchronized (queries)
{
if (queries.remove(msoabcq))
queries.notify();
}
return -1;
}
/**
* Returns the index of the contact source in the result list.
*
* @return the index of the contact source in the result list
* Delegate class to be notified for addressbook changes.
*/
public int getIndex()
public class NotificationsDelegate
{
return -1;
/**
* Callback method when receiving notifications for inserted items.
*/
public void inserted(long person)
{
if(latestQuery != null)
latestQuery.inserted(person);
}
/**
* Callback method when receiving notifications for updated items.
*/
public void updated(long person)
{
if(latestQuery != null)
latestQuery.updated(person);
}
/**
* Callback method when receiving notifications for deleted items.
*/
public void deleted(String id)
{
if(latestQuery != null)
latestQuery.deleted(id);
}
}
public static native void setDelegate(
NotificationsDelegate callback);
}

@ -0,0 +1,195 @@
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.plugin.addrbook.msoutlook;
import net.java.sip.communicator.plugin.addrbook.*;
import net.java.sip.communicator.service.contactsource.*;
import net.java.sip.communicator.util.*;
import java.util.*;
/**
* Implements a custom <tt>SourceContact</tt> for the Address Book of Microsoft
* Outlook.
*
* @author Vincent Lucas
*/
public class MsOutlookAddrBookSourceContact
extends GenericSourceContact
implements EditableSourceContact
{
/**
* The <tt>Logger</tt> used by the <tt>MsOutlookAddrBookSourceContact</tt>
* class and its instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(MsOutlookAddrBookSourceContact.class);
/**
* Initializes a new MsOutlookAddrBookSourceContact instance.
*
* @param contactSource The ContactSourceService which is creating the new
* instance.
* @param id The outlook entry identifier for contacts.
* @param displayName The display name of the new instance.
* @param contactDetails The ContactDetails of the new instance.
*/
public MsOutlookAddrBookSourceContact(
ContactSourceService contactSource,
String id,
String displayName,
List<ContactDetail> contactDetails)
{
super(contactSource, displayName, contactDetails);
this.setData(SourceContact.DATA_ID, id);
}
/**
* Returns the string identifier for this source contact.
*
* @return The string identifier for this source contact.
*/
public String getId()
{
return (String) this.getData(SourceContact.DATA_ID);
}
/**
* Changes the details list with the supplied one. Called when the outlook
* database for this source contact has been updated.
*
* @param details the details.
*/
public void setDetails(List<ContactDetail> details)
{
synchronized(this.contactDetails)
{
contactDetails.clear();
contactDetails.addAll(details);
}
}
/**
* Saves all the properties from this source contact into the outlook
* database.
*/
public void save()
{
synchronized(this.contactDetails)
{
MsOutlookAddrBookContactDetail outlookContactDetail;
for(ContactDetail contactDetail: this.contactDetails)
{
if(contactDetail instanceof MsOutlookAddrBookContactDetail)
{
outlookContactDetail
= (MsOutlookAddrBookContactDetail) contactDetail;
for(Long propId: outlookContactDetail.getOutlookPropId())
{
MsOutlookAddrBookContactQuery.IMAPIProp_SetPropString(
propId.longValue(),
contactDetail.getDetail(),
this.getId());
}
}
}
}
}
/**
* Removes the given <tt>ContactDetail</tt> from the list of details for
* this <tt>SourceContact</tt>.
*
* @param detail the <tt>ContactDetail</tt> to remove
*/
public void removeContactDetail(ContactDetail detail)
{
synchronized(this.contactDetails)
{
int i = 0;
while(i < this.contactDetails.size())
{
MsOutlookAddrBookContactDetail contactDetail
= ((MsOutlookAddrBookContactDetail)
this.contactDetails.get(i));
if(contactDetail.match(detail))
{
this.removeProperty(contactDetail);
this.contactDetails.remove(i);
}
else
{
++i;
}
}
}
}
/**
* Removes the contact detail from the outlook database.
*
* @param contactDetail The contact detail to remove.
*/
public void removeProperty(
final MsOutlookAddrBookContactDetail contactDetail)
{
for(Long propId: contactDetail.getOutlookPropId())
{
MsOutlookAddrBookContactQuery.IMAPIProp_DeleteProp(
propId.longValue(),
this.getId());
}
}
/**
* Adds a contact detail to the list of contact details.
*
* @param detail the <tt>ContactDetail</tt> to add
*/
public void addContactDetail(ContactDetail detail)
{
synchronized(this.contactDetails)
{
MsOutlookAddrBookContactDetail addDetail;
if(!(detail instanceof MsOutlookAddrBookContactDetail))
{
long property = MsOutlookAddrBookContactQuery.getProperty(
detail.getCategory(),
detail.getSubCategories());
Collection<ContactDetail.SubCategory> subCategories
= detail.getSubCategories();
addDetail = new MsOutlookAddrBookContactDetail(
detail.getDetail(),
detail.getCategory(),
subCategories.toArray(
new ContactDetail.SubCategory[
subCategories.size()]),
property);
}
else
{
addDetail = (MsOutlookAddrBookContactDetail) detail;
}
// Checks if this property already exists.
for(int i = 0; i < this.contactDetails.size(); ++ i)
{
MsOutlookAddrBookContactDetail contactDetail
= ((MsOutlookAddrBookContactDetail)
this.contactDetails.get(i));
if(contactDetail.match(addDetail))
{
return;
}
}
this.contactDetails.add(addDetail);
this.save();
}
}
}
Loading…
Cancel
Save