aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/native/addrbook/AddrBookContactQuery.c35
-rw-r--r--src/native/addrbook/AddrBookContactQuery.h3
-rw-r--r--src/native/addrbook/macosx/net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery.h9
-rw-r--r--src/native/addrbook/macosx/net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery.m1161
-rw-r--r--src/native/addrbook/msoutlook/MAPINotification.cxx1136
-rw-r--r--src/native/addrbook/msoutlook/net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.cxx3319
-rw-r--r--src/native/addrbook/msoutlook/net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.h78
-rw-r--r--src/native/build.xml5
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactDetail.java7
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactQuery.java2757
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactSourceService.java457
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookSourceContact.java92
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactQuery.java2005
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactSourceService.java487
-rw-r--r--src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookSourceContact.java143
-rw-r--r--src/net/java/sip/communicator/service/contactsource/EditableContactSourceService.java41
-rw-r--r--src/net/java/sip/communicator/service/contactsource/EditableSourceContact.java11
17 files changed, 6560 insertions, 5186 deletions
diff --git a/src/native/addrbook/AddrBookContactQuery.c b/src/native/addrbook/AddrBookContactQuery.c
index 1045214..415ce67 100644
--- a/src/native/addrbook/AddrBookContactQuery.c
+++ b/src/native/addrbook/AddrBookContactQuery.c
@@ -44,6 +44,41 @@ AddrBookContactQuery_getPtrCallbackMethodID(JNIEnv *jniEnv, jobject callback)
return callbackMethodID;
}
+jmethodID
+AddrBookContactQuery_getStringCallbackMethodID(
+ JNIEnv *jniEnv, jobject callback)
+{
+ jclass callbackClass;
+ jmethodID callbackMethodID = 0;
+
+ /*
+ * Make sure that the specified arguments are valid. For example, check
+ * whether callback exists and has the necessary signature.
+ */
+ if (callback)
+ {
+ callbackClass = (*jniEnv)->GetObjectClass(jniEnv, callback);
+ if (callbackClass)
+ {
+ callbackMethodID
+ = (*jniEnv)->GetMethodID(
+ jniEnv,
+ callbackClass, "callback", "(Ljava/lang/String;)Z");
+ if (!callbackMethodID)
+ {
+ Exception_throwNew(
+ jniEnv, "java/lang/IllegalArgumentException", "callback");
+ }
+ }
+ }
+ else
+ {
+ Exception_throwNew(
+ jniEnv, "java/lang/NullPointerException", "callback");
+ }
+ return callbackMethodID;
+}
+
static void
Exception_throwNew(JNIEnv *jniEnv, const char *className, const char *message)
{
diff --git a/src/native/addrbook/AddrBookContactQuery.h b/src/native/addrbook/AddrBookContactQuery.h
index 46cc77d..b0d146a 100644
--- a/src/native/addrbook/AddrBookContactQuery.h
+++ b/src/native/addrbook/AddrBookContactQuery.h
@@ -17,6 +17,9 @@ extern "C" {
jmethodID AddrBookContactQuery_getPtrCallbackMethodID
(JNIEnv *jniEnv, jobject callback);
+jmethodID AddrBookContactQuery_getStringCallbackMethodID
+ (JNIEnv *jniEnv, jobject callback);
+
#ifdef __cplusplus
}
#endif /* #ifdef __cplusplus */
diff --git a/src/native/addrbook/macosx/net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery.h b/src/native/addrbook/macosx/net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery.h
index 0f4d218..714bdf0 100644
--- a/src/native/addrbook/macosx/net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery.h
+++ b/src/native/addrbook/macosx/net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery.h
@@ -557,6 +557,15 @@ JNIEXPORT jstring JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_
JNIEXPORT jstring JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_kABAddressCountryKey
(JNIEnv *, jclass);
+JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_deleteContact
+ (JNIEnv *jniEnv, jclass clazz, jstring id);
+
+JNIEXPORT jstring JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_createContact
+ (JNIEnv *jniEnv, jclass clazz);
+
+JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_getContactPointer
+ (JNIEnv *jniEnv, jclass clazz, jstring id);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/native/addrbook/macosx/net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery.m b/src/native/addrbook/macosx/net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery.m
index fca3afa..0c2ba19 100644
--- a/src/native/addrbook/macosx/net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery.m
+++ b/src/native/addrbook/macosx/net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery.m
@@ -1,551 +1,610 @@
-/*
- * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-
-#include "net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery.h"
-
-#include "AddrBookContactQuery.h"
-
-#import <AddressBook/AddressBook.h>
-#import <Foundation/NSArray.h>
-#import <Foundation/NSAutoreleasePool.h>
-#import <Foundation/NSData.h>
-
-static void MacOSXAddrBookContactQuery_idToJObject
- (JNIEnv *jniEnv, id o, jobjectArray jos, jint i, jclass objectClass);
-
-JNIEXPORT jbyteArray JNICALL
-Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_ABPerson_1imageData
- (JNIEnv *jniEnv, jclass clazz, jlong person)
-{
- NSData *imageData = [((ABPerson *) person) imageData];
- jbyteArray jImageData;
-
- if (imageData)
- {
- NSUInteger length = [imageData length];
-
- if (length)
- {
- jImageData = (*jniEnv)->NewByteArray(jniEnv, length);
- if (jImageData)
- {
- (*jniEnv)->SetByteArrayRegion(
- jniEnv,
- jImageData, 0, length,
- [imageData bytes]);
- }
- }
- else
- jImageData = NULL;
- }
- else
- jImageData = NULL;
- return jImageData;
-}
-
-JNIEXPORT jobjectArray JNICALL
-Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_ABRecord_1valuesForProperties
- (JNIEnv *jniEnv, jclass clazz, jlong record, jlongArray properties)
-{
- jsize propertyCount;
- jobjectArray values = NULL;
-
- propertyCount = (*jniEnv)->GetArrayLength(jniEnv, properties);
- if (propertyCount)
- {
- jclass objectClass;
-
- objectClass = (*jniEnv)->FindClass(jniEnv, "java/lang/Object");
- if (objectClass)
- {
- values
- = (*jniEnv)->NewObjectArray(
- jniEnv,
- propertyCount, objectClass, NULL);
- if (values)
- {
- jint i;
- ABRecord *r = (ABRecord *) record;
-
- for (i = 0; i < propertyCount; i++)
- {
- jlong property;
-
- (*jniEnv)->GetLongArrayRegion(
- jniEnv,
- properties, i, 1, &property);
- MacOSXAddrBookContactQuery_idToJObject(
- jniEnv,
- [r valueForProperty:(NSString *)property],
- values, i,
- objectClass);
- if (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv))
- break;
- }
- }
- }
- }
- return values;
-}
-
-JNIEXPORT void JNICALL
-Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_foreachPerson
- (JNIEnv *jniEnv, jclass clazz, jstring query, jobject callback)
-{
- jmethodID callbackMethodID;
- NSAutoreleasePool *autoreleasePool;
- ABAddressBook *addressBook;
- NSArray *people;
- NSUInteger peopleCount;
- NSUInteger i;
-
- callbackMethodID
- = AddrBookContactQuery_getPtrCallbackMethodID(jniEnv, callback);
- if (!callbackMethodID || (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv)))
- return;
-
- autoreleasePool = [[NSAutoreleasePool alloc] init];
-
- addressBook = [ABAddressBook sharedAddressBook];
- people = [addressBook people];
- peopleCount = [people count];
- for (i = 0; i < peopleCount; i++)
- {
- jboolean proceed;
- ABPerson *person = [people objectAtIndex:i];
-
- proceed
- = (*jniEnv)->CallBooleanMethod(
- jniEnv,
- callback, callbackMethodID,
- person);
- if ((JNI_FALSE == proceed)
- || (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv)))
- break;
- }
-
- [autoreleasePool release];
-}
-
-/*
- * Class: net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery
- * Method: ABRecord_uniqueId
- * Signature: (J)Ljava/lang/String;
- */
-JNIEXPORT jstring JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_ABRecord_1uniqueId
- (JNIEnv *jniEnv, jclass clazz, jlong record)
-{
- return (*jniEnv)->NewStringUTF(jniEnv, [[(ABRecord *)record uniqueId] UTF8String]);
-}
-
-NSString *JavaStringToNSString(JNIEnv *env, jstring aString)
-{
- if(aString == NULL)
- return nil;
-
- const jchar *chars = (*env)->GetStringChars(env, aString, NULL);
- NSString *resultString = [NSString stringWithCharacters:(UniChar *)chars length:(*env)->GetStringLength(env, aString)];
- (*env)->ReleaseStringChars(env, aString, chars);
- return resultString;
-}
-
-/*
- * Class: net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery
- * Method: setProperty
- * Signature: (Ljava/lang/String;JLjava/lang/String;Ljava/lang/Object;)Z
- */
-JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_setProperty
- (JNIEnv *jniEnv, jclass clazz, jstring id, jlong prop, jstring subProperty, jobject value)
-{
- void* data;
- ABAddressBook *addressBook;
- ABRecord *r;
- NSAutoreleasePool *autoreleasePool;
- NSString *property;
- BOOL res = FALSE;
- int i;
-
- autoreleasePool = [[NSAutoreleasePool alloc] init];
-
- addressBook = [ABAddressBook sharedAddressBook];
- r = [addressBook recordForUniqueId:JavaStringToNSString(jniEnv, id)];
-
- property = (NSString *)prop;
-
- if(property == kABFirstNameProperty
- || property == kABLastNameProperty
- || property == kABFirstNamePhoneticProperty
- || property == kABLastNamePhoneticProperty
- || property == kABNicknameProperty
- || property == kABMaidenNameProperty
- || property == kABOrganizationProperty
- || property == kABJobTitleProperty
- || property == kABHomePageProperty
- || property == kABDepartmentProperty
- || property == kABNoteProperty
- || property == kABMiddleNameProperty
- || property == kABMiddleNamePhoneticProperty
- || property == kABTitleProperty
- || property == kABSuffixProperty)
- {
- data = JavaStringToNSString(jniEnv, (jstring)value);
- }
- else if(property == kABBirthdayProperty)
- {
- data = [NSDate dateWithTimeIntervalSince1970:(jlong)value];
- }
- else if(property == kABURLsProperty
- || property == kABCalendarURIsProperty
- || property == kABEmailProperty
- || property == kABRelatedNamesProperty
- || property == kABPhoneProperty
- || property == kABAIMInstantProperty
- || property == kABJabberInstantProperty
- || property == kABMSNInstantProperty
- || property == kABYahooInstantProperty
- || property == kABICQInstantProperty)
- {
- data=[[ABMutableMultiValue alloc] init];
- jobjectArray arr = (jobjectArray)value;
- jsize propertyCount = (*jniEnv)->GetArrayLength(jniEnv, arr);
-
- for (i = 0; i < propertyCount; i+=2)
- {
- jstring value = (jstring) (*jniEnv)->GetObjectArrayElement(jniEnv, arr, i);
- jstring label = (jstring) (*jniEnv)->GetObjectArrayElement(jniEnv, arr, i+1);
-
- [(ABMutableMultiValue *) data
- addValue:JavaStringToNSString(jniEnv, value)
- withLabel:JavaStringToNSString(jniEnv, label)];
- }
- }
- else if(property == kABAddressProperty)
- {
- jobjectArray arr = (jobjectArray)value;
- jsize propertyCount = (*jniEnv)->GetArrayLength(jniEnv, arr);
-
- NSMutableDictionary *addr;
- addr = [NSMutableDictionary dictionary];
-
- data=[[ABMutableMultiValue alloc] init];
- NSString *subProp = NULL;
- NSString *lastSubProp;
- for (i = 0; i < propertyCount; i+=3)
- {
- jstring value
- = (jstring) (*jniEnv)->GetObjectArrayElement(jniEnv, arr, i);
- jstring label
- = (jstring) (*jniEnv)->GetObjectArrayElement(jniEnv, arr, i+1);
- jstring tmpLastSubProp
- = (jstring) (*jniEnv)->GetObjectArrayElement(jniEnv, arr, i+2);
- lastSubProp = JavaStringToNSString(jniEnv, tmpLastSubProp);
- // Initiates the first sub-property value.
- if(i == 0)
- {
- subProp = lastSubProp;
- }
-
- // If there is a change in the sub-property, then save the actual
- // one to the address property and create a new sub-property list
- // (Home, Work).
- if(![lastSubProp isEqualToString: subProp])
- {
- [(ABMutableMultiValue *) data addValue:addr withLabel:subProp];
- addr = [NSMutableDictionary dictionary];
- // Sets the new current proeperty
- subProp = lastSubProp;
- }
-
- //NSLog(@"key:%@, value:%@", JavaStringToNSString(jniEnv, label),
- //JavaStringToNSString(jniEnv, value));
- [addr setObject:JavaStringToNSString(jniEnv, value)
- forKey:JavaStringToNSString(jniEnv, label)];
- }
- // Adds the last sub-property to the address book.
- if(i > 0)
- {
- [(ABMutableMultiValue *) data addValue: addr withLabel: subProp];
- }
- }
- //else if(property == kABOtherDatesProperty)//kABMultiDateProperty
- else
- {
- data = NULL;
- }
-
- if(data)
- res = [r setValue:data forProperty:(NSString *)property];
-
- [addressBook save];
-
- [autoreleasePool release];
-
- return res;
-}
-
-/*
- * Class: net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery
- * Method: removeProperty
- * Signature: (Ljava/lang/String;J)Z
- */
-JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_removeProperty
- (JNIEnv *jniEnv, jclass clazz, jstring id, jlong property)
-{
- ABAddressBook *addressBook;
- ABRecord *r;
- NSAutoreleasePool *autoreleasePool;
-
- autoreleasePool = [[NSAutoreleasePool alloc] init];
-
- addressBook = [ABAddressBook sharedAddressBook];
- r = [addressBook recordForUniqueId:JavaStringToNSString(jniEnv, id)];
-
- BOOL res = [r removeValueForProperty:(NSString *)property];
-
- [addressBook save];
-
- [autoreleasePool release];
-
- return res;
-}
-
-#define DEFINE_ABPERSON_PROPERTY_GETTER(property) \
- JNIEXPORT jlong JNICALL \
- Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_##property \
- (JNIEnv *jniEnv, jclass clazz) \
- { \
- return (jlong) property; \
- }
-
-DEFINE_ABPERSON_PROPERTY_GETTER(kABAIMInstantProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABEmailProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABFirstNameProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABFirstNamePhoneticProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABICQInstantProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABJabberInstantProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABLastNameProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABLastNamePhoneticProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABMiddleNameProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABMiddleNamePhoneticProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABMSNInstantProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABNicknameProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABOrganizationProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABPersonFlags)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABPhoneProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABYahooInstantProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABMaidenNameProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABBirthdayProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABJobTitleProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABHomePageProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABURLsProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABCalendarURIsProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABAddressProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABOtherDatesProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABOtherDateComponentsProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABRelatedNamesProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABDepartmentProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABNoteProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABTitleProperty)
-DEFINE_ABPERSON_PROPERTY_GETTER(kABSuffixProperty)
-
-#define DEFINE_ABLABEL_PROPERTY_GETTER(property) \
- JNIEXPORT jstring JNICALL \
- Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_##property \
- (JNIEnv *jniEnv, jclass clazz) \
- { \
- return (*jniEnv)->NewStringUTF(jniEnv, [((NSString *) property) UTF8String]); \
- }
-DEFINE_ABLABEL_PROPERTY_GETTER(kABHomePageLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABEmailWorkLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABEmailHomeLabel)
-//DEFINE_ABLABEL_PROPERTY_GETTER(kABEmailMobileMeLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressHomeLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressWorkLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABAnniversaryLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABFatherLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABMotherLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABParentLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABBrotherLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABSisterLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABChildLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABFriendLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABSpouseLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABPartnerLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABAssistantLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABManagerLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABPhoneWorkLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABPhoneHomeLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABPhoneMobileLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABPhoneMainLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABPhoneHomeFAXLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABPhoneWorkFAXLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABPhonePagerLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABAIMWorkLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABAIMHomeLabel)
-//DEFINE_ABLABEL_PROPERTY_GETTER(kABAIMMobileMeLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABJabberWorkLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABJabberHomeLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABMSNWorkLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABMSNHomeLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABYahooWorkLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABYahooHomeLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABICQWorkLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABICQHomeLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressStreetKey)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressCityKey)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressStateKey)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressZIPKey)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressCountryKey)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABWorkLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABHomeLabel)
-DEFINE_ABLABEL_PROPERTY_GETTER(kABOtherLabel)
-
-static void
-MacOSXAddrBookContactQuery_idToJObject
- (JNIEnv *jniEnv,
- id o,
- jobjectArray jos, jint i,
- jclass objectClass)
-{
- if (o)
- {
- jobject jo;
-
- if ([o isKindOfClass:[NSString class]])
- {
- jo = (*jniEnv)->NewStringUTF(jniEnv, [((NSString *) o) UTF8String]);
- }
- else if ([o isKindOfClass:[ABMultiValue class]])
- {
- /*
- * We changed our minds after the initial implementation and decided
- * that we want to display not only the values but the labels as
- * well. In order to minimize the scope of the modifications, we'll
- * be returning each label in the same array right after its
- * corresponding value.
- */
- ABMultiValue *mv = (ABMultiValue *) o;
- NSUInteger mvCount = [mv count];
- jobjectArray joArray
- = (*jniEnv)->NewObjectArray(
- jniEnv,
- mvCount * 2 /* value, label */,
- objectClass, NULL);
-
- jo = joArray;
- if (joArray)
- {
- NSUInteger j, j2;
-
- for (j = 0; j < mvCount; j++)
- {
- j2 = j * 2;
-
- //NSLog(@"key:%@, label:%@",
- // [mv valueAtIndex:j], [mv labelAtIndex:j]);
-
- MacOSXAddrBookContactQuery_idToJObject(
- jniEnv,
- [mv valueAtIndex:j],
- joArray, j2,
- objectClass);
- if (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv))
- {
- jo = NULL;
- break;
- }
- MacOSXAddrBookContactQuery_idToJObject(
- jniEnv,
- [mv labelAtIndex:j],
- joArray, j2 + 1,
- objectClass);
- if (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv))
- {
- jo = NULL;
- break;
- }
- }
- }
- }
- else if ([o isKindOfClass:[NSNumber class]])
- {
- jclass longClass = (*jniEnv)->FindClass(jniEnv, "java/lang/Long");
-
- jo = NULL;
- if (longClass)
- {
- jmethodID longMethodID
- = (*jniEnv)->GetMethodID(
- jniEnv,
- longClass, "<init>", "(J)V");
-
- if (longMethodID)
- {
- jo
- = (*jniEnv)->NewObject(
- jniEnv,
- longClass, longMethodID,
- (jlong) ([((NSNumber *) o) longValue]));
- }
- }
- }
- else if ([o isKindOfClass:[NSDictionary class]])
- {
- NSDictionary *dict = (NSDictionary *)o;
-
- NSUInteger dictCount = [dict count];
- jobjectArray joArray
- = (*jniEnv)->NewObjectArray(
- jniEnv,
- dictCount * 2, objectClass, NULL);
- jo = joArray;
- if (joArray)
- {
- NSEnumerator *enumerator = [dict keyEnumerator];
- id key;
- NSUInteger j, j2;
- j = 0;
- while ((key = [enumerator nextObject]))
- {
- //NSLog(@"key:%@, value:%@", key, [dict objectForKey: key]);
-
- j2 = j * 2;
-
- MacOSXAddrBookContactQuery_idToJObject(
- jniEnv,
- [dict objectForKey: key],
- joArray, j2,
- objectClass);
- if (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv))
- {
- jo = NULL;
- break;
- }
- MacOSXAddrBookContactQuery_idToJObject(
- jniEnv,
- key,
- joArray, j2 + 1,
- objectClass);
- if (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv))
- {
- jo = NULL;
- break;
- }
-
- j++;
- }
- }
- }
- else
- {
- //NSLog(@"type:%@", NSStringFromClass([o class]));
- jo = NULL;
- }
- if (jo)
- (*jniEnv)->SetObjectArrayElement(jniEnv, jos, i, jo);
- }
-}
+/*
+ * SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+#include "net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery.h"
+
+#include "AddrBookContactQuery.h"
+
+#import <AddressBook/AddressBook.h>
+#import <Foundation/NSArray.h>
+#import <Foundation/NSAutoreleasePool.h>
+#import <Foundation/NSData.h>
+
+static void MacOSXAddrBookContactQuery_idToJObject
+ (JNIEnv *jniEnv, id o, jobjectArray jos, jint i, jclass objectClass);
+
+JNIEXPORT jbyteArray JNICALL
+Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_ABPerson_1imageData
+ (JNIEnv *jniEnv, jclass clazz, jlong person)
+{
+ NSData *imageData = [((ABPerson *) person) imageData];
+ jbyteArray jImageData;
+
+ if (imageData)
+ {
+ NSUInteger length = [imageData length];
+
+ if (length)
+ {
+ jImageData = (*jniEnv)->NewByteArray(jniEnv, length);
+ if (jImageData)
+ {
+ (*jniEnv)->SetByteArrayRegion(
+ jniEnv,
+ jImageData, 0, length,
+ [imageData bytes]);
+ }
+ }
+ else
+ jImageData = NULL;
+ }
+ else
+ jImageData = NULL;
+ return jImageData;
+}
+
+JNIEXPORT jobjectArray JNICALL
+Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_ABRecord_1valuesForProperties
+ (JNIEnv *jniEnv, jclass clazz, jlong record, jlongArray properties)
+{
+ jsize propertyCount;
+ jobjectArray values = NULL;
+
+ propertyCount = (*jniEnv)->GetArrayLength(jniEnv, properties);
+ if (propertyCount)
+ {
+ jclass objectClass;
+
+ objectClass = (*jniEnv)->FindClass(jniEnv, "java/lang/Object");
+ if (objectClass)
+ {
+ values
+ = (*jniEnv)->NewObjectArray(
+ jniEnv,
+ propertyCount, objectClass, NULL);
+ if (values)
+ {
+ jint i;
+ ABRecord *r = (ABRecord *) record;
+
+ for (i = 0; i < propertyCount; i++)
+ {
+ jlong property;
+
+ (*jniEnv)->GetLongArrayRegion(
+ jniEnv,
+ properties, i, 1, &property);
+ MacOSXAddrBookContactQuery_idToJObject(
+ jniEnv,
+ [r valueForProperty:(NSString *)property],
+ values, i,
+ objectClass);
+ if (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv))
+ break;
+ }
+ }
+ }
+ }
+ return values;
+}
+
+JNIEXPORT void JNICALL
+Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_foreachPerson
+ (JNIEnv *jniEnv, jclass clazz, jstring query, jobject callback)
+{
+ jmethodID callbackMethodID;
+ NSAutoreleasePool *autoreleasePool;
+ ABAddressBook *addressBook;
+ NSArray *people;
+ NSUInteger peopleCount;
+ NSUInteger i;
+
+ callbackMethodID
+ = AddrBookContactQuery_getPtrCallbackMethodID(jniEnv, callback);
+ if (!callbackMethodID || (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv)))
+ return;
+
+ autoreleasePool = [[NSAutoreleasePool alloc] init];
+
+ addressBook = [ABAddressBook sharedAddressBook];
+ people = [addressBook people];
+ peopleCount = [people count];
+ for (i = 0; i < peopleCount; i++)
+ {
+ jboolean proceed;
+ ABPerson *person = [people objectAtIndex:i];
+
+ proceed
+ = (*jniEnv)->CallBooleanMethod(
+ jniEnv,
+ callback, callbackMethodID,
+ person);
+ if ((JNI_FALSE == proceed)
+ || (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv)))
+ break;
+ }
+
+ [autoreleasePool release];
+}
+
+/*
+ * Class: net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery
+ * Method: ABRecord_uniqueId
+ * Signature: (J)Ljava/lang/String;
+ */
+JNIEXPORT jstring JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_ABRecord_1uniqueId
+ (JNIEnv *jniEnv, jclass clazz, jlong record)
+{
+ return (*jniEnv)->NewStringUTF(jniEnv, [[(ABRecord *)record uniqueId] UTF8String]);
+}
+
+NSString *JavaStringToNSString(JNIEnv *env, jstring aString)
+{
+ if(aString == NULL)
+ return nil;
+
+ const jchar *chars = (*env)->GetStringChars(env, aString, NULL);
+ NSString *resultString = [NSString stringWithCharacters:(UniChar *)chars length:(*env)->GetStringLength(env, aString)];
+ (*env)->ReleaseStringChars(env, aString, chars);
+ return resultString;
+}
+
+/*
+ * Class: net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery
+ * Method: setProperty
+ * Signature: (Ljava/lang/String;JLjava/lang/String;Ljava/lang/Object;)Z
+ */
+JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_setProperty
+ (JNIEnv *jniEnv, jclass clazz, jstring id, jlong prop, jstring subProperty, jobject value)
+{
+ void* data;
+ ABAddressBook *addressBook;
+ ABRecord *r;
+ NSAutoreleasePool *autoreleasePool;
+ NSString *property;
+ BOOL res = FALSE;
+ int i;
+
+ autoreleasePool = [[NSAutoreleasePool alloc] init];
+
+ addressBook = [ABAddressBook sharedAddressBook];
+ r = [addressBook recordForUniqueId:JavaStringToNSString(jniEnv, id)];
+
+ property = (NSString *)prop;
+
+ if(property == kABFirstNameProperty
+ || property == kABLastNameProperty
+ || property == kABFirstNamePhoneticProperty
+ || property == kABLastNamePhoneticProperty
+ || property == kABNicknameProperty
+ || property == kABMaidenNameProperty
+ || property == kABOrganizationProperty
+ || property == kABJobTitleProperty
+ || property == kABHomePageProperty
+ || property == kABDepartmentProperty
+ || property == kABNoteProperty
+ || property == kABMiddleNameProperty
+ || property == kABMiddleNamePhoneticProperty
+ || property == kABTitleProperty
+ || property == kABSuffixProperty)
+ {
+ data = JavaStringToNSString(jniEnv, (jstring)value);
+ }
+ else if(property == kABBirthdayProperty)
+ {
+ data = [NSDate dateWithTimeIntervalSince1970:(jlong)value];
+ }
+ else if(property == kABURLsProperty
+ || property == kABCalendarURIsProperty
+ || property == kABEmailProperty
+ || property == kABRelatedNamesProperty
+ || property == kABPhoneProperty
+ || property == kABAIMInstantProperty
+ || property == kABJabberInstantProperty
+ || property == kABMSNInstantProperty
+ || property == kABYahooInstantProperty
+ || property == kABICQInstantProperty)
+ {
+ data=[[ABMutableMultiValue alloc] init];
+ jobjectArray arr = (jobjectArray)value;
+ jsize propertyCount = (*jniEnv)->GetArrayLength(jniEnv, arr);
+
+ for (i = 0; i < propertyCount; i+=2)
+ {
+ jstring value = (jstring) (*jniEnv)->GetObjectArrayElement(jniEnv, arr, i);
+ jstring label = (jstring) (*jniEnv)->GetObjectArrayElement(jniEnv, arr, i+1);
+
+ [(ABMutableMultiValue *) data
+ addValue:JavaStringToNSString(jniEnv, value)
+ withLabel:JavaStringToNSString(jniEnv, label)];
+ }
+ }
+ else if(property == kABAddressProperty)
+ {
+ jobjectArray arr = (jobjectArray)value;
+ jsize propertyCount = (*jniEnv)->GetArrayLength(jniEnv, arr);
+
+ NSMutableDictionary *addr;
+ addr = [NSMutableDictionary dictionary];
+
+ data=[[ABMutableMultiValue alloc] init];
+ NSString *subProp = NULL;
+ NSString *lastSubProp;
+ for (i = 0; i < propertyCount; i+=3)
+ {
+ jstring value
+ = (jstring) (*jniEnv)->GetObjectArrayElement(jniEnv, arr, i);
+ jstring label
+ = (jstring) (*jniEnv)->GetObjectArrayElement(jniEnv, arr, i+1);
+ jstring tmpLastSubProp
+ = (jstring) (*jniEnv)->GetObjectArrayElement(jniEnv, arr, i+2);
+ lastSubProp = JavaStringToNSString(jniEnv, tmpLastSubProp);
+ // Initiates the first sub-property value.
+ if(i == 0)
+ {
+ subProp = lastSubProp;
+ }
+
+ // If there is a change in the sub-property, then save the actual
+ // one to the address property and create a new sub-property list
+ // (Home, Work).
+ if(![lastSubProp isEqualToString: subProp])
+ {
+ [(ABMutableMultiValue *) data addValue:addr withLabel:subProp];
+ addr = [NSMutableDictionary dictionary];
+ // Sets the new current proeperty
+ subProp = lastSubProp;
+ }
+
+ //NSLog(@"key:%@, value:%@", JavaStringToNSString(jniEnv, label),
+ //JavaStringToNSString(jniEnv, value));
+ [addr setObject:JavaStringToNSString(jniEnv, value)
+ forKey:JavaStringToNSString(jniEnv, label)];
+ }
+ // Adds the last sub-property to the address book.
+ if(i > 0)
+ {
+ [(ABMutableMultiValue *) data addValue: addr withLabel: subProp];
+ }
+ }
+ //else if(property == kABOtherDatesProperty)//kABMultiDateProperty
+ else
+ {
+ data = NULL;
+ }
+
+ if(data)
+ res = [r setValue:data forProperty:(NSString *)property];
+
+ [addressBook save];
+
+ [autoreleasePool release];
+
+ return res;
+}
+
+/*
+ * Class: net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery
+ * Method: removeProperty
+ * Signature: (Ljava/lang/String;J)Z
+ */
+JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_removeProperty
+ (JNIEnv *jniEnv, jclass clazz, jstring id, jlong property)
+{
+ ABAddressBook *addressBook;
+ ABRecord *r;
+ NSAutoreleasePool *autoreleasePool;
+
+ autoreleasePool = [[NSAutoreleasePool alloc] init];
+
+ addressBook = [ABAddressBook sharedAddressBook];
+ r = [addressBook recordForUniqueId:JavaStringToNSString(jniEnv, id)];
+
+ BOOL res = [r removeValueForProperty:(NSString *)property];
+
+ [addressBook save];
+
+ [autoreleasePool release];
+
+ return res;
+}
+
+#define DEFINE_ABPERSON_PROPERTY_GETTER(property) \
+ JNIEXPORT jlong JNICALL \
+ Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_##property \
+ (JNIEnv *jniEnv, jclass clazz) \
+ { \
+ return (jlong) property; \
+ }
+
+DEFINE_ABPERSON_PROPERTY_GETTER(kABAIMInstantProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABEmailProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABFirstNameProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABFirstNamePhoneticProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABICQInstantProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABJabberInstantProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABLastNameProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABLastNamePhoneticProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABMiddleNameProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABMiddleNamePhoneticProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABMSNInstantProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABNicknameProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABOrganizationProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABPersonFlags)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABPhoneProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABYahooInstantProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABMaidenNameProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABBirthdayProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABJobTitleProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABHomePageProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABURLsProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABCalendarURIsProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABAddressProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABOtherDatesProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABOtherDateComponentsProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABRelatedNamesProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABDepartmentProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABNoteProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABTitleProperty)
+DEFINE_ABPERSON_PROPERTY_GETTER(kABSuffixProperty)
+
+#define DEFINE_ABLABEL_PROPERTY_GETTER(property) \
+ JNIEXPORT jstring JNICALL \
+ Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_##property \
+ (JNIEnv *jniEnv, jclass clazz) \
+ { \
+ return (*jniEnv)->NewStringUTF(jniEnv, [((NSString *) property) UTF8String]); \
+ }
+DEFINE_ABLABEL_PROPERTY_GETTER(kABHomePageLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABEmailWorkLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABEmailHomeLabel)
+//DEFINE_ABLABEL_PROPERTY_GETTER(kABEmailMobileMeLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressHomeLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressWorkLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABAnniversaryLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABFatherLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABMotherLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABParentLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABBrotherLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABSisterLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABChildLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABFriendLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABSpouseLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABPartnerLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABAssistantLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABManagerLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABPhoneWorkLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABPhoneHomeLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABPhoneMobileLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABPhoneMainLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABPhoneHomeFAXLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABPhoneWorkFAXLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABPhonePagerLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABAIMWorkLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABAIMHomeLabel)
+//DEFINE_ABLABEL_PROPERTY_GETTER(kABAIMMobileMeLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABJabberWorkLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABJabberHomeLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABMSNWorkLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABMSNHomeLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABYahooWorkLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABYahooHomeLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABICQWorkLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABICQHomeLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressStreetKey)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressCityKey)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressStateKey)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressZIPKey)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABAddressCountryKey)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABWorkLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABHomeLabel)
+DEFINE_ABLABEL_PROPERTY_GETTER(kABOtherLabel)
+
+static void
+MacOSXAddrBookContactQuery_idToJObject
+ (JNIEnv *jniEnv,
+ id o,
+ jobjectArray jos, jint i,
+ jclass objectClass)
+{
+ if (o)
+ {
+ jobject jo;
+
+ if ([o isKindOfClass:[NSString class]])
+ {
+ jo = (*jniEnv)->NewStringUTF(jniEnv, [((NSString *) o) UTF8String]);
+ }
+ else if ([o isKindOfClass:[ABMultiValue class]])
+ {
+ /*
+ * We changed our minds after the initial implementation and decided
+ * that we want to display not only the values but the labels as
+ * well. In order to minimize the scope of the modifications, we'll
+ * be returning each label in the same array right after its
+ * corresponding value.
+ */
+ ABMultiValue *mv = (ABMultiValue *) o;
+ NSUInteger mvCount = [mv count];
+ jobjectArray joArray
+ = (*jniEnv)->NewObjectArray(
+ jniEnv,
+ mvCount * 2 /* value, label */,
+ objectClass, NULL);
+
+ jo = joArray;
+ if (joArray)
+ {
+ NSUInteger j, j2;
+
+ for (j = 0; j < mvCount; j++)
+ {
+ j2 = j * 2;
+
+ //NSLog(@"key:%@, label:%@",
+ // [mv valueAtIndex:j], [mv labelAtIndex:j]);
+
+ MacOSXAddrBookContactQuery_idToJObject(
+ jniEnv,
+ [mv valueAtIndex:j],
+ joArray, j2,
+ objectClass);
+ if (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv))
+ {
+ jo = NULL;
+ break;
+ }
+ MacOSXAddrBookContactQuery_idToJObject(
+ jniEnv,
+ [mv labelAtIndex:j],
+ joArray, j2 + 1,
+ objectClass);
+ if (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv))
+ {
+ jo = NULL;
+ break;
+ }
+ }
+ }
+ }
+ else if ([o isKindOfClass:[NSNumber class]])
+ {
+ jclass longClass = (*jniEnv)->FindClass(jniEnv, "java/lang/Long");
+
+ jo = NULL;
+ if (longClass)
+ {
+ jmethodID longMethodID
+ = (*jniEnv)->GetMethodID(
+ jniEnv,
+ longClass, "<init>", "(J)V");
+
+ if (longMethodID)
+ {
+ jo
+ = (*jniEnv)->NewObject(
+ jniEnv,
+ longClass, longMethodID,
+ (jlong) ([((NSNumber *) o) longValue]));
+ }
+ }
+ }
+ else if ([o isKindOfClass:[NSDictionary class]])
+ {
+ NSDictionary *dict = (NSDictionary *)o;
+
+ NSUInteger dictCount = [dict count];
+ jobjectArray joArray
+ = (*jniEnv)->NewObjectArray(
+ jniEnv,
+ dictCount * 2, objectClass, NULL);
+ jo = joArray;
+ if (joArray)
+ {
+ NSEnumerator *enumerator = [dict keyEnumerator];
+ id key;
+ NSUInteger j, j2;
+ j = 0;
+ while ((key = [enumerator nextObject]))
+ {
+ //NSLog(@"key:%@, value:%@", key, [dict objectForKey: key]);
+
+ j2 = j * 2;
+
+ MacOSXAddrBookContactQuery_idToJObject(
+ jniEnv,
+ [dict objectForKey: key],
+ joArray, j2,
+ objectClass);
+ if (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv))
+ {
+ jo = NULL;
+ break;
+ }
+ MacOSXAddrBookContactQuery_idToJObject(
+ jniEnv,
+ key,
+ joArray, j2 + 1,
+ objectClass);
+ if (JNI_TRUE == (*jniEnv)->ExceptionCheck(jniEnv))
+ {
+ jo = NULL;
+ break;
+ }
+
+ j++;
+ }
+ }
+ }
+ else
+ {
+ //NSLog(@"type:%@", NSStringFromClass([o class]));
+ jo = NULL;
+ }
+ if (jo)
+ (*jniEnv)->SetObjectArrayElement(jniEnv, jos, i, jo);
+ }
+}
+
+JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_deleteContact
+ (JNIEnv *jniEnv, jclass clazz, jstring id)
+{
+ ABAddressBook *addressBook;
+ ABRecord *r;
+ NSAutoreleasePool *autoreleasePool;
+
+ autoreleasePool = [[NSAutoreleasePool alloc] init];
+
+ addressBook = [ABAddressBook sharedAddressBook];
+ r = [addressBook recordForUniqueId:JavaStringToNSString(jniEnv, id)];
+
+ BOOL res = [addressBook removeRecord: r];
+
+ [addressBook save];
+
+ [autoreleasePool release];
+
+ return res;
+}
+
+JNIEXPORT jstring JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_createContact
+ (JNIEnv *jniEnv, jclass clazz)
+{
+ ABAddressBook *addressBook;
+ NSAutoreleasePool *autoreleasePool;
+
+ autoreleasePool = [[NSAutoreleasePool alloc] init];
+
+ addressBook = [ABAddressBook sharedAddressBook];
+ ABPerson* person = [[ABPerson alloc] initWithAddressBook:addressBook];
+
+ [addressBook save];
+
+ [autoreleasePool release];
+
+ return
+ Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_ABRecord_1uniqueId(jniEnv, clazz, (jlong) person);
+}
+
+JNIEXPORT jlong JNICALL Java_net_java_sip_communicator_plugin_addrbook_macosx_MacOSXAddrBookContactQuery_getContactPointer
+ (JNIEnv *jniEnv, jclass clazz, jstring id)
+{
+ ABAddressBook *addressBook;
+ ABRecord *r;
+ NSAutoreleasePool *autoreleasePool;
+
+ autoreleasePool = [[NSAutoreleasePool alloc] init];
+
+ addressBook = [ABAddressBook sharedAddressBook];
+ r = [addressBook recordForUniqueId:JavaStringToNSString(jniEnv, id)];
+
+ [addressBook save];
+
+ [autoreleasePool release];
+
+ return (jlong) r;
+}
diff --git a/src/native/addrbook/msoutlook/MAPINotification.cxx b/src/native/addrbook/msoutlook/MAPINotification.cxx
index 78c039f..3e0d749 100644
--- a/src/native/addrbook/msoutlook/MAPINotification.cxx
+++ b/src/native/addrbook/msoutlook/MAPINotification.cxx
@@ -1,555 +1,581 @@
-/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-#include "MAPINotification.h"
-
-#include "MAPISession.h"
-#include "net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactSourceService.h"
-#include "net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.h"
-
-#include <mapidefs.h>
-#include <stdio.h>
-#include <unknwn.h>
-
-/**
- * Manages notification for the message data base (used to get the list of
- * contact).
- *
- * @author Vincent Lucas
- */
-
-void MAPINotification_registerNotifyAllMsgStores(LPMAPISESSION mapiSession);
-
-void MAPINotification_unregisterNotifyAllMsgStores(void);
-
-
-
-
-
-/**
- * The List of events we want to retrieve.
- */
-static ULONG MAPINotification_EVENT_MASK
- = fnevObjectCreated
- | fnevObjectDeleted
- | fnevObjectModified
- | fnevObjectMoved;
-
-static LPMDB * MAPINotification_msgStores = NULL;
-static ULONG * MAPINotification_msgStoresConnection = NULL;
-static LPMAPITABLE MAPINotification_msgStoresTable = NULL;
-static ULONG MAPINotification_msgStoresTableConnection = 0;
-static ULONG MAPINotification_nbMsgStores = 0;
-static jmethodID MAPINotification_notificationsDelegateMethodIdDeleted = NULL;
-static jmethodID MAPINotification_notificationsDelegateMethodIdInserted = NULL;
-static jmethodID MAPINotification_notificationsDelegateMethodIdUpdated = NULL;
-static jobject MAPINotification_notificationsDelegateObject = NULL;
-static ULONG MAPINotification_openEntryUlFlags = MAPI_BEST_ACCESS;
-static JavaVM * MAPINotification_VM = NULL;
-
-void MAPINotification_callDeletedMethod(LPSTR iUnknown);
-void MAPINotification_callInsertedMethod(LPUNKNOWN iUnknown);
-void MAPINotification_callUpdatedMethod(LPUNKNOWN iUnknown);
-LPUNKNOWN
-MAPINotification_openEntry
- (ULONG cbEntryID, LPENTRYID lpEntryID, LPVOID lpvContext);
-ULONG MAPINotification_registerNotifyTable(LPMAPITABLE iUnknown);
-LONG STDAPICALLTYPE MAPINotification_tableChanged
- (LPVOID lpvContext, ULONG cNotifications, LPNOTIFICATION lpNotifications);
-
-/**
- * Calls back the java side when a contact is deleted.
- *
- * @param iUnknown The string representation of the entry id of the deleted
- * contact.
- */
-void MAPINotification_callDeletedMethod(LPSTR iUnknown)
-{
- JNIEnv *tmpJniEnv = NULL;
-
- if(MAPINotification_VM
- ->AttachCurrentThreadAsDaemon((void**) &tmpJniEnv, NULL) == 0)
- {
- jstring value = tmpJniEnv->NewStringUTF(iUnknown);
-
- tmpJniEnv->CallBooleanMethod(
- MAPINotification_notificationsDelegateObject,
- MAPINotification_notificationsDelegateMethodIdDeleted,
- value);
-
- MAPINotification_VM->DetachCurrentThread();
- }
-}
-
-/**
- * Calls back the java side when a contact is inserted.
- *
- * @param iUnknown A pointer to the newly created contact.
- */
-void MAPINotification_callInsertedMethod(LPUNKNOWN iUnknown)
-{
- JNIEnv *tmpJniEnv = NULL;
-
- if(MAPINotification_VM
- ->AttachCurrentThreadAsDaemon((void**) &tmpJniEnv, NULL) == 0)
- {
- tmpJniEnv->CallBooleanMethod(
- MAPINotification_notificationsDelegateObject,
- MAPINotification_notificationsDelegateMethodIdInserted,
- iUnknown);
-
- MAPINotification_VM->DetachCurrentThread();
- }
-}
-
-/**
- * Calls back the java side when a contact is updated.
- *
- * @param iUnknown A pointer to the updated contact.
- */
-void MAPINotification_callUpdatedMethod(LPUNKNOWN iUnknown)
-{
- JNIEnv *tmpJniEnv = NULL;
-
- if(MAPINotification_VM
- ->AttachCurrentThreadAsDaemon((void**) &tmpJniEnv, NULL) == 0)
- {
- tmpJniEnv->CallBooleanMethod(
- MAPINotification_notificationsDelegateObject,
- MAPINotification_notificationsDelegateMethodIdUpdated,
- iUnknown);
-
- MAPINotification_VM->DetachCurrentThread();
- }
-}
-
-/**
- * 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 MAPINotification_onNotify
- (LPVOID lpvContext, ULONG cNotifications, LPNOTIFICATION lpNotifications)
-{
- for(unsigned int i = 0; i < cNotifications; ++i)
- {
- LPUNKNOWN iUnknown = NULL;
- if(lpvContext != NULL)
- {
- iUnknown = MAPINotification_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)
- {
- MAPINotification_callInsertedMethod(iUnknown);
- }
- }
- // A contact has been Modified
- else if(lpNotifications[i].ulEventType == fnevObjectModified)
- {
- if(lpNotifications[i].info.obj.ulObjType == MAPI_MESSAGE)
- {
- MAPINotification_callUpdatedMethod(iUnknown);
- }
- }
- // A contact has been deleted.
- else if(lpNotifications[i].ulEventType == fnevObjectDeleted)
- {
- if(lpvContext != NULL)
- {
- LPSTR entryIdStr = (LPSTR)::malloc(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)
- {
- MAPINotification_callDeletedMethod(entryIdStr);
- }
-
- ::free(entryIdStr);
- entryIdStr = NULL;
- }
- }
- // A contact has been deleted (moved to trash).
- else if(lpNotifications[i].ulEventType == fnevObjectMoved)
- {
- if(lpvContext != NULL)
- {
- LPSTR entryIdStr
- = (LPSTR)::malloc(lpNotifications[i].info.obj.cbEntryID * 2 + 1);
- HexFromBin(
- (LPBYTE) lpNotifications[i].info.obj.lpEntryID,
- lpNotifications[i].info.obj.cbEntryID,
- entryIdStr);
- LPSTR parentEntryIdStr
- = (LPSTR)::malloc(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);
- LPSTR wasteBasketEntryIdStr
- = (LPSTR)::malloc(wasteBasketProps[0].Value.bin.cb * 2 + 1);
- HexFromBin(
- (LPBYTE) wasteBasketProps[0].Value.bin.lpb,
- wasteBasketProps[0].Value.bin.cb,
- wasteBasketEntryIdStr);
-
- MAPINotification_openEntry(
- lpNotifications[i].info.obj.cbParentID,
- lpNotifications[i].info.obj.lpParentID,
- lpvContext);
-
-
- if(lpNotifications[i].info.obj.ulObjType == MAPI_MESSAGE
- && strcmp(parentEntryIdStr, wasteBasketEntryIdStr) == 0)
- {
- MAPINotification_callDeletedMethod(entryIdStr);
- }
-
- ::free(entryIdStr);
- entryIdStr = NULL;
- ::free(parentEntryIdStr);
- parentEntryIdStr = NULL;
- ::free(wasteBasketEntryIdStr);
- wasteBasketEntryIdStr = NULL;
- }
- }
-
- if(iUnknown != NULL)
- {
- iUnknown->Release();
- }
- }
-
- // A client must always return a S_OK.
- return S_OK;
-}
-
-/**
- * Opens an object from its entry id.
- */
-LPUNKNOWN
-MAPINotification_openEntry
- (ULONG cbEntryID, LPENTRYID lpEntryID, LPVOID lpvContext)
-{
- if(lpvContext != NULL)
- {
- LPUNKNOWN iUnknown;
- ULONG objType;
-
- HRESULT hResult =
- ((LPMDB) lpvContext)->OpenEntry(
- cbEntryID,
- lpEntryID,
- NULL,
- MAPINotification_openEntryUlFlags,
- &objType,
- &iUnknown);
- if (HR_SUCCEEDED(hResult))
- {
- return iUnknown;
- }
- }
- return NULL;
-}
-
-/**
- * Registers java callback functions when a contact is deleted, inserted or
- * updated.
- *
- * @param jniEnv The Java native interface environment.
- * @param mapiSession The current MAPI session.
- * @param notificationsDelegate The object called when a notification is fired
- * (contact updated, inserted or deleted).
- */
-void
-MAPINotification_registerNotificationsDelegate
- (JNIEnv *jniEnv, LPMAPISESSION mapiSession, jobject notificationsDelegate)
-{
- if(jniEnv->GetJavaVM(&MAPINotification_VM) < 0)
- {
- fprintf(stderr, "Failed to get the Java VM\n");
- fflush(stderr);
- }
-
- // If this function is called once again, then check first to unregister
- // previous notification advises.
- MAPINotification_unregisterNotificationsDelegate(jniEnv);
-
- if(notificationsDelegate != NULL && mapiSession != NULL)
- {
- MAPINotification_notificationsDelegateObject
- = jniEnv->NewGlobalRef(notificationsDelegate);
- if(MAPINotification_notificationsDelegateObject != NULL)
- {
- jclass callbackClass
- = jniEnv->GetObjectClass(notificationsDelegate);
- MAPINotification_notificationsDelegateMethodIdInserted
- = jniEnv->GetMethodID(
- callbackClass,
- "inserted",
- "(J)V");
- MAPINotification_notificationsDelegateMethodIdUpdated
- = jniEnv->GetMethodID(
- callbackClass,
- "updated",
- "(J)V");
- MAPINotification_notificationsDelegateMethodIdDeleted
- = jniEnv->GetMethodID(
- callbackClass,
- "deleted",
- "(Ljava/lang/String;)V");
- // Register the notification of contact changed, created and
- // deleted.
- MAPINotification_registerNotifyAllMsgStores(mapiSession);
- }
- }
-}
-
-/**
- * Opens all the message store and register to notifications.
- */
-void MAPINotification_registerNotifyAllMsgStores(LPMAPISESSION mapiSession)
-{
- HRESULT hResult;
-
- hResult = mapiSession->GetMsgStoresTable(
- 0,
- &MAPINotification_msgStoresTable);
- if(HR_SUCCEEDED(hResult) && MAPINotification_msgStoresTable)
- {
- MAPINotification_msgStoresTableConnection
- = MAPINotification_registerNotifyTable(
- MAPINotification_msgStoresTable);
- hResult = MAPINotification_msgStoresTable->SeekRow(
- BOOKMARK_BEGINNING,
- 0,
- NULL);
- if (HR_SUCCEEDED(hResult))
- {
- LPSRowSet rows;
- hResult = HrQueryAllRows(
- MAPINotification_msgStoresTable,
- NULL,
- NULL,
- NULL,
- 0,
- &rows);
- if (HR_SUCCEEDED(hResult))
- {
- MAPINotification_nbMsgStores = rows->cRows;
- MAPINotification_msgStores
- = (LPMDB*) malloc(rows->cRows * sizeof(LPMDB));
- memset(
- MAPINotification_msgStores,
- 0,
- rows->cRows * sizeof(LPMDB));
- MAPINotification_msgStoresConnection
- = (ULONG*) malloc(rows->cRows * sizeof(ULONG));
- memset(
- MAPINotification_msgStoresConnection,
- 0,
- rows->cRows * sizeof(ULONG));
-
- if(MAPINotification_msgStores != NULL
- && MAPINotification_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
- | MAPINotification_openEntryUlFlags,
- &MAPINotification_msgStores[r]);
- if (HR_SUCCEEDED(hResult))
- {
- MAPINotification_msgStoresConnection[r]
- = MAPINotification_registerNotifyMessageDataBase(
- MAPINotification_msgStores[r]);
- }
- }
- }
- }
- FreeProws(rows);
- }
- }
- }
-}
-
-
-/**
- * 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 MAPINotification_registerNotifyMessageDataBase(LPMDB iUnknown)
-{
- LPMAPIADVISESINK adviseSink;
-
- HrAllocAdviseSink(&MAPINotification_onNotify, iUnknown, &adviseSink);
-
- ULONG nbConnection = 0;
- iUnknown->Advise(
- (ULONG) 0,
- (LPENTRYID) NULL,
- MAPINotification_EVENT_MASK,
- adviseSink,
- (ULONG *) &nbConnection);
-
- return nbConnection;
-}
-
-/**
- * Registers a callback function for when the message store table changes.
- */
-ULONG MAPINotification_registerNotifyTable(LPMAPITABLE iUnknown)
-{
- LPMAPIADVISESINK adviseSink;
- HrAllocAdviseSink(
- &MAPINotification_tableChanged,
- iUnknown,
- &adviseSink);
- ULONG nbConnection = 0;
- iUnknown->Advise(
- fnevTableModified,
- adviseSink,
- (ULONG *) &nbConnection);
-
- return nbConnection;
-}
-
-/**
- * Function called when a message store table changed.
- */
-LONG
-STDAPICALLTYPE MAPINotification_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.
- MAPINotification_unregisterNotifyAllMsgStores();
- MAPINotification_registerNotifyAllMsgStores(
- MAPISession_getMapiSession());
- }
-
- // A client must always return a S_OK.
- return S_OK;
-}
-
-/**
- * Unregisters java callback functions when a contact is deleted, inserted or
- * updated.
- *
- * @param jniEnv The Java native interface environment.
- */
-void MAPINotification_unregisterNotificationsDelegate(JNIEnv *jniEnv)
-{
- MAPINotification_unregisterNotifyAllMsgStores();
- if(MAPINotification_notificationsDelegateObject != NULL)
- {
- jniEnv->DeleteGlobalRef(MAPINotification_notificationsDelegateObject);
- MAPINotification_notificationsDelegateObject = NULL;
- MAPINotification_notificationsDelegateMethodIdInserted = NULL;
- MAPINotification_notificationsDelegateMethodIdUpdated = NULL;
- MAPINotification_notificationsDelegateMethodIdDeleted = NULL;
- }
-}
-
-/**
- * Frees all memory used to keep in mind the list of the message store and
- * unregister each of them from the notifications.
- */
-void MAPINotification_unregisterNotifyAllMsgStores(void)
-{
- if(MAPINotification_msgStoresConnection != NULL)
- {
- for(unsigned int i = 0; i < MAPINotification_nbMsgStores; ++i)
- {
- if(MAPINotification_msgStoresConnection[i] != 0)
- {
- MAPINotification_msgStores[i]->Unadvise(
- MAPINotification_msgStoresConnection[i]);
- }
- }
- free(MAPINotification_msgStoresConnection);
- MAPINotification_msgStoresConnection = NULL;
- }
-
- if(MAPINotification_msgStores != NULL)
- {
- for(unsigned int i = 0; i < MAPINotification_nbMsgStores; ++i)
- {
- if(MAPINotification_msgStores[i] != NULL)
- {
- MAPINotification_msgStores[i]->Release();
- }
- }
- free(MAPINotification_msgStores);
- MAPINotification_msgStores = NULL;
- }
-
- if(MAPINotification_msgStoresTable != NULL)
- {
- MAPINotification_msgStoresTable->Unadvise(
- MAPINotification_msgStoresTableConnection);
- MAPINotification_msgStoresTable->Release();
- MAPINotification_msgStoresTable = NULL;
- }
-}
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+#include "MAPINotification.h"
+
+#include "MAPISession.h"
+#include "net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactSourceService.h"
+#include "net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.h"
+
+#include <mapidefs.h>
+#include <stdio.h>
+#include <unknwn.h>
+
+/**
+ * Manages notification for the message data base (used to get the list of
+ * contact).
+ *
+ * @author Vincent Lucas
+ */
+
+void MAPINotification_registerNotifyAllMsgStores(LPMAPISESSION mapiSession);
+
+void MAPINotification_unregisterNotifyAllMsgStores(void);
+
+/**
+ * The List of events we want to retrieve.
+ */
+static ULONG MAPINotification_EVENT_MASK
+ = fnevObjectCreated
+ | fnevObjectDeleted
+ | fnevObjectModified
+ | fnevObjectMoved;
+
+static LPMDB * MAPINotification_msgStores = NULL;
+static ULONG * MAPINotification_msgStoresConnection = NULL;
+static LPMAPITABLE MAPINotification_msgStoresTable = NULL;
+static ULONG MAPINotification_msgStoresTableConnection = 0;
+static ULONG MAPINotification_nbMsgStores = 0;
+static jmethodID MAPINotification_notificationsDelegateMethodIdDeleted = NULL;
+static jmethodID MAPINotification_notificationsDelegateMethodIdInserted = NULL;
+static jmethodID MAPINotification_notificationsDelegateMethodIdUpdated = NULL;
+static jobject MAPINotification_notificationsDelegateObject = NULL;
+static ULONG MAPINotification_openEntryUlFlags = MAPI_BEST_ACCESS;
+static JavaVM * MAPINotification_VM = NULL;
+
+void MAPINotification_callDeletedMethod(LPSTR iUnknown);
+void MAPINotification_callInsertedMethod(LPSTR iUnknown);
+void MAPINotification_callUpdatedMethod(LPSTR iUnknown);
+LPUNKNOWN
+MAPINotification_openEntry
+ (ULONG cbEntryID, LPENTRYID lpEntryID, LPVOID lpvContext);
+ULONG MAPINotification_registerNotifyTable(LPMAPITABLE iUnknown);
+LONG STDAPICALLTYPE MAPINotification_tableChanged
+ (LPVOID lpvContext, ULONG cNotifications, LPNOTIFICATION lpNotifications);
+
+/**
+ * Calls back the java side when a contact is deleted.
+ *
+ * @param iUnknown The string representation of the entry id of the deleted
+ * contact.
+ */
+void MAPINotification_callDeletedMethod(LPSTR iUnknown)
+{
+ JNIEnv *tmpJniEnv = NULL;
+
+ if(MAPINotification_VM
+ ->AttachCurrentThreadAsDaemon((void**) &tmpJniEnv, NULL) == 0)
+ {
+ jstring value = tmpJniEnv->NewStringUTF(iUnknown);
+
+ tmpJniEnv->CallBooleanMethod(
+ MAPINotification_notificationsDelegateObject,
+ MAPINotification_notificationsDelegateMethodIdDeleted,
+ value);
+
+ MAPINotification_VM->DetachCurrentThread();
+ }
+}
+
+/**
+ * Calls back the java side when a contact is inserted.
+ *
+ * @param iUnknown A pointer to the newly created contact.
+ */
+void MAPINotification_callInsertedMethod(LPSTR iUnknown)
+{
+ JNIEnv *tmpJniEnv = NULL;
+
+ if(MAPINotification_VM
+ ->AttachCurrentThreadAsDaemon((void**) &tmpJniEnv, NULL) == 0)
+ {
+ jstring value = tmpJniEnv->NewStringUTF(iUnknown);
+
+ tmpJniEnv->CallBooleanMethod(
+ MAPINotification_notificationsDelegateObject,
+ MAPINotification_notificationsDelegateMethodIdInserted,
+ value);
+
+ MAPINotification_VM->DetachCurrentThread();
+ }
+}
+
+/**
+ * Calls back the java side when a contact is updated.
+ *
+ * @param iUnknown A pointer to the updated contact.
+ */
+void MAPINotification_callUpdatedMethod(LPSTR iUnknown)
+{
+ JNIEnv *tmpJniEnv = NULL;
+
+ if(MAPINotification_VM
+ ->AttachCurrentThreadAsDaemon((void**) &tmpJniEnv, NULL) == 0)
+ {
+ jstring value = tmpJniEnv->NewStringUTF(iUnknown);
+
+ tmpJniEnv->CallBooleanMethod(
+ MAPINotification_notificationsDelegateObject,
+ MAPINotification_notificationsDelegateMethodIdUpdated,
+ value);
+
+ MAPINotification_VM->DetachCurrentThread();
+ }
+}
+
+/**
+ * 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 MAPINotification_onNotify
+ (LPVOID lpvContext, ULONG cNotifications, LPNOTIFICATION lpNotifications)
+{
+ for(unsigned int i = 0; i < cNotifications; ++i)
+ {
+ LPUNKNOWN iUnknown = NULL;
+ if(lpvContext != NULL)
+ {
+ iUnknown = MAPINotification_openEntry(
+ lpNotifications[i].info.obj.cbEntryID,
+ lpNotifications[i].info.obj.lpEntryID,
+ lpvContext);
+ }
+
+ // A contact has been created
+ if(lpNotifications[i].ulEventType == fnevObjectCreated)
+ {
+ if(lpvContext != NULL)
+ {
+ LPSTR entryIdStr = (LPSTR)::malloc(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)
+ {
+ MAPINotification_callInsertedMethod(entryIdStr);
+ }
+
+ ::free(entryIdStr);
+ entryIdStr = NULL;
+ }
+ }
+ // A contact has been Modified
+ else if(lpNotifications[i].ulEventType == fnevObjectModified)
+ {
+ if(lpvContext != NULL)
+ {
+ LPSTR entryIdStr = (LPSTR)::malloc(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)
+ {
+ MAPINotification_callUpdatedMethod(entryIdStr);
+ }
+
+ ::free(entryIdStr);
+ entryIdStr = NULL;
+ }
+ }
+ // A contact has been deleted.
+ else if(lpNotifications[i].ulEventType == fnevObjectDeleted)
+ {
+ if(lpvContext != NULL)
+ {
+ LPSTR entryIdStr = (LPSTR)::malloc(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)
+ {
+ MAPINotification_callDeletedMethod(entryIdStr);
+ }
+
+ ::free(entryIdStr);
+ entryIdStr = NULL;
+ }
+ }
+ // A contact has been deleted (moved to trash).
+ else if(lpNotifications[i].ulEventType == fnevObjectMoved)
+ {
+ if(lpvContext != NULL)
+ {
+ LPSTR entryIdStr
+ = (LPSTR)::malloc(lpNotifications[i].info.obj.cbEntryID * 2 + 1);
+ HexFromBin(
+ (LPBYTE) lpNotifications[i].info.obj.lpEntryID,
+ lpNotifications[i].info.obj.cbEntryID,
+ entryIdStr);
+ LPSTR parentEntryIdStr
+ = (LPSTR)::malloc(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);
+ LPSTR wasteBasketEntryIdStr
+ = (LPSTR)::malloc(wasteBasketProps[0].Value.bin.cb * 2 + 1);
+ HexFromBin(
+ (LPBYTE) wasteBasketProps[0].Value.bin.lpb,
+ wasteBasketProps[0].Value.bin.cb,
+ wasteBasketEntryIdStr);
+
+ MAPINotification_openEntry(
+ lpNotifications[i].info.obj.cbParentID,
+ lpNotifications[i].info.obj.lpParentID,
+ lpvContext);
+
+
+ if(lpNotifications[i].info.obj.ulObjType == MAPI_MESSAGE
+ && strcmp(parentEntryIdStr, wasteBasketEntryIdStr) == 0)
+ {
+ MAPINotification_callDeletedMethod(entryIdStr);
+ }
+
+ ::free(entryIdStr);
+ entryIdStr = NULL;
+ ::free(parentEntryIdStr);
+ parentEntryIdStr = NULL;
+ ::free(wasteBasketEntryIdStr);
+ wasteBasketEntryIdStr = NULL;
+ }
+ }
+
+ if(iUnknown != NULL)
+ {
+ iUnknown->Release();
+ }
+ }
+
+ // A client must always return a S_OK.
+ return S_OK;
+}
+
+/**
+ * Opens an object from its entry id.
+ */
+LPUNKNOWN
+MAPINotification_openEntry
+ (ULONG cbEntryID, LPENTRYID lpEntryID, LPVOID lpvContext)
+{
+ if(lpvContext != NULL)
+ {
+ LPUNKNOWN iUnknown;
+ ULONG objType;
+
+ HRESULT hResult =
+ ((LPMDB) lpvContext)->OpenEntry(
+ cbEntryID,
+ lpEntryID,
+ NULL,
+ MAPINotification_openEntryUlFlags,
+ &objType,
+ &iUnknown);
+ if (HR_SUCCEEDED(hResult))
+ {
+ return iUnknown;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * Registers java callback functions when a contact is deleted, inserted or
+ * updated.
+ *
+ * @param jniEnv The Java native interface environment.
+ * @param mapiSession The current MAPI session.
+ * @param notificationsDelegate The object called when a notification is fired
+ * (contact updated, inserted or deleted).
+ */
+void
+MAPINotification_registerNotificationsDelegate
+ (JNIEnv *jniEnv, LPMAPISESSION mapiSession, jobject notificationsDelegate)
+{
+ if(jniEnv->GetJavaVM(&MAPINotification_VM) < 0)
+ {
+ fprintf(stderr, "Failed to get the Java VM\n");
+ fflush(stderr);
+ }
+
+ // If this function is called once again, then check first to unregister
+ // previous notification advises.
+ MAPINotification_unregisterNotificationsDelegate(jniEnv);
+
+ if(notificationsDelegate != NULL && mapiSession != NULL)
+ {
+ MAPINotification_notificationsDelegateObject
+ = jniEnv->NewGlobalRef(notificationsDelegate);
+ if(MAPINotification_notificationsDelegateObject != NULL)
+ {
+ jclass callbackClass
+ = jniEnv->GetObjectClass(notificationsDelegate);
+ MAPINotification_notificationsDelegateMethodIdInserted
+ = jniEnv->GetMethodID(
+ callbackClass,
+ "inserted",
+ "(Ljava/lang/String;)V");
+ MAPINotification_notificationsDelegateMethodIdUpdated
+ = jniEnv->GetMethodID(
+ callbackClass,
+ "updated",
+ "(Ljava/lang/String;)V");
+ MAPINotification_notificationsDelegateMethodIdDeleted
+ = jniEnv->GetMethodID(
+ callbackClass,
+ "deleted",
+ "(Ljava/lang/String;)V");
+ // Register the notification of contact changed, created and
+ // deleted.
+ MAPINotification_registerNotifyAllMsgStores(mapiSession);
+ }
+ }
+}
+
+/**
+ * Opens all the message store and register to notifications.
+ */
+void MAPINotification_registerNotifyAllMsgStores(LPMAPISESSION mapiSession)
+{
+ HRESULT hResult;
+
+ hResult = mapiSession->GetMsgStoresTable(
+ 0,
+ &MAPINotification_msgStoresTable);
+ if(HR_SUCCEEDED(hResult) && MAPINotification_msgStoresTable)
+ {
+ MAPINotification_msgStoresTableConnection
+ = MAPINotification_registerNotifyTable(
+ MAPINotification_msgStoresTable);
+ hResult = MAPINotification_msgStoresTable->SeekRow(
+ BOOKMARK_BEGINNING,
+ 0,
+ NULL);
+ if (HR_SUCCEEDED(hResult))
+ {
+ LPSRowSet rows;
+ hResult = HrQueryAllRows(
+ MAPINotification_msgStoresTable,
+ NULL,
+ NULL,
+ NULL,
+ 0,
+ &rows);
+ if (HR_SUCCEEDED(hResult))
+ {
+ MAPINotification_nbMsgStores = rows->cRows;
+ MAPINotification_msgStores
+ = (LPMDB*) malloc(rows->cRows * sizeof(LPMDB));
+ memset(
+ MAPINotification_msgStores,
+ 0,
+ rows->cRows * sizeof(LPMDB));
+ MAPINotification_msgStoresConnection
+ = (ULONG*) malloc(rows->cRows * sizeof(ULONG));
+ memset(
+ MAPINotification_msgStoresConnection,
+ 0,
+ rows->cRows * sizeof(ULONG));
+
+ if(MAPINotification_msgStores != NULL
+ && MAPINotification_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
+ | MAPINotification_openEntryUlFlags,
+ &MAPINotification_msgStores[r]);
+ if (HR_SUCCEEDED(hResult))
+ {
+ MAPINotification_msgStoresConnection[r]
+ = MAPINotification_registerNotifyMessageDataBase(
+ MAPINotification_msgStores[r]);
+ }
+ }
+ }
+ }
+ FreeProws(rows);
+ }
+ }
+ }
+}
+
+
+/**
+ * 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 MAPINotification_registerNotifyMessageDataBase(LPMDB iUnknown)
+{
+ LPMAPIADVISESINK adviseSink;
+
+ HrAllocAdviseSink(&MAPINotification_onNotify, iUnknown, &adviseSink);
+
+ ULONG nbConnection = 0;
+ iUnknown->Advise(
+ (ULONG) 0,
+ (LPENTRYID) NULL,
+ MAPINotification_EVENT_MASK,
+ adviseSink,
+ (ULONG *) &nbConnection);
+
+ return nbConnection;
+}
+
+/**
+ * Registers a callback function for when the message store table changes.
+ */
+ULONG MAPINotification_registerNotifyTable(LPMAPITABLE iUnknown)
+{
+ LPMAPIADVISESINK adviseSink;
+ HrAllocAdviseSink(
+ &MAPINotification_tableChanged,
+ iUnknown,
+ &adviseSink);
+ ULONG nbConnection = 0;
+ iUnknown->Advise(
+ fnevTableModified,
+ adviseSink,
+ (ULONG *) &nbConnection);
+
+ return nbConnection;
+}
+
+/**
+ * Function called when a message store table changed.
+ */
+LONG
+STDAPICALLTYPE MAPINotification_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.
+ MAPINotification_unregisterNotifyAllMsgStores();
+ MAPINotification_registerNotifyAllMsgStores(
+ MAPISession_getMapiSession());
+ }
+
+ // A client must always return a S_OK.
+ return S_OK;
+}
+
+/**
+ * Unregisters java callback functions when a contact is deleted, inserted or
+ * updated.
+ *
+ * @param jniEnv The Java native interface environment.
+ */
+void MAPINotification_unregisterNotificationsDelegate(JNIEnv *jniEnv)
+{
+ MAPINotification_unregisterNotifyAllMsgStores();
+ if(MAPINotification_notificationsDelegateObject != NULL)
+ {
+ jniEnv->DeleteGlobalRef(MAPINotification_notificationsDelegateObject);
+ MAPINotification_notificationsDelegateObject = NULL;
+ MAPINotification_notificationsDelegateMethodIdInserted = NULL;
+ MAPINotification_notificationsDelegateMethodIdUpdated = NULL;
+ MAPINotification_notificationsDelegateMethodIdDeleted = NULL;
+ }
+}
+
+/**
+ * Frees all memory used to keep in mind the list of the message store and
+ * unregister each of them from the notifications.
+ */
+void MAPINotification_unregisterNotifyAllMsgStores(void)
+{
+ if(MAPINotification_msgStoresConnection != NULL)
+ {
+ for(unsigned int i = 0; i < MAPINotification_nbMsgStores; ++i)
+ {
+ if(MAPINotification_msgStoresConnection[i] != 0)
+ {
+ MAPINotification_msgStores[i]->Unadvise(
+ MAPINotification_msgStoresConnection[i]);
+ }
+ }
+ free(MAPINotification_msgStoresConnection);
+ MAPINotification_msgStoresConnection = NULL;
+ }
+
+ if(MAPINotification_msgStores != NULL)
+ {
+ for(unsigned int i = 0; i < MAPINotification_nbMsgStores; ++i)
+ {
+ if(MAPINotification_msgStores[i] != NULL)
+ {
+ MAPINotification_msgStores[i]->Release();
+ }
+ }
+ free(MAPINotification_msgStores);
+ MAPINotification_msgStores = NULL;
+ }
+
+ if(MAPINotification_msgStoresTable != NULL)
+ {
+ MAPINotification_msgStoresTable->Unadvise(
+ MAPINotification_msgStoresTableConnection);
+ MAPINotification_msgStoresTable->Release();
+ MAPINotification_msgStoresTable = NULL;
+ }
+}
diff --git a/src/native/addrbook/msoutlook/net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.cxx b/src/native/addrbook/msoutlook/net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.cxx
index 5eae7ed..31f048d 100644
--- a/src/native/addrbook/msoutlook/net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.cxx
+++ b/src/native/addrbook/msoutlook/net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.cxx
@@ -1,1370 +1,1949 @@
-/*
- * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
- *
- * Distributable under LGPL license.
- * See terms of license at gnu.org.
- */
-
-#include "net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.h"
-
-#include "../AddrBookContactQuery.h"
-#include "MAPINotification.h"
-#include "MAPISession.h"
-#include "MsOutlookMAPIHResultException.h"
-#include <stdio.h>
-#include <string.h>
-
-#define PR_ATTACHMENT_CONTACTPHOTO PROP_TAG(PT_BOOLEAN, 0x7FFF)
-
-typedef
- jboolean (*MsOutlookAddrBookContactQuery_ForeachRowInTableCallback)
- (LPUNKNOWN iUnknown,
- ULONG entryIDByteCount, LPENTRYID entryID, ULONG objType,
- JNIEnv *jniEnv,
- jstring query,
- jobject callback, jmethodID callbackMethodID);
-
-static ULONG MsOutlookAddrBookContactQuery_openEntryUlFlags = MAPI_BEST_ACCESS;
-
-static HRESULT MsOutlookAddrBookContactQuery_HrGetOneProp
- (LPMAPIPROP mapiProp, ULONG propTag, LPSPropValue *prop);
-
-static jboolean MsOutlookAddrBookContactQuery_foreachContactInMsgStoresTable
- (LPMAPISESSION mapiSession,
- JNIEnv *jniEnv,
- jstring query,
- jobject callback, jmethodID callbackMethodID);
-static jboolean MsOutlookAddrBookContactQuery_foreachMailUser
- (ULONG objType, LPUNKNOWN iUnknown,
- JNIEnv *jniEnv,
- jstring query,
- jobject callback, jmethodID callbackMethodID);
-static jboolean MsOutlookAddrBookContactQuery_foreachMailUserInAddressBook
- (LPMAPISESSION mapiSession,
- JNIEnv *jniEnv,
- jstring query,
- jobject callback, jmethodID callbackMethodID);
-static jboolean MsOutlookAddrBookContactQuery_foreachMailUserInContainerTable
- (LPMAPICONTAINER mapiContainer, LPMAPITABLE mapiTable,
- JNIEnv *jniEnv,
- jstring query,
- jobject callback, jmethodID callbackMethodID);
-static jboolean MsOutlookAddrBookContactQuery_foreachRowInTable
- (LPMAPITABLE mapiTable,
- MsOutlookAddrBookContactQuery_ForeachRowInTableCallback rowCallback,
- LPUNKNOWN iUnknown,
- JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID);
-static void MsOutlookAddrBookContactQuery_freeSRowSet(LPSRowSet rows);
-static jbyteArray MsOutlookAddrBookContactQuery_getAttachmentContactPhoto
- (LPMESSAGE message,
- JNIEnv *jniEnv);
-static HRESULT MsOutlookAddrBookContactQuery_getContactsFolderEntryID
- (LPMDB msgStore,
- ULONG folderEntryIDByteCount, LPENTRYID folderEntryID,
- ULONG *contactsFolderEntryIDByteCount, LPENTRYID *contactsFolderEntryID);
-static ULONG MsOutlookAddrBookContactQuery_getPropTagFromLid
- (LPMAPIPROP mapiProp, LONG lid);
-static jboolean MsOutlookAddrBookContactQuery_mailUserMatches
- (LPMAPIPROP mailUser, JNIEnv *jniEnv, jstring query);
-static jboolean MsOutlookAddrBookContactQuery_onForeachContactInMsgStoresTableRow
- (LPUNKNOWN mapiSession,
- ULONG entryIDByteCount, LPENTRYID entryID, ULONG objType,
- JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID);
-static jboolean MsOutlookAddrBookContactQuery_onForeachMailUserInContainerTableRow
- (LPUNKNOWN mapiContainer,
- ULONG entryIDByteCount, LPENTRYID entryID, ULONG objType,
- JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID);
-LPUNKNOWN MsOutlookAddrBookContactQuery_openEntryId(const char* entryId);
-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)
-{
- jmethodID callbackMethodID;
-
- MAPISession_lock();
- LPMAPISESSION mapiSession = MAPISession_getMapiSession();
- if (!mapiSession)
- {
- MAPISession_unlock();
- return;
- }
-
- callbackMethodID
- = AddrBookContactQuery_getPtrCallbackMethodID(jniEnv, callback);
- if (!callbackMethodID || jniEnv->ExceptionCheck())
- {
- MAPISession_unlock();
- return;
- }
-
- jboolean proceed
- = MsOutlookAddrBookContactQuery_foreachContactInMsgStoresTable(
- mapiSession,
- jniEnv,
- query,
- callback, callbackMethodID);
-
- if (proceed && !(jniEnv->ExceptionCheck()))
- {
- MsOutlookAddrBookContactQuery_foreachMailUserInAddressBook(
- mapiSession,
- jniEnv,
- query,
- callback, callbackMethodID);
- }
-
- MAPISession_unlock();
-}
-
-/**
- * Deletes one property from a contact.
- *
- * @param jniEnv The Java native interface environment.
- * @param clazz A Java class Object.
- * @param propId The outlook property identifier.
- * @param entryId The identifer of the outlook entry to modify.
- *
- * @return JNI_TRUE if the deletion succeded. JNI_FALSE otherwise.
- */
-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 = MsOutlookAddrBookContactQuery_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;
- 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;
-}
-
-JNIEXPORT jobjectArray JNICALL
-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;
- jobjectArray props = NULL;
-
- propIdCount = jniEnv->GetArrayLength(propIds);
- if (S_OK
- == MAPIAllocateBuffer(
- CbNewSPropTagArray(propIdCount),
- (void **) &propTagArray))
- {
- jsize i;
-
- propTagArray->cValues = propIdCount;
- for (i = 0; i < propIdCount; i++)
- {
- jlong propId;
-
- jniEnv->GetLongArrayRegion(propIds, i, 1, &propId);
- if (jniEnv->ExceptionCheck())
- {
- MAPIFreeBuffer(propTagArray);
- propTagArray = NULL;
- break;
- }
- else
- {
- ULONG propTag;
-
- if (propId < 0x8000)
- {
- if (propId == PROP_ID(PR_ATTACHMENT_CONTACTPHOTO))
- propTag = PR_HASATTACH;
- else
- propTag = PROP_TAG(PT_UNSPECIFIED, propId);
- }
- else
- {
- propTag
- = MsOutlookAddrBookContactQuery_getPropTagFromLid(
- (LPMAPIPROP) mapiProp,
- (LONG)propId);
- }
- *(propTagArray->aulPropTag + i) = propTag;
- }
- }
- if (propTagArray)
- {
- HRESULT hResult;
- ULONG propCount;
- LPSPropValue propArray;
-
- hResult
- = ((LPMAPIPROP) mapiProp)->GetProps(
- propTagArray,
- (ULONG) flags,
- &propCount, &propArray);
- if (HR_SUCCEEDED(hResult))
- {
- jclass objectClass;
- ULONG j;
-
- objectClass = jniEnv->FindClass("java/lang/Object");
- if (objectClass)
- {
- props
- = jniEnv->NewObjectArray(
- (jsize) propCount,
- objectClass,
- NULL);
- }
- for (j = 0; j < propCount; j++)
- {
- LPSPropValue prop = propArray;
-
- if (props)
- {
- switch (PROP_TYPE(prop->ulPropTag))
- {
- case PT_BOOLEAN:
- {
- if ((PR_HASATTACH == prop->ulPropTag)
- && prop->Value.b)
- {
- jbyteArray value
- = MsOutlookAddrBookContactQuery_getAttachmentContactPhoto(
- (LPMESSAGE) mapiProp,
- jniEnv);
-
- if (value)
- {
- jniEnv->SetObjectArrayElement(
- props, j,
- value);
- if (jniEnv->ExceptionCheck())
- props = NULL;
- }
- }
- break;
- }
-
- case PT_LONG:
- {
- jclass longClass
- = jniEnv->FindClass("java/lang/Long");
-
- if (longClass)
- {
- jmethodID longMethodID
- = jniEnv->GetMethodID(
- longClass,
- "<init>", "(J)V");
-
- if (longMethodID)
- {
- jlong l = prop->Value.l;
- jobject value
- = jniEnv->NewObject(
- longClass, longMethodID,
- l);
-
- if (value)
- {
- jniEnv->SetObjectArrayElement(
- props, j,
- value);
- if (jniEnv->ExceptionCheck())
- props = NULL;
- }
- }
- }
- break;
- }
-
- case PT_STRING8:
- {
- if (prop->Value.lpszA)
- {
- jstring value;
-
- value = jniEnv->NewStringUTF(prop->Value.lpszA);
- if (value)
- {
- jniEnv->SetObjectArrayElement(
- props,
- j, value);
- if (jniEnv->ExceptionCheck())
- props = NULL;
- }
- }
- break;
- }
-
- case PT_UNICODE:
- {
- if (prop->Value.lpszW)
- {
- jstring value;
-
- value
- = jniEnv->NewString(
- (const jchar *) (prop->Value.lpszW),
- wcslen(prop->Value.lpszW));
- if (value)
- {
- jniEnv->SetObjectArrayElement(
- props,
- j, value);
- if (jniEnv->ExceptionCheck())
- props = NULL;
- }
- }
- break;
- }
-
- case PT_BINARY:
- {
- LPSTR entryIdStr = (LPSTR)::malloc(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;
- }
-
- ::free(entryIdStr);
- entryIdStr = NULL;
- break;
- }
- }
- }
- propArray++;
- MAPIFreeBuffer(prop);
- }
- }
- else
- {
- MsOutlookMAPIHResultException_throwNew(
- jniEnv,
- hResult,
- __FILE__, __LINE__);
- }
- MAPIFreeBuffer(propTagArray);
- }
- }
- else
- {
- MsOutlookMAPIHResultException_throwNew(
- jniEnv,
- MAPI_E_NOT_ENOUGH_MEMORY,
- __FILE__, __LINE__);
- }
- return props;
-}
-
-/**
- * Saves one contact property.
- *
- * @param jniEnv The Java native interface environment.
- * @param clazz A Java class Object.
- * @param propId The outlook property identifier.
- * @param value The value to set to the outlook property.
- * @param entryId The identifer of the outlook entry to modify.
- *
- * @return JNI_TRUE if the modification succeded. JNI_FALSE otherwise.
- */
-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 = MsOutlookAddrBookContactQuery_openEntryId(nativeEntryId))
- == NULL)
- {
- return JNI_FALSE;
- }
- jniEnv->ReleaseStringUTFChars(entryId, nativeEntryId);
-
- const char *nativeValue = jniEnv->GetStringUTFChars(value, NULL);
- size_t valueLength = strlen(nativeValue);
- LPWSTR wCharValue = (LPWSTR)::malloc((valueLength + 1) * sizeof(wchar_t));
- if(mbstowcs(wCharValue, nativeValue, valueLength + 1)
- != valueLength)
- {
- fprintf(stderr,
- "setPropUnicode (addrbook/MsOutlookAddrBookContactQuery.c): \
- \n\tmbstowcs\n");
- fflush(stderr);
- jniEnv->ReleaseStringUTFChars(value, nativeValue);
- ::free(wCharValue);
- wCharValue = NULL;
- 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();
- ::free(wCharValue);
- wCharValue = NULL;
- return JNI_TRUE;
- }
- }
-
- ((LPMAPIPROP) mapiProp)->Release();
- ::free(wCharValue);
- wCharValue = NULL;
- return JNI_FALSE;
-}
-
-static HRESULT
-MsOutlookAddrBookContactQuery_HrGetOneProp(
- LPMAPIPROP mapiProp,
- ULONG propTag,
- LPSPropValue *prop)
-{
- SPropTagArray propTagArray;
- HRESULT hResult;
- ULONG valueCount;
- LPSPropValue values;
-
- propTagArray.cValues = 1;
- propTagArray.aulPropTag[0] = propTag;
-
- hResult = mapiProp->GetProps(&propTagArray, 0, &valueCount, &values);
- if (HR_SUCCEEDED(hResult))
- {
- ULONG i;
- jboolean propHasBeenAssignedTo = JNI_FALSE;
-
- for (i = 0; i < valueCount; i++)
- {
- LPSPropValue value = values;
-
- values++;
- if (value->ulPropTag == propTag)
- {
- *prop = value;
- propHasBeenAssignedTo = JNI_TRUE;
- }
- else
- MAPIFreeBuffer(value);
- }
- if (!propHasBeenAssignedTo)
- hResult = MAPI_E_NOT_FOUND;
- }
- return hResult;
-}
-
-static jboolean
-MsOutlookAddrBookContactQuery_foreachContactInMsgStoresTable
- (LPMAPISESSION mapiSession,
- JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
-{
- HRESULT hResult;
- LPMAPITABLE msgStoresTable = NULL;
- jboolean proceed;
-
- hResult = mapiSession->GetMsgStoresTable(0, &msgStoresTable);
- if (HR_SUCCEEDED(hResult) && msgStoresTable)
- {
- proceed
- = MsOutlookAddrBookContactQuery_foreachRowInTable(
- msgStoresTable,
- MsOutlookAddrBookContactQuery_onForeachContactInMsgStoresTableRow,
- (LPUNKNOWN) mapiSession,
- jniEnv, query, callback, callbackMethodID);
- msgStoresTable->Release();
- }
- else
- {
- MsOutlookMAPIHResultException_throwNew(
- jniEnv,
- hResult,
- __FILE__, __LINE__);
- proceed = JNI_TRUE;
- }
- return proceed;
-}
-
-static jboolean
-MsOutlookAddrBookContactQuery_foreachMailUser
- (ULONG objType, LPUNKNOWN iUnknown,
- JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
-{
- jboolean proceed = JNI_TRUE;
-
- switch (objType)
- {
- case MAPI_ABCONT:
- case MAPI_FOLDER:
- {
- LPMAPICONTAINER mapiContainer = (LPMAPICONTAINER) iUnknown;
-
- HRESULT hResult;
- LPMAPITABLE mapiTable;
-
- /* Look for MAPI_MAILUSER through the contents. */
- mapiTable = NULL;
- hResult = mapiContainer->GetContentsTable(0, &mapiTable);
- if (HR_SUCCEEDED(hResult) && mapiTable)
- {
- proceed
- = MsOutlookAddrBookContactQuery_foreachMailUserInContainerTable(
- mapiContainer, mapiTable,
- jniEnv, query, callback, callbackMethodID);
- mapiTable->Release();
- }
-
- /* Drill down the hierarchy. */
- if (proceed)
- {
- mapiTable = NULL;
- hResult = mapiContainer->GetHierarchyTable(0, &mapiTable);
- if (HR_SUCCEEDED(hResult) && mapiTable)
- {
- proceed
- = MsOutlookAddrBookContactQuery_foreachMailUserInContainerTable(
- mapiContainer, mapiTable,
- jniEnv, query, callback, callbackMethodID);
- mapiTable->Release();
- }
- }
-
- break;
- }
-
- case MAPI_MAILUSER:
- case MAPI_MESSAGE:
- {
- if (MsOutlookAddrBookContactQuery_mailUserMatches(
- (LPMAPIPROP) iUnknown,
- jniEnv, query))
- {
- /* Report the MAPI_MAILUSER to the callback. */
- proceed
- = jniEnv->CallBooleanMethod(
- callback, callbackMethodID,
- iUnknown);
-
- /*
- * XXX When an exception is thrown in the callback, does proceed get
- * assigned JNI_FALSE?
- */
- if (proceed && jniEnv->ExceptionCheck())
- proceed = JNI_FALSE;
- }
- break;
- }
- }
- return proceed;
-}
-
-static jboolean
-MsOutlookAddrBookContactQuery_foreachMailUserInAddressBook
- (LPMAPISESSION mapiSession,
- JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
-{
- HRESULT hResult;
- LPADRBOOK adrBook;
- jboolean proceed;
-
- hResult = mapiSession->OpenAddressBook(0, NULL, AB_NO_DIALOG, &adrBook);
- if (HR_SUCCEEDED(hResult))
- {
- ULONG objType;
- LPUNKNOWN iUnknown;
-
- hResult = adrBook->OpenEntry(
- 0,
- NULL,
- NULL,
- MsOutlookAddrBookContactQuery_openEntryUlFlags,
- &objType,
- &iUnknown);
-
- if (HR_SUCCEEDED(hResult))
- {
- proceed
- = MsOutlookAddrBookContactQuery_foreachMailUser(
- objType, iUnknown,
- jniEnv, query, callback, callbackMethodID);
-
- iUnknown->Release();
- }
- else
- {
- MsOutlookMAPIHResultException_throwNew(
- jniEnv,
- hResult,
- __FILE__, __LINE__);
- proceed = JNI_TRUE;
- }
-
- adrBook->Release();
- }
- else
- {
- MsOutlookMAPIHResultException_throwNew(
- jniEnv,
- hResult,
- __FILE__, __LINE__);
- proceed = JNI_TRUE;
- }
- return proceed;
-}
-
-static jboolean
-MsOutlookAddrBookContactQuery_foreachMailUserInContainerTable
- (LPMAPICONTAINER mapiContainer, LPMAPITABLE mapiTable,
- JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
-{
- return
- MsOutlookAddrBookContactQuery_foreachRowInTable(
- mapiTable,
- MsOutlookAddrBookContactQuery_onForeachMailUserInContainerTableRow,
- (LPUNKNOWN) mapiContainer,
- jniEnv, query, callback, callbackMethodID);
-}
-
-static jboolean
-MsOutlookAddrBookContactQuery_foreachRowInTable
- (LPMAPITABLE mapiTable,
- MsOutlookAddrBookContactQuery_ForeachRowInTableCallback rowCallback,
- LPUNKNOWN iUnknown,
- JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
-{
- HRESULT hResult;
- jboolean proceed;
-
- hResult = mapiTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
- if (HR_SUCCEEDED(hResult))
- {
- proceed = JNI_TRUE;
- while (proceed)
- {
- LPSRowSet rows;
-
- hResult = mapiTable->QueryRows(1, 0, &rows);
- if (HR_FAILED(hResult))
- break;
-
- if (rows->cRows == 1)
- {
- ULONG i;
- LPSRow row = rows->aRow;
- 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)
- {
- LPENTRYID entryID = NULL;
-
- if (S_OK
- == MAPIAllocateBuffer(
- entryIDBinary.cb,
- (void **) &entryID))
- {
- CopyMemory(
- entryID,
- entryIDBinary.lpb,
- entryIDBinary.cb);
-
- /*
- * We no longer need the rows at this point so free them
- * before we drill down the hierarchy and allocate even more rows.
- */
- MsOutlookAddrBookContactQuery_freeSRowSet(rows);
-
- proceed
- = rowCallback(
- iUnknown,
- entryIDBinary.cb, entryID, objType,
- jniEnv, query, callback, callbackMethodID);
-
- MAPIFreeBuffer(entryID);
-
- if (proceed && jniEnv->ExceptionCheck())
- proceed = JNI_FALSE;
- }
- else
- MsOutlookAddrBookContactQuery_freeSRowSet(rows);
- }
- else
- MsOutlookAddrBookContactQuery_freeSRowSet(rows);
- }
- else
- {
- MAPIFreeBuffer(rows);
- break;
- }
- }
- }
- else
- {
- /* We've failed but other parts of the hierarchy may still succeed. */
- proceed = JNI_TRUE;
- }
- return proceed;
-}
-
-static void
-MsOutlookAddrBookContactQuery_freeSRowSet(LPSRowSet rows)
-{
- ULONG i;
-
- for (i = 0; i < rows->cRows; i++)
- {
- LPSRow row = (rows->aRow) + i;
- ULONG j;
-
- for (j = 0; j < row->cValues; j++)
- {
- LPSPropValue prop = (row->lpProps) + j;
-
- MAPIFreeBuffer(prop);
- }
- }
- MAPIFreeBuffer(rows);
-}
-
-static jbyteArray
-MsOutlookAddrBookContactQuery_getAttachmentContactPhoto
- (LPMESSAGE message, JNIEnv *jniEnv)
-{
- HRESULT hResult;
- LPMAPITABLE attachmentTable;
- jbyteArray attachmentContactPhoto = NULL;
-
- hResult = message->GetAttachmentTable(0, &attachmentTable);
- if (HR_SUCCEEDED(hResult))
- {
- hResult = attachmentTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
- if (HR_SUCCEEDED(hResult))
- {
- while (1)
- {
- LPSRowSet rows;
-
- hResult = attachmentTable->QueryRows(1, 0, &rows);
- if (HR_FAILED(hResult))
- break;
-
- if (rows->cRows == 1)
- {
- ULONG i;
- LPSRow row = rows->aRow;
- jboolean isAttachmentContactPhotoRow = JNI_FALSE;
- jboolean hasAttachmentContactPhoto = JNI_FALSE;
- ULONG attachNum = 0;
- LONG attachMethod = NO_ATTACHMENT;
-
- for (i = 0; i < row->cValues; i++)
- {
- LPSPropValue prop = (row->lpProps) + i;
-
- switch (prop->ulPropTag)
- {
- case PR_ATTACHMENT_CONTACTPHOTO:
- isAttachmentContactPhotoRow = JNI_TRUE;
- hasAttachmentContactPhoto
- = prop->Value.b ? JNI_TRUE : JNI_FALSE;
- break;
- case PR_ATTACH_METHOD:
- attachMethod = prop->Value.l;
- break;
- case PR_ATTACH_NUM:
- attachNum = prop->Value.l;
- break;
- }
- }
-
- MsOutlookAddrBookContactQuery_freeSRowSet(rows);
-
- /*
- * As the reference says and as discovered in practice,
- * PR_ATTACHMENT_CONTACTPHOTO is sometimes in IAttach.
- */
- if ((isAttachmentContactPhotoRow
- && hasAttachmentContactPhoto)
- || !isAttachmentContactPhotoRow)
- {
- attachmentContactPhoto
- = MsOutlookAddrBookContactQuery_readAttachment(
- message,
- attachMethod, attachNum,
- jniEnv,
- (!isAttachmentContactPhotoRow)
- ? PR_ATTACHMENT_CONTACTPHOTO
- : PROP_TAG(PT_UNSPECIFIED, 0));
- }
- if (isAttachmentContactPhotoRow
- || attachmentContactPhoto)
- {
- /*
- * The reference says there can be only 1
- * PR_ATTACHMENT_CONTACTPHOTO.
- */
- break;
- }
- }
- else
- {
- MAPIFreeBuffer(rows);
- break;
- }
- }
- }
-
- attachmentTable->Release();
- }
- return attachmentContactPhoto;
-}
-
-static HRESULT
-MsOutlookAddrBookContactQuery_getContactsFolderEntryID
- (LPMDB msgStore,
- ULONG folderEntryIDByteCount, LPENTRYID folderEntryID,
- ULONG *contactsFolderEntryIDByteCount, LPENTRYID *contactsFolderEntryID)
-{
- HRESULT hResult;
- ULONG objType;
- LPUNKNOWN folder;
-
- hResult = msgStore->OpenEntry(
- folderEntryIDByteCount,
- folderEntryID,
- NULL,
- MsOutlookAddrBookContactQuery_openEntryUlFlags,
- &objType,
- &folder);
-
- if (HR_SUCCEEDED(hResult))
- {
- LPSPropValue prop;
-
- hResult
- = MsOutlookAddrBookContactQuery_HrGetOneProp(
- (LPMAPIPROP) folder,
- 0x36D10102 /* PR_IPM_CONTACT_ENTRYID */,
- &prop);
- if (HR_SUCCEEDED(hResult))
- {
- LPSBinary bin = &(prop->Value.bin);
- if (S_OK
- == MAPIAllocateBuffer(
- bin->cb,
- (void **) contactsFolderEntryID))
- {
- hResult = S_OK;
- *contactsFolderEntryIDByteCount = bin->cb;
- CopyMemory(*contactsFolderEntryID, bin->lpb, bin->cb);
- }
- else
- hResult = MAPI_E_NOT_ENOUGH_MEMORY;
- MAPIFreeBuffer(prop);
- }
- folder->Release();
- }
- return hResult;
-}
-
-static ULONG
-MsOutlookAddrBookContactQuery_getPropTagFromLid(LPMAPIPROP mapiProp, LONG lid)
-{
- GUID PSETID_Address
- = {0x00062004, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
- MAPINAMEID propName;
- LPMAPINAMEID propNamePtr;
- HRESULT hResult;
- LPSPropTagArray propTagArray;
-
- propName.lpguid = (LPGUID) &PSETID_Address;
- propName.ulKind = MNID_ID;
- propName.Kind.lID = lid;
- propNamePtr = &propName;
- hResult
- = mapiProp->GetIDsFromNames(
- 1, &propNamePtr,
- 0,
- &propTagArray);
- if (HR_SUCCEEDED(hResult) && (1 == propTagArray->cValues))
- {
- ULONG propTag = propTagArray->aulPropTag[0];
-
- if (PT_ERROR == PROP_TYPE(propTag))
- propTag = PROP_TAG(PT_UNSPECIFIED, lid);
- MAPIFreeBuffer(propTagArray);
- return propTag;
- }
- else
- return PROP_TAG(PT_UNSPECIFIED, lid);
-}
-
-static jboolean
-MsOutlookAddrBookContactQuery_mailUserMatches
- (LPMAPIPROP mailUser, JNIEnv *jniEnv, jstring query)
-{
- // TODO Auto-generated method stub
- return JNI_TRUE;
-}
-
-static jboolean
-MsOutlookAddrBookContactQuery_onForeachContactInMsgStoresTableRow
- (LPUNKNOWN mapiSession,
- ULONG entryIDByteCount, LPENTRYID entryID, ULONG objType,
- JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
-{
- HRESULT hResult;
- LPMDB msgStore;
- jboolean proceed;
-
- hResult
- = ((LPMAPISESSION) mapiSession)->OpenMsgStore(
- 0,
- entryIDByteCount, entryID,
- NULL,
- MDB_NO_MAIL | MsOutlookAddrBookContactQuery_openEntryUlFlags,
- &msgStore);
- if (HR_SUCCEEDED(hResult))
- {
- LPENTRYID receiveFolderEntryID = NULL;
- ULONG contactsFolderEntryIDByteCount = 0;
- LPENTRYID contactsFolderEntryID = NULL;
-
- hResult
- = msgStore->GetReceiveFolder(
- NULL,
- 0,
- &entryIDByteCount, &receiveFolderEntryID,
- NULL);
- if (HR_SUCCEEDED(hResult))
- {
- hResult
- = MsOutlookAddrBookContactQuery_getContactsFolderEntryID(
- msgStore,
- entryIDByteCount, receiveFolderEntryID,
- &contactsFolderEntryIDByteCount, &contactsFolderEntryID);
- MAPIFreeBuffer(receiveFolderEntryID);
- }
- if (HR_FAILED(hResult))
- {
- hResult
- = MsOutlookAddrBookContactQuery_getContactsFolderEntryID(
- msgStore,
- 0, NULL,
- &contactsFolderEntryIDByteCount, &contactsFolderEntryID);
- }
- if (HR_SUCCEEDED(hResult))
- {
- ULONG contactsFolderObjType;
- LPUNKNOWN contactsFolder;
-
- hResult
- = msgStore->OpenEntry(
- contactsFolderEntryIDByteCount, contactsFolderEntryID,
- NULL,
- MsOutlookAddrBookContactQuery_openEntryUlFlags,
- &contactsFolderObjType, &contactsFolder);
- if (HR_SUCCEEDED(hResult))
- {
- proceed
- = MsOutlookAddrBookContactQuery_foreachMailUser(
- contactsFolderObjType, contactsFolder,
- jniEnv, query, callback, callbackMethodID);
- contactsFolder->Release();
- }
- else
- {
- /*
- * We've failed but other parts of the hierarchy may still
- * succeed.
- */
- proceed = JNI_TRUE;
- }
- MAPIFreeBuffer(contactsFolderEntryID);
- }
- else
- {
- /*
- * We've failed but other parts of the hierarchy may still succeed.
- */
- proceed = JNI_TRUE;
- }
- msgStore->Release();
- }
- else
- {
- /* We've failed but other parts of the hierarchy may still succeed. */
- proceed = JNI_TRUE;
- }
- return proceed;
-}
-
-static jboolean
-MsOutlookAddrBookContactQuery_onForeachMailUserInContainerTableRow
- (LPUNKNOWN mapiContainer,
- ULONG entryIDByteCount, LPENTRYID entryID, ULONG objType,
- JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
-{
- HRESULT hResult;
- LPUNKNOWN iUnknown;
- jboolean proceed;
-
- // Make write failed and image load.
- hResult
- = ((LPMAPICONTAINER) mapiContainer)->OpenEntry(
- entryIDByteCount, entryID,
- NULL,
- MsOutlookAddrBookContactQuery_openEntryUlFlags,
- &objType, &iUnknown);
- if (HR_SUCCEEDED(hResult))
- {
- proceed
- = MsOutlookAddrBookContactQuery_foreachMailUser(
- objType, iUnknown,
- jniEnv, query, callback, callbackMethodID);
- iUnknown->Release();
- }
- else
- {
- /* We've failed but other parts of the hierarchy may still succeed. */
- proceed = JNI_TRUE;
- }
- return proceed;
-}
-
-/**
- * Opens an object based on the string representation of its entry id.
- *
- * @param entryId The identifier of the entry to open.
- *
- * @return A pointer to the opened entry. NULL if anything goes wrong.
- */
-LPUNKNOWN MsOutlookAddrBookContactQuery_openEntryId(const char* entryId)
-{
- ULONG tmpEntryIdSize = strlen(entryId) / 2;
- LPENTRYID tmpEntryId = (LPENTRYID) malloc(tmpEntryIdSize * sizeof(char));
- if(FBinFromHex((LPSTR) entryId, (LPBYTE) tmpEntryId))
- {
- LPMAPISESSION mapiSession = MAPISession_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;
-}
-
-static jbyteArray
-MsOutlookAddrBookContactQuery_readAttachment
- (LPMESSAGE message, LONG method, ULONG num, JNIEnv *jniEnv, ULONG cond)
-{
- jbyteArray attachment = NULL;
-
- if (ATTACH_BY_VALUE == method)
- {
- HRESULT hResult;
- LPATTACH attach;
-
- hResult = message->OpenAttach(num, NULL, 0, &attach);
- if (HR_SUCCEEDED(hResult))
- {
- IStream *stream = NULL;
-
- if (PT_BOOLEAN == PROP_TYPE(cond))
- {
- LPSPropValue condValue;
-
- hResult = MsOutlookAddrBookContactQuery_HrGetOneProp(
- (LPMAPIPROP) attach,
- cond,
- &condValue);
- if (HR_SUCCEEDED(hResult))
- {
- if ((PT_BOOLEAN != PROP_TYPE(condValue->ulPropTag))
- || !(condValue->Value.b))
- hResult = MAPI_E_NOT_FOUND;
- MAPIFreeBuffer(condValue);
- }
- }
-
- if (HR_SUCCEEDED(hResult))
- {
- hResult
- = ((LPMAPIPROP) attach)->OpenProperty(
- PR_ATTACH_DATA_BIN,
- &IID_IStream, 0,
- 0,
- (LPUNKNOWN *) &stream);
- }
- if (HR_SUCCEEDED(hResult) && stream)
- {
- STATSTG statstg;
- ULONGLONG length;
-
- hResult = stream->Stat(&statstg, STATFLAG_NONAME);
- if ((S_OK == hResult) && ((length = statstg.cbSize.QuadPart)))
- {
- attachment = jniEnv->NewByteArray((jsize) length);
- if (attachment)
- {
- jbyte *bytes
- = jniEnv->GetByteArrayElements(attachment, NULL);
-
- if (bytes)
- {
- ULONG read;
- jint mode;
-
- hResult
- = stream->Read(bytes, (ULONG) length, &read);
- mode
- = ((S_OK == hResult) || (S_FALSE == hResult))
- ? 0
- : JNI_ABORT;
- jniEnv->ReleaseByteArrayElements(
- attachment, bytes,
- mode);
- if (0 != mode)
- attachment = NULL;
- }
- }
- }
-
- stream->Release();
- }
-
- attach->Release();
- }
- }
- return attachment;
-}
+/*
+ * Jitsi, the OpenSource Java VoIP and Instant Messaging client.
+ *
+ * Distributable under LGPL license.
+ * See terms of license at gnu.org.
+ */
+
+#include "net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.h"
+
+#include "../AddrBookContactQuery.h"
+#include "MAPINotification.h"
+#include "MAPISession.h"
+#include "MsOutlookMAPIHResultException.h"
+#include <initguid.h>
+#include <stdio.h>
+#include <string.h>
+
+#define BODY_ENCODING_TEXT_AND_HTML ((ULONG) 0x00100000)
+#define DELETE_HARD_DELETE ((ULONG) 0x00000010)
+#define ENCODING_PREFERENCE ((ULONG) 0x00020000)
+#define ENCODING_MIME ((ULONG) 0x00040000)
+#define OOP_DONT_LOOKUP ((ULONG) 0x10000000)
+#define PR_ATTACHMENT_CONTACTPHOTO PROP_TAG(PT_BOOLEAN, 0x7FFF)
+
+DEFINE_OLEGUID(PSETID_Address, MAKELONG(0x2000+(0x04),0x0006),0,0);
+
+const MAPIUID MsOutlookAddrBookContactQuery_MuidOneOffEntryID =
+{
+ 0x81, 0x2b, 0x1f, 0xa4, 0xbe, 0xa3, 0x10, 0x19,
+ 0x9d, 0x6e, 0x00, 0xdd, 0x01, 0x0f, 0x54, 0x02
+};
+
+typedef struct MsOutlookAddrBookContactQuery_OneOffEntryID
+{
+ ULONG ulFlags;
+ MAPIUID muid;
+ ULONG ulBitMask;
+ BYTE bData[];
+} ONEOFFENTRYID;
+
+typedef UNALIGNED ONEOFFENTRYID *MsOutlookAddrBookContactQuery_LPONEOFFENTRYID;
+
+typedef
+ jboolean (*MsOutlookAddrBookContactQuery_ForeachRowInTableCallback)
+ (LPUNKNOWN iUnknown,
+ ULONG entryIDByteCount, LPENTRYID entryID, ULONG objType,
+ JNIEnv *jniEnv,
+ jstring query,
+ jobject callback, jmethodID callbackMethodID);
+
+static ULONG MsOutlookAddrBookContactQuery_openEntryUlFlags = MAPI_BEST_ACCESS;
+
+static HRESULT MsOutlookAddrBookContactQuery_HrGetOneProp
+ (LPMAPIPROP mapiProp, ULONG propTag, LPSPropValue *prop);
+
+HRESULT MsOutlookAddrBookContactQuery_buildOneOff
+ (LPWSTR displayName, LPWSTR addressType, LPWSTR emailAddress,
+ ULONG* oneOffEntryIdLength, LPBYTE* oneOffEntryId);
+HRESULT MsOutlookAddrBookContactQuery_createEmailAddress
+ (LPMESSAGE contact, LPWSTR displayName, LPWSTR addressType,
+ LPWSTR emailAddress, LPWSTR originalDisplayName, LONG providerEmailList[],
+ LONG providerArrayType, ULONG propIds[], int nbPropId);
+static jboolean MsOutlookAddrBookContactQuery_foreachContactInMsgStoresTable
+ (LPMAPISESSION mapiSession,
+ JNIEnv *jniEnv,
+ jstring query,
+ jobject callback, jmethodID callbackMethodID);
+static jboolean MsOutlookAddrBookContactQuery_foreachMailUser
+ (ULONG objType, LPUNKNOWN iUnknown,
+ JNIEnv *jniEnv,
+ jstring query,
+ jobject callback, jmethodID callbackMethodID);
+static jboolean MsOutlookAddrBookContactQuery_foreachMailUserInAddressBook
+ (LPMAPISESSION mapiSession,
+ JNIEnv *jniEnv,
+ jstring query,
+ jobject callback, jmethodID callbackMethodID);
+static jboolean MsOutlookAddrBookContactQuery_foreachMailUserInContainerTable
+ (LPMAPICONTAINER mapiContainer, LPMAPITABLE mapiTable,
+ JNIEnv *jniEnv,
+ jstring query,
+ jobject callback, jmethodID callbackMethodID);
+static jboolean MsOutlookAddrBookContactQuery_foreachRowInTable
+ (LPMAPITABLE mapiTable,
+ MsOutlookAddrBookContactQuery_ForeachRowInTableCallback rowCallback,
+ LPUNKNOWN iUnknown,
+ JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID);
+static void MsOutlookAddrBookContactQuery_freeSRowSet(LPSRowSet rows);
+static jbyteArray MsOutlookAddrBookContactQuery_getAttachmentContactPhoto
+ (LPMESSAGE message, JNIEnv *jniEnv);
+void MsOutlookAddrBookContactQuery_getBinaryProp
+ (LPMAPIPROP entry, ULONG propId, LPSBinary binaryProp);
+static HRESULT MsOutlookAddrBookContactQuery_getContactsFolderEntryID
+ (LPMDB msgStore,
+ ULONG folderEntryIDByteCount, LPENTRYID folderEntryID,
+ ULONG *contactsFolderEntryIDByteCount, LPENTRYID *contactsFolderEntryID);
+LPSTR MsOutlookAddrBookContactQuery_getContactId(LPMAPIPROP contact);
+LPMAPIFOLDER MsOutlookAddrBookContactQuery_getDefaultContactFolderId(void);
+LPMDB MsOutlookAddrBookContactQuery_getDefaultMsgStores(void);
+static ULONG MsOutlookAddrBookContactQuery_getPropTagFromLid
+ (LPMAPIPROP mapiProp, LONG lid);
+static jboolean MsOutlookAddrBookContactQuery_mailUserMatches
+ (LPMAPIPROP mailUser, JNIEnv *jniEnv, jstring query);
+static jboolean MsOutlookAddrBookContactQuery_onForeachContactInMsgStoresTableRow
+ (LPUNKNOWN mapiSession,
+ ULONG entryIDByteCount, LPENTRYID entryID, ULONG objType,
+ JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID);
+static jboolean MsOutlookAddrBookContactQuery_onForeachMailUserInContainerTableRow
+ (LPUNKNOWN mapiContainer,
+ ULONG entryIDByteCount, LPENTRYID entryID, ULONG objType,
+ JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID);
+LPUNKNOWN MsOutlookAddrBookContactQuery_openEntryId
+ (ULONG entryIdSize, LPENTRYID entryId);
+LPUNKNOWN MsOutlookAddrBookContactQuery_openEntryIdStr(const char* entryId);
+static jbyteArray MsOutlookAddrBookContactQuery_readAttachment
+ (LPMESSAGE message, LONG method, ULONG num, JNIEnv *jniEnv, ULONG cond);
+
+/**
+ * creates a new contact from the outlook database.
+ *
+ * @param jniEnv The Java native interface environment.
+ * @param clazz A Java class Object.
+ *
+ * @return The identifer of the created outlook contact. NULL on failure.
+ */
+JNIEXPORT jstring JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_createContact
+ (JNIEnv *jniEnv, jclass clazz)
+{
+ jstring value = NULL;
+
+ LPMAPIFOLDER parentEntry
+ = MsOutlookAddrBookContactQuery_getDefaultContactFolderId();
+
+ LPMESSAGE message;
+ if(parentEntry->CreateMessage(NULL, 0, &message) == S_OK)
+ {
+ SPropValue updateValue;
+
+ // PR_MESSAGE_CLASS_W
+ updateValue.ulPropTag = PROP_TAG(PT_UNICODE, 0x001A);
+ updateValue.Value.lpszW = (LPWSTR) L"IPM.Contact";
+ if(((LPMAPIPROP) message)->SetProps(
+ 1,
+ (LPSPropValue) &updateValue,
+ NULL) == S_OK)
+ {
+ ((LPMAPIPROP) message)->SaveChanges(
+ FORCE_SAVE | KEEP_OPEN_READWRITE);
+ }
+
+ updateValue.ulPropTag = PROP_TAG(PT_LONG, 0x1080); // PR_ICON_INDEX
+ updateValue.Value.l = 512;
+ if(((LPMAPIPROP) message)->SetProps(
+ 1,
+ (LPSPropValue) &updateValue,
+ NULL) == S_OK)
+ {
+ ((LPMAPIPROP) message)->SaveChanges(
+ FORCE_SAVE | KEEP_OPEN_READWRITE);
+ }
+
+ LPSTR messageIdStr
+ = MsOutlookAddrBookContactQuery_getContactId((LPMAPIPROP) message);
+ value = jniEnv->NewStringUTF(messageIdStr);
+ ::free(messageIdStr);
+ messageIdStr = NULL;
+
+ ((LPMAPIPROP) message)->Release();
+ }
+ parentEntry->Release();
+
+ return value;
+}
+
+/**
+ * Delete the given contact from the outlook database.
+ *
+ * @param jniEnv The Java native interface environment.
+ * @param clazz A Java class Object.
+ * @param id The identifer of the outlook contact to remove.
+ *
+ * @return JNI_TRUE if the deletion succeded. JNI_FALSE otherwise.
+ */
+JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_deleteContact
+ (JNIEnv *jniEnv, jclass clazz, jstring id)
+{
+ jboolean res = JNI_FALSE;
+
+ const char *nativeEntryId = jniEnv->GetStringUTFChars(id, NULL);
+ LPUNKNOWN mapiProp;
+ if((mapiProp = MsOutlookAddrBookContactQuery_openEntryIdStr(nativeEntryId))
+ == NULL)
+ {
+ return JNI_FALSE;
+ }
+ jniEnv->ReleaseStringUTFChars(id, nativeEntryId);
+
+ SBinary contactId;
+ contactId.cb = 0;
+ MsOutlookAddrBookContactQuery_getBinaryProp(
+ (LPMAPIPROP) mapiProp,
+ 0x0FFF,
+ &contactId);
+
+ SBinary parentId;
+ parentId.cb = 0;
+ MsOutlookAddrBookContactQuery_getBinaryProp(
+ (LPMAPIPROP) mapiProp,
+ 0x0E09, //PR_PARENT_ENTRYID,
+ &parentId);
+ LPUNKNOWN parentEntry = MsOutlookAddrBookContactQuery_openEntryId(
+ parentId.cb, (LPENTRYID) parentId.lpb);
+
+ SBinaryArray deleteIdArray;
+ deleteIdArray.cValues = 1;
+ deleteIdArray.lpbin = &contactId;
+ res = (((LPMAPIFOLDER) parentEntry)->DeleteMessages(
+ &deleteIdArray,
+ 0,
+ NULL,
+ DELETE_HARD_DELETE) == S_OK);
+
+ ((LPMAPIPROP) parentEntry)->Release();
+ MAPIFreeBuffer(&parentId);
+ MAPIFreeBuffer(&contactId);
+ ((LPMAPIPROP) mapiProp)->Release();
+
+ return res;
+}
+
+JNIEXPORT void JNICALL
+Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_foreachMailUser(
+ JNIEnv *jniEnv,
+ jclass clazz,
+ jstring query,
+ jobject callback)
+{
+ jmethodID callbackMethodID;
+
+ MAPISession_lock();
+ LPMAPISESSION mapiSession = MAPISession_getMapiSession();
+ if (!mapiSession)
+ {
+ MAPISession_unlock();
+ return;
+ }
+
+ callbackMethodID
+ = AddrBookContactQuery_getStringCallbackMethodID(jniEnv, callback);
+ if (!callbackMethodID || jniEnv->ExceptionCheck())
+ {
+ MAPISession_unlock();
+ return;
+ }
+
+ jboolean proceed
+ = MsOutlookAddrBookContactQuery_foreachContactInMsgStoresTable(
+ mapiSession,
+ jniEnv,
+ query,
+ callback, callbackMethodID);
+
+ if (proceed && !(jniEnv->ExceptionCheck()))
+ {
+ MsOutlookAddrBookContactQuery_foreachMailUserInAddressBook(
+ mapiSession,
+ jniEnv,
+ query,
+ callback, callbackMethodID);
+ }
+
+ MAPISession_unlock();
+}
+
+/**
+ * Deletes one property from a contact.
+ *
+ * @param jniEnv The Java native interface environment.
+ * @param clazz A Java class Object.
+ * @param propId The outlook property identifier.
+ * @param entryId The identifer of the outlook entry to modify.
+ *
+ * @return JNI_TRUE if the deletion succeded. JNI_FALSE otherwise.
+ */
+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 = MsOutlookAddrBookContactQuery_openEntryIdStr(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;
+ 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;
+ if(propId == 0x8062) // PidLidInstantMessagingAddress
+ {
+ propToDelete.aulPropTag[0] = 0x8046001F;
+ }
+ else
+ {
+ 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;
+}
+
+JNIEXPORT jobjectArray JNICALL
+Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_IMAPIProp_1GetProps(
+ JNIEnv *jniEnv,
+ jclass clazz,
+ jstring entryId,
+ jlongArray propIds,
+ jlong flags)
+{
+ jsize propIdCount;
+ LPSPropTagArray propTagArray;
+ jobjectArray props = NULL;
+
+ const char *nativeEntryId = jniEnv->GetStringUTFChars(entryId, NULL);
+ LPUNKNOWN mapiProp;
+ if((mapiProp = MsOutlookAddrBookContactQuery_openEntryIdStr(nativeEntryId))
+ == NULL)
+ {
+ return NULL;
+ }
+ jniEnv->ReleaseStringUTFChars(entryId, nativeEntryId);
+
+ propIdCount = jniEnv->GetArrayLength(propIds);
+ if (S_OK
+ == MAPIAllocateBuffer(
+ CbNewSPropTagArray(propIdCount),
+ (void **) &propTagArray))
+ {
+ jsize i;
+
+ propTagArray->cValues = propIdCount;
+ for (i = 0; i < propIdCount; i++)
+ {
+ jlong propId;
+
+ jniEnv->GetLongArrayRegion(propIds, i, 1, &propId);
+ if (jniEnv->ExceptionCheck())
+ {
+ MAPIFreeBuffer(propTagArray);
+ propTagArray = NULL;
+ break;
+ }
+ else
+ {
+ ULONG propTag;
+
+ if (propId < 0x8000)
+ {
+ if (propId == PROP_ID(PR_ATTACHMENT_CONTACTPHOTO))
+ propTag = PR_HASATTACH;
+ else
+ propTag = PROP_TAG(PT_UNSPECIFIED, propId);
+ }
+ else
+ {
+ propTag
+ = MsOutlookAddrBookContactQuery_getPropTagFromLid(
+ (LPMAPIPROP) mapiProp,
+ (LONG)propId);
+ }
+ *(propTagArray->aulPropTag + i) = propTag;
+ }
+ }
+ if (propTagArray)
+ {
+ HRESULT hResult;
+ ULONG propCount;
+ LPSPropValue propArray;
+
+ hResult
+ = ((LPMAPIPROP) mapiProp)->GetProps(
+ propTagArray,
+ (ULONG) flags,
+ &propCount, &propArray);
+ if (HR_SUCCEEDED(hResult))
+ {
+ jclass objectClass;
+ ULONG j;
+
+ objectClass = jniEnv->FindClass("java/lang/Object");
+ if (objectClass)
+ {
+ props
+ = jniEnv->NewObjectArray(
+ (jsize) propCount,
+ objectClass,
+ NULL);
+ }
+ for (j = 0; j < propCount; j++)
+ {
+ LPSPropValue prop = propArray;
+
+ if (props)
+ {
+ switch (PROP_TYPE(prop->ulPropTag))
+ {
+ case PT_BOOLEAN:
+ {
+ if ((PR_HASATTACH == prop->ulPropTag)
+ && prop->Value.b)
+ {
+ jbyteArray value
+ = MsOutlookAddrBookContactQuery_getAttachmentContactPhoto(
+ (LPMESSAGE) mapiProp,
+ jniEnv);
+
+ if (value)
+ {
+ jniEnv->SetObjectArrayElement(
+ props, j,
+ value);
+ if (jniEnv->ExceptionCheck())
+ props = NULL;
+ }
+ }
+ break;
+ }
+
+ case PT_LONG:
+ {
+ jclass longClass
+ = jniEnv->FindClass("java/lang/Long");
+
+ if (longClass)
+ {
+ jmethodID longMethodID
+ = jniEnv->GetMethodID(
+ longClass,
+ "<init>", "(J)V");
+
+ if (longMethodID)
+ {
+ jlong l = prop->Value.l;
+ jobject value
+ = jniEnv->NewObject(
+ longClass, longMethodID,
+ l);
+
+ if (value)
+ {
+ jniEnv->SetObjectArrayElement(
+ props, j,
+ value);
+ if (jniEnv->ExceptionCheck())
+ props = NULL;
+ }
+ }
+ }
+ break;
+ }
+
+ case PT_STRING8:
+ {
+ if (prop->Value.lpszA)
+ {
+ jstring value;
+
+ value = jniEnv->NewStringUTF(prop->Value.lpszA);
+ if (value)
+ {
+ jniEnv->SetObjectArrayElement(
+ props,
+ j, value);
+ if (jniEnv->ExceptionCheck())
+ props = NULL;
+ }
+ }
+ break;
+ }
+
+ case PT_UNICODE:
+ {
+ if (prop->Value.lpszW)
+ {
+ jstring value;
+
+ value
+ = jniEnv->NewString(
+ (const jchar *) (prop->Value.lpszW),
+ wcslen(prop->Value.lpszW));
+ if (value)
+ {
+ jniEnv->SetObjectArrayElement(
+ props,
+ j, value);
+ if (jniEnv->ExceptionCheck())
+ props = NULL;
+ }
+ }
+ break;
+ }
+
+ case PT_BINARY:
+ {
+ LPSTR entryIdStr = (LPSTR)::malloc(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;
+ }
+
+ ::free(entryIdStr);
+ entryIdStr = NULL;
+ break;
+ }
+ }
+ }
+ propArray++;
+ MAPIFreeBuffer(prop);
+ }
+ }
+ else
+ {
+ MsOutlookMAPIHResultException_throwNew(
+ jniEnv,
+ hResult,
+ __FILE__, __LINE__);
+ }
+ MAPIFreeBuffer(propTagArray);
+ }
+ }
+ else
+ {
+ MsOutlookMAPIHResultException_throwNew(
+ jniEnv,
+ MAPI_E_NOT_ENOUGH_MEMORY,
+ __FILE__, __LINE__);
+ }
+ ((LPMAPIPROP) mapiProp)->Release();
+
+ return props;
+}
+
+/**
+ * Saves one contact property.
+ *
+ * @param jniEnv The Java native interface environment.
+ * @param clazz A Java class Object.
+ * @param propId The outlook property identifier.
+ * @param value The value to set to the outlook property.
+ * @param entryId The identifer of the outlook entry to modify.
+ *
+ * @return JNI_TRUE if the modification succeded. JNI_FALSE otherwise.
+ */
+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 = MsOutlookAddrBookContactQuery_openEntryIdStr(nativeEntryId))
+ == NULL)
+ {
+ return JNI_FALSE;
+ }
+ jniEnv->ReleaseStringUTFChars(entryId, nativeEntryId);
+
+ const char *nativeValue = jniEnv->GetStringUTFChars(value, NULL);
+ size_t valueLength = strlen(nativeValue);
+ LPWSTR wCharValue = (LPWSTR)::malloc((valueLength + 1) * sizeof(wchar_t));
+ if(mbstowcs(wCharValue, nativeValue, valueLength + 1)
+ != valueLength)
+ {
+ fprintf(stderr,
+ "setPropUnicode (addrbook/MsOutlookAddrBookContactQuery.c): \
+ \n\tmbstowcs\n");
+ fflush(stderr);
+ jniEnv->ReleaseStringUTFChars(value, nativeValue);
+ ::free(wCharValue);
+ wCharValue = NULL;
+ 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))
+ {
+ LPWSTR addressType = (LPWSTR) L"SMTP";
+ LONG providerEmailList[1];
+ switch(propId)
+ {
+ case 0x00008083: // dispidEmail1EmailAddress
+ providerEmailList[0] = 0x00000000;
+ propArray[1].Value.l |= 0x00000001;
+ break;
+ case 0x00008093: // dispidEmail2EmailAddress
+ providerEmailList[0] = 0x00000001;
+ propArray[1].Value.l |= 0x00000002;
+ break;
+ case 0x000080A3: // dispidEmail3EmailAddress
+ providerEmailList[0] = 0x00000002;
+ propArray[1].Value.l |= 0x00000004;
+ break;
+ }
+ propArray[0].Value.MVl.cValues = 1;
+ propArray[0].Value.MVl.lpl = providerEmailList;
+
+ propArray[2].Value.lpszW = wCharValue;
+ propArray[3].Value.lpszW = addressType;
+ propArray[4].Value.lpszW = wCharValue;
+ propArray[5].Value.lpszW = wCharValue;
+
+ if(MsOutlookAddrBookContactQuery_createEmailAddress(
+ (LPMESSAGE) mapiProp,
+ wCharValue, // displayName
+ addressType, // addressType
+ wCharValue, // emailAddress
+ wCharValue, // originalDisplayName
+ providerEmailList,
+ propArray[1].Value.l,
+ propIds,
+ 7) == S_OK)
+ {
+ MAPIFreeBuffer(propTagArray);
+ ((LPMAPIPROP) mapiProp)->Release();
+ return JNI_TRUE;
+ }
+ }
+ MAPIFreeBuffer(propTagArray);
+ ((LPMAPIPROP) mapiProp)->Release();
+ return JNI_FALSE;
+ }
+
+ SPropValue updateValue;
+ if(propId == 0x8062) // PidLidInstantMessagingAddress
+ {
+ updateValue.ulPropTag = 0x8046001F;
+ }
+ else
+ {
+ 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();
+ ::free(wCharValue);
+ wCharValue = NULL;
+ return JNI_TRUE;
+ }
+ }
+
+ ((LPMAPIPROP) mapiProp)->Release();
+ ::free(wCharValue);
+ wCharValue = NULL;
+ return JNI_FALSE;
+}
+
+static HRESULT
+MsOutlookAddrBookContactQuery_HrGetOneProp(
+ LPMAPIPROP mapiProp,
+ ULONG propTag,
+ LPSPropValue *prop)
+{
+ SPropTagArray propTagArray;
+ HRESULT hResult;
+ ULONG valueCount;
+ LPSPropValue values;
+
+ propTagArray.cValues = 1;
+ propTagArray.aulPropTag[0] = propTag;
+
+ hResult = mapiProp->GetProps(&propTagArray, 0, &valueCount, &values);
+ if (HR_SUCCEEDED(hResult))
+ {
+ ULONG i;
+ jboolean propHasBeenAssignedTo = JNI_FALSE;
+
+ for (i = 0; i < valueCount; i++)
+ {
+ LPSPropValue value = values;
+
+ values++;
+ if (value->ulPropTag == propTag)
+ {
+ *prop = value;
+ propHasBeenAssignedTo = JNI_TRUE;
+ }
+ else
+ MAPIFreeBuffer(value);
+ }
+ if (!propHasBeenAssignedTo)
+ hResult = MAPI_E_NOT_FOUND;
+ }
+ return hResult;
+}
+
+/**
+ * Creates a One-Off entry to register an email address.
+ *
+ * @param displayName The display name corresponding to the new email address.
+ * @param addressType The address type corresponding to the new email address
+ * (SMTP).
+ * @param emailAddress The email address string.
+ * @param oneOffEntryIdLength The length of the One-Off entry id once created.
+ * @param oneOffEntriId Pointer used to store the One-Off entry id created.
+ *
+ * @return S_OK if everything was fine. MAPI_E_INVALID_PARAMETER, if there was
+ * an invalid argument. MAPI_E_CALL_FAILED otherwise.
+ */
+HRESULT MsOutlookAddrBookContactQuery_buildOneOff
+ (LPWSTR displayName, LPWSTR addressType, LPWSTR emailAddress,
+ ULONG* oneOffEntryIdLength, LPBYTE* oneOffEntryId)
+{
+ if (!displayName || !addressType || !emailAddress
+ || !oneOffEntryIdLength || !oneOffEntryId)
+ {
+ return MAPI_E_INVALID_PARAMETER;
+ }
+
+ // Calculate how large our EID will be
+ size_t cbDisplayName
+ = wcslen(displayName) * sizeof(WCHAR) + sizeof(WCHAR);
+ size_t cbAddressType
+ = wcslen(addressType) * sizeof(WCHAR) + sizeof(WCHAR);
+ size_t cbEmailAddress
+ = wcslen(emailAddress) * sizeof(WCHAR) + sizeof(WCHAR);
+ size_t cbEID = sizeof(ONEOFFENTRYID)
+ + cbDisplayName + cbAddressType + cbEmailAddress;
+
+ // Allocate our buffer
+ MsOutlookAddrBookContactQuery_LPONEOFFENTRYID lpEID
+ = (MsOutlookAddrBookContactQuery_LPONEOFFENTRYID)
+ malloc(cbEID * sizeof(BYTE));
+
+ // Populate it
+ if (lpEID)
+ {
+ memset(lpEID,0,cbEID);
+ lpEID->muid = MsOutlookAddrBookContactQuery_MuidOneOffEntryID;
+ lpEID->ulBitMask |= MAPI_UNICODE; // Set U, the unicode bit
+ lpEID->ulBitMask |= OOP_DONT_LOOKUP; // Set L, the no lookup bit
+ lpEID->ulBitMask |= MAPI_SEND_NO_RICH_INFO; // Set M, the mime bit
+ // Set the encoding format
+ lpEID->ulBitMask |=
+ ENCODING_PREFERENCE | ENCODING_MIME | BODY_ENCODING_TEXT_AND_HTML;
+
+ LPBYTE pb = lpEID->bData;
+ // this will copy the string and the NULL terminator together
+ memcpy(pb, displayName, cbDisplayName);
+ pb += cbDisplayName;
+ memcpy(pb, addressType, cbAddressType);
+ pb += cbAddressType;
+ memcpy(pb, emailAddress, cbEmailAddress);
+ pb += cbEmailAddress;
+
+ // Return it
+ *oneOffEntryIdLength = cbEID;
+ *oneOffEntryId = (LPBYTE) lpEID;
+
+ return S_OK;
+ }
+
+ return MAPI_E_CALL_FAILED;
+}
+
+/**
+ * Creates or modifies an email address.
+ *
+ * @param contact The contact to add the email address.
+ * @param displayName The display name for the email address.
+ * @param addressType the address type for the email address (SMTP).
+ * @param emailAddress The email address.
+ * @param originalDisplayName The original display name for the email address.
+ * @param providerEmailList A list of values used to define which email address
+ * is set.
+ * @param providerArrayType A bitsmask used to define which email address is
+ * set.
+ * @param propIds A list of property to set for this email address.
+ * @param nbPropId The number of properties contained in propIds.
+ *
+ * @return S_OK if the email address was created/modified.
+ */
+HRESULT MsOutlookAddrBookContactQuery_createEmailAddress
+ (LPMESSAGE contact, LPWSTR displayName, LPWSTR addressType,
+ LPWSTR emailAddress, LPWSTR originalDisplayName, LONG providerEmailList[],
+ LONG providerArrayType, ULONG propIds[], int nbPropId)
+{
+ SBinary parentId;
+ parentId.cb = 0;
+ MsOutlookAddrBookContactQuery_getBinaryProp(
+ (LPMAPIPROP) contact,
+ 0x0E09, //PR_PARENT_ENTRYID,
+ &parentId);
+ LPMAPIFOLDER parentEntry
+ = (LPMAPIFOLDER) MsOutlookAddrBookContactQuery_openEntryId(
+ parentId.cb, (LPENTRYID) parentId.lpb);
+ HRESULT hRes = S_OK;
+ MAPINAMEID rgnmid[nbPropId];
+ LPMAPINAMEID rgpnmid[nbPropId];
+ LPSPropTagArray lpNamedPropTags = NULL;
+
+ for(int i = 0 ; i < nbPropId ; i++)
+ {
+ rgnmid[i].lpguid = (LPGUID) &PSETID_Address;
+ rgnmid[i].ulKind = MNID_ID;
+ rgnmid[i].Kind.lID = propIds[i];
+ rgpnmid[i] = &rgnmid[i];
+ }
+ hRes = parentEntry->GetIDsFromNames(
+ nbPropId,
+ (LPMAPINAMEID*) &rgpnmid,
+ 0,
+ &lpNamedPropTags);
+
+ if (SUCCEEDED(hRes) && lpNamedPropTags)
+ {
+ SPropValue spvProps[nbPropId];
+ spvProps[0].ulPropTag
+ = CHANGE_PROP_TYPE(lpNamedPropTags->aulPropTag[0], PT_MV_LONG);
+ spvProps[1].ulPropTag
+ = CHANGE_PROP_TYPE(lpNamedPropTags->aulPropTag[1], PT_LONG);
+ spvProps[2].ulPropTag
+ = CHANGE_PROP_TYPE(lpNamedPropTags->aulPropTag[2], PT_UNICODE);
+ spvProps[3].ulPropTag
+ = CHANGE_PROP_TYPE(lpNamedPropTags->aulPropTag[3], PT_UNICODE);
+ spvProps[4].ulPropTag
+ = CHANGE_PROP_TYPE(lpNamedPropTags->aulPropTag[4], PT_UNICODE);
+ spvProps[5].ulPropTag
+ = CHANGE_PROP_TYPE(lpNamedPropTags->aulPropTag[5], PT_UNICODE);
+ spvProps[6].ulPropTag
+ = CHANGE_PROP_TYPE(lpNamedPropTags->aulPropTag[6], PT_BINARY);
+
+ spvProps[0].Value.MVl.cValues = 1;
+ spvProps[0].Value.MVl.lpl = providerEmailList;
+
+ spvProps[1].Value.l = providerArrayType;
+
+ spvProps[2].Value.lpszW = displayName;
+ spvProps[3].Value.lpszW = addressType;
+ spvProps[4].Value.lpszW = emailAddress;
+ spvProps[5].Value.lpszW = originalDisplayName;
+
+ hRes = MsOutlookAddrBookContactQuery_buildOneOff(
+ displayName,
+ addressType,
+ emailAddress,
+ &spvProps[6].Value.bin.cb,
+ &spvProps[6].Value.bin.lpb);
+
+ if (SUCCEEDED(hRes))
+ {
+ hRes = contact->SetProps(nbPropId, spvProps, NULL);
+ if (SUCCEEDED(hRes))
+ {
+ hRes = contact->SaveChanges(FORCE_SAVE);
+ }
+ }
+
+ if (spvProps[6].Value.bin.lpb)
+ free(spvProps[6].Value.bin.lpb);
+ }
+
+ MAPIFreeBuffer(lpNamedPropTags);
+ MAPIFreeBuffer(&parentId);
+ parentEntry->Release();
+
+ return hRes;
+}
+
+static jboolean
+MsOutlookAddrBookContactQuery_foreachContactInMsgStoresTable
+ (LPMAPISESSION mapiSession,
+ JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
+{
+ HRESULT hResult;
+ LPMAPITABLE msgStoresTable = NULL;
+ jboolean proceed;
+
+ hResult = mapiSession->GetMsgStoresTable(0, &msgStoresTable);
+ if (HR_SUCCEEDED(hResult) && msgStoresTable)
+ {
+ proceed
+ = MsOutlookAddrBookContactQuery_foreachRowInTable(
+ msgStoresTable,
+ MsOutlookAddrBookContactQuery_onForeachContactInMsgStoresTableRow,
+ (LPUNKNOWN) mapiSession,
+ jniEnv, query, callback, callbackMethodID);
+ msgStoresTable->Release();
+ }
+ else
+ {
+ MsOutlookMAPIHResultException_throwNew(
+ jniEnv,
+ hResult,
+ __FILE__, __LINE__);
+ proceed = JNI_TRUE;
+ }
+ return proceed;
+}
+
+static jboolean
+MsOutlookAddrBookContactQuery_foreachMailUser
+ (ULONG objType, LPUNKNOWN iUnknown,
+ JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
+{
+ jboolean proceed = JNI_TRUE;
+
+ switch (objType)
+ {
+ case MAPI_ABCONT:
+ case MAPI_FOLDER:
+ {
+ LPMAPICONTAINER mapiContainer = (LPMAPICONTAINER) iUnknown;
+
+ HRESULT hResult;
+ LPMAPITABLE mapiTable;
+
+ /* Look for MAPI_MAILUSER through the contents. */
+ mapiTable = NULL;
+ hResult = mapiContainer->GetContentsTable(0, &mapiTable);
+ if (HR_SUCCEEDED(hResult) && mapiTable)
+ {
+ proceed
+ = MsOutlookAddrBookContactQuery_foreachMailUserInContainerTable(
+ mapiContainer, mapiTable,
+ jniEnv, query, callback, callbackMethodID);
+ mapiTable->Release();
+ }
+
+ /* Drill down the hierarchy. */
+ if (proceed)
+ {
+ mapiTable = NULL;
+ hResult = mapiContainer->GetHierarchyTable(0, &mapiTable);
+ if (HR_SUCCEEDED(hResult) && mapiTable)
+ {
+ proceed
+ = MsOutlookAddrBookContactQuery_foreachMailUserInContainerTable(
+ mapiContainer, mapiTable,
+ jniEnv, query, callback, callbackMethodID);
+ mapiTable->Release();
+ }
+ }
+
+ break;
+ }
+
+ case MAPI_MAILUSER:
+ case MAPI_MESSAGE:
+ {
+ if (MsOutlookAddrBookContactQuery_mailUserMatches(
+ (LPMAPIPROP) iUnknown,
+ jniEnv, query))
+ {
+ LPSTR contactId = MsOutlookAddrBookContactQuery_getContactId(
+ (LPMAPIPROP) iUnknown);
+ jstring value = jniEnv->NewStringUTF(contactId);
+
+ // Report the MAPI_MAILUSER to the callback.
+ proceed
+ = jniEnv->CallBooleanMethod(callback, callbackMethodID, value);
+
+ ::free(contactId);
+ contactId = NULL;
+
+ /*
+ * XXX When an exception is thrown in the callback, does proceed get
+ * assigned JNI_FALSE?
+ */
+ if (proceed && jniEnv->ExceptionCheck())
+ proceed = JNI_FALSE;
+ }
+ break;
+ }
+ }
+ return proceed;
+}
+
+static jboolean
+MsOutlookAddrBookContactQuery_foreachMailUserInAddressBook
+ (LPMAPISESSION mapiSession,
+ JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
+{
+ HRESULT hResult;
+ LPADRBOOK adrBook;
+ jboolean proceed;
+
+ hResult = mapiSession->OpenAddressBook(0, NULL, AB_NO_DIALOG, &adrBook);
+ if (HR_SUCCEEDED(hResult))
+ {
+ ULONG objType;
+ LPUNKNOWN iUnknown;
+
+ hResult = adrBook->OpenEntry(
+ 0,
+ NULL,
+ NULL,
+ MsOutlookAddrBookContactQuery_openEntryUlFlags,
+ &objType,
+ &iUnknown);
+
+ if (HR_SUCCEEDED(hResult))
+ {
+ proceed
+ = MsOutlookAddrBookContactQuery_foreachMailUser(
+ objType, iUnknown,
+ jniEnv, query, callback, callbackMethodID);
+
+ iUnknown->Release();
+ }
+ else
+ {
+ MsOutlookMAPIHResultException_throwNew(
+ jniEnv,
+ hResult,
+ __FILE__, __LINE__);
+ proceed = JNI_TRUE;
+ }
+
+ adrBook->Release();
+ }
+ else
+ {
+ MsOutlookMAPIHResultException_throwNew(
+ jniEnv,
+ hResult,
+ __FILE__, __LINE__);
+ proceed = JNI_TRUE;
+ }
+ return proceed;
+}
+
+static jboolean
+MsOutlookAddrBookContactQuery_foreachMailUserInContainerTable
+ (LPMAPICONTAINER mapiContainer, LPMAPITABLE mapiTable,
+ JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
+{
+ return
+ MsOutlookAddrBookContactQuery_foreachRowInTable(
+ mapiTable,
+ MsOutlookAddrBookContactQuery_onForeachMailUserInContainerTableRow,
+ (LPUNKNOWN) mapiContainer,
+ jniEnv, query, callback, callbackMethodID);
+}
+
+static jboolean
+MsOutlookAddrBookContactQuery_foreachRowInTable
+ (LPMAPITABLE mapiTable,
+ MsOutlookAddrBookContactQuery_ForeachRowInTableCallback rowCallback,
+ LPUNKNOWN iUnknown,
+ JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
+{
+ HRESULT hResult;
+ jboolean proceed;
+
+ hResult = mapiTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
+ if (HR_SUCCEEDED(hResult))
+ {
+ proceed = JNI_TRUE;
+ while (proceed)
+ {
+ LPSRowSet rows;
+
+ hResult = mapiTable->QueryRows(1, 0, &rows);
+ if (HR_FAILED(hResult))
+ break;
+
+ if (rows->cRows == 1)
+ {
+ ULONG i;
+ LPSRow row = rows->aRow;
+ 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)
+ {
+ LPENTRYID entryID = NULL;
+
+ if (S_OK
+ == MAPIAllocateBuffer(
+ entryIDBinary.cb,
+ (void **) &entryID))
+ {
+ CopyMemory(
+ entryID,
+ entryIDBinary.lpb,
+ entryIDBinary.cb);
+
+ /*
+ * We no longer need the rows at this point so free them
+ * before we drill down the hierarchy and allocate even
+ * more rows.
+ */
+ MsOutlookAddrBookContactQuery_freeSRowSet(rows);
+
+ proceed
+ = rowCallback(
+ iUnknown,
+ entryIDBinary.cb, entryID, objType,
+ jniEnv, query, callback, callbackMethodID);
+
+ MAPIFreeBuffer(entryID);
+
+ if (proceed && jniEnv->ExceptionCheck())
+ proceed = JNI_FALSE;
+ }
+ else
+ MsOutlookAddrBookContactQuery_freeSRowSet(rows);
+ }
+ else
+ MsOutlookAddrBookContactQuery_freeSRowSet(rows);
+ }
+ else
+ {
+ MAPIFreeBuffer(rows);
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* We've failed but other parts of the hierarchy may still succeed. */
+ proceed = JNI_TRUE;
+ }
+ return proceed;
+}
+
+static void
+MsOutlookAddrBookContactQuery_freeSRowSet(LPSRowSet rows)
+{
+ ULONG i;
+
+ for (i = 0; i < rows->cRows; i++)
+ {
+ LPSRow row = (rows->aRow) + i;
+ ULONG j;
+
+ for (j = 0; j < row->cValues; j++)
+ {
+ LPSPropValue prop = (row->lpProps) + j;
+
+ MAPIFreeBuffer(prop);
+ }
+ }
+ MAPIFreeBuffer(rows);
+}
+
+static jbyteArray
+MsOutlookAddrBookContactQuery_getAttachmentContactPhoto
+ (LPMESSAGE message, JNIEnv *jniEnv)
+{
+ HRESULT hResult;
+ LPMAPITABLE attachmentTable;
+ jbyteArray attachmentContactPhoto = NULL;
+
+ hResult = message->GetAttachmentTable(0, &attachmentTable);
+ if (HR_SUCCEEDED(hResult))
+ {
+ hResult = attachmentTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
+ if (HR_SUCCEEDED(hResult))
+ {
+ while (1)
+ {
+ LPSRowSet rows;
+
+ hResult = attachmentTable->QueryRows(1, 0, &rows);
+ if (HR_FAILED(hResult))
+ break;
+
+ if (rows->cRows == 1)
+ {
+ ULONG i;
+ LPSRow row = rows->aRow;
+ jboolean isAttachmentContactPhotoRow = JNI_FALSE;
+ jboolean hasAttachmentContactPhoto = JNI_FALSE;
+ ULONG attachNum = 0;
+ LONG attachMethod = NO_ATTACHMENT;
+
+ for (i = 0; i < row->cValues; i++)
+ {
+ LPSPropValue prop = (row->lpProps) + i;
+
+ switch (prop->ulPropTag)
+ {
+ case PR_ATTACHMENT_CONTACTPHOTO:
+ isAttachmentContactPhotoRow = JNI_TRUE;
+ hasAttachmentContactPhoto
+ = prop->Value.b ? JNI_TRUE : JNI_FALSE;
+ break;
+ case PR_ATTACH_METHOD:
+ attachMethod = prop->Value.l;
+ break;
+ case PR_ATTACH_NUM:
+ attachNum = prop->Value.l;
+ break;
+ }
+ }
+
+ MsOutlookAddrBookContactQuery_freeSRowSet(rows);
+
+ /*
+ * As the reference says and as discovered in practice,
+ * PR_ATTACHMENT_CONTACTPHOTO is sometimes in IAttach.
+ */
+ if ((isAttachmentContactPhotoRow
+ && hasAttachmentContactPhoto)
+ || !isAttachmentContactPhotoRow)
+ {
+ attachmentContactPhoto
+ = MsOutlookAddrBookContactQuery_readAttachment(
+ message,
+ attachMethod, attachNum,
+ jniEnv,
+ (!isAttachmentContactPhotoRow)
+ ? PR_ATTACHMENT_CONTACTPHOTO
+ : PROP_TAG(PT_UNSPECIFIED, 0));
+ }
+ if (isAttachmentContactPhotoRow
+ || attachmentContactPhoto)
+ {
+ /*
+ * The reference says there can be only 1
+ * PR_ATTACHMENT_CONTACTPHOTO.
+ */
+ break;
+ }
+ }
+ else
+ {
+ MAPIFreeBuffer(rows);
+ break;
+ }
+ }
+ }
+
+ attachmentTable->Release();
+ }
+ return attachmentContactPhoto;
+}
+
+/**
+ * Gets a binary property for a given entry.
+ *
+ * @param entry The entry to red the property from.
+ * @param propId The property identifier.
+ * @param binaryProp A pointer to a SBinary to store the property value
+ * retrieved.
+ */
+void MsOutlookAddrBookContactQuery_getBinaryProp
+ (LPMAPIPROP entry, ULONG propId, LPSBinary binaryProp)
+{
+ SPropTagArray tagArray;
+ tagArray.cValues = 1;
+ tagArray.aulPropTag[0] = PROP_TAG(PT_BINARY, propId);
+
+ ULONG propCount;
+ LPSPropValue propArray;
+ HRESULT hResult = entry->GetProps(
+ &tagArray,
+ 0x80000000, // MAPI_UNICODE.
+ &propCount,
+ &propArray);
+
+ if (HR_SUCCEEDED(hResult))
+ {
+ SPropValue prop = propArray[0];
+ binaryProp->cb = prop.Value.bin.cb;
+ binaryProp->lpb = prop.Value.bin.lpb;
+ }
+}
+
+
+static HRESULT
+MsOutlookAddrBookContactQuery_getContactsFolderEntryID
+ (LPMDB msgStore,
+ ULONG folderEntryIDByteCount, LPENTRYID folderEntryID,
+ ULONG *contactsFolderEntryIDByteCount, LPENTRYID *contactsFolderEntryID)
+{
+ HRESULT hResult;
+ ULONG objType;
+ LPUNKNOWN folder;
+
+ hResult = msgStore->OpenEntry(
+ folderEntryIDByteCount,
+ folderEntryID,
+ NULL,
+ MsOutlookAddrBookContactQuery_openEntryUlFlags,
+ &objType,
+ &folder);
+
+ if (HR_SUCCEEDED(hResult))
+ {
+ LPSPropValue prop;
+
+ hResult
+ = MsOutlookAddrBookContactQuery_HrGetOneProp(
+ (LPMAPIPROP) folder,
+ 0x36D10102 /* PR_IPM_CONTACT_ENTRYID */,
+ &prop);
+ if (HR_SUCCEEDED(hResult))
+ {
+ LPSBinary bin = &(prop->Value.bin);
+ if (S_OK
+ == MAPIAllocateBuffer(
+ bin->cb,
+ (void **) contactsFolderEntryID))
+ {
+ hResult = S_OK;
+ *contactsFolderEntryIDByteCount = bin->cb;
+ CopyMemory(*contactsFolderEntryID, bin->lpb, bin->cb);
+ }
+ else
+ hResult = MAPI_E_NOT_ENOUGH_MEMORY;
+ MAPIFreeBuffer(prop);
+ }
+ folder->Release();
+ }
+ return hResult;
+}
+
+/**
+ * Returns a pointer to the default contact folder.
+ *
+ * @return A pointer to the default contact folder. Or NULL if unavailable.
+ */
+LPMAPIFOLDER MsOutlookAddrBookContactQuery_getDefaultContactFolderId(void)
+{
+ LPMAPIFOLDER rootFolder = NULL;
+ LPMDB msgStore = MsOutlookAddrBookContactQuery_getDefaultMsgStores();
+
+ if(msgStore != NULL)
+ {
+ ULONG entryIdLength = 0;
+ LPENTRYID receiveFolderEntryID = NULL;
+
+ ULONG contactEntryIdLength = 0;
+ LPENTRYID contactsFolderEntryID = NULL;
+
+ HRESULT hResult = msgStore->GetReceiveFolder(
+ NULL,
+ 0,
+ &entryIdLength,
+ &receiveFolderEntryID,
+ NULL);
+
+ if(HR_SUCCEEDED(hResult))
+ {
+ hResult = MsOutlookAddrBookContactQuery_getContactsFolderEntryID(
+ msgStore,
+ entryIdLength,
+ receiveFolderEntryID,
+ &contactEntryIdLength,
+ &contactsFolderEntryID);
+ MAPIFreeBuffer(receiveFolderEntryID);
+ }
+
+ ULONG objType;
+ hResult = msgStore->OpenEntry(
+ contactEntryIdLength,
+ contactsFolderEntryID,
+ NULL,
+ MsOutlookAddrBookContactQuery_openEntryUlFlags,
+ &objType,
+ (LPUNKNOWN *) &rootFolder);
+ if(contactsFolderEntryID != NULL)
+ {
+ MAPIFreeBuffer(contactsFolderEntryID);
+ }
+
+ msgStore->Release();
+ }
+
+ return rootFolder;
+}
+
+/**
+ * Open the default message store.
+ *
+ * @return The default message store. Or NULL if unavailable.
+ */
+LPMDB MsOutlookAddrBookContactQuery_getDefaultMsgStores(void)
+{
+ LPMDB msgStore = NULL;
+ LPMAPITABLE msgStoresTable;
+ LPMAPISESSION mapiSession = MAPISession_getMapiSession();
+ HRESULT hResult;
+
+ hResult = mapiSession->GetMsgStoresTable(0, &msgStoresTable);
+ if(HR_SUCCEEDED(hResult) && msgStoresTable)
+ {
+ hResult = msgStoresTable->SeekRow(BOOKMARK_BEGINNING, 0, NULL);
+ if (HR_SUCCEEDED(hResult))
+ {
+ LPSRowSet rows;
+
+ SBitMaskRestriction bitMaskRestriction;
+ bitMaskRestriction.relBMR = BMR_NEZ;
+ bitMaskRestriction.ulPropTag = PR_RESOURCE_FLAGS;
+ bitMaskRestriction.ulMask = STATUS_DEFAULT_STORE;
+
+ SRestriction defaultFolderRestriction;
+ memset(
+ &defaultFolderRestriction,
+ 0,
+ sizeof(defaultFolderRestriction));
+ defaultFolderRestriction.rt = RES_BITMASK;
+ defaultFolderRestriction.res.resBitMask = bitMaskRestriction;
+ hResult = HrQueryAllRows(
+ msgStoresTable,
+ NULL,
+ &defaultFolderRestriction, // restriction
+ NULL,
+ 0,
+ &rows);
+ if (HR_SUCCEEDED(hResult) && rows->cRows == 1)
+ {
+ SRow row = rows->aRow[0];
+ SBinary entryIDBinary = { 0, NULL };
+
+ for(ULONG i = 0; i < row.cValues; ++i)
+ {
+ LPSPropValue prop = (row.lpProps) + i;
+ switch (prop->ulPropTag)
+ {
+ case PR_ENTRYID:
+ entryIDBinary = prop->Value.bin;
+ break;
+ }
+ }
+
+ if(entryIDBinary.cb && entryIDBinary.lpb)
+ {
+ hResult = mapiSession->OpenMsgStore(
+ 0,
+ entryIDBinary.cb,
+ (LPENTRYID) entryIDBinary.lpb,
+ NULL,
+ MDB_NO_MAIL
+ | MsOutlookAddrBookContactQuery_openEntryUlFlags,
+ &msgStore);
+ }
+ }
+ FreeProws(rows);
+ }
+ msgStoresTable->Release();
+ }
+
+ return msgStore;
+}
+
+static ULONG
+MsOutlookAddrBookContactQuery_getPropTagFromLid(LPMAPIPROP mapiProp, LONG lid)
+{
+ GUID PSETID_Address
+ = {0x00062004, 0x0000, 0x0000,
+ {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
+ MAPINAMEID propName;
+ LPMAPINAMEID propNamePtr;
+ HRESULT hResult;
+ LPSPropTagArray propTagArray;
+
+ propName.lpguid = (LPGUID) &PSETID_Address;
+ propName.ulKind = MNID_ID;
+ propName.Kind.lID = lid;
+ propNamePtr = &propName;
+ hResult
+ = mapiProp->GetIDsFromNames(
+ 1, &propNamePtr,
+ 0,
+ &propTagArray);
+ if (HR_SUCCEEDED(hResult) && (1 == propTagArray->cValues))
+ {
+ ULONG propTag = propTagArray->aulPropTag[0];
+
+ if (PT_ERROR == PROP_TYPE(propTag))
+ propTag = PROP_TAG(PT_UNSPECIFIED, lid);
+ MAPIFreeBuffer(propTagArray);
+ return propTag;
+ }
+ else
+ return PROP_TAG(PT_UNSPECIFIED, lid);
+}
+
+static jboolean
+MsOutlookAddrBookContactQuery_mailUserMatches
+ (LPMAPIPROP mailUser, JNIEnv *jniEnv, jstring query)
+{
+ // TODO Auto-generated method stub
+ return JNI_TRUE;
+}
+
+static jboolean
+MsOutlookAddrBookContactQuery_onForeachContactInMsgStoresTableRow
+ (LPUNKNOWN mapiSession,
+ ULONG entryIDByteCount, LPENTRYID entryID, ULONG objType,
+ JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
+{
+ HRESULT hResult;
+ LPMDB msgStore;
+ jboolean proceed;
+
+ hResult
+ = ((LPMAPISESSION) mapiSession)->OpenMsgStore(
+ 0,
+ entryIDByteCount, entryID,
+ NULL,
+ MDB_NO_MAIL | MsOutlookAddrBookContactQuery_openEntryUlFlags,
+ &msgStore);
+ if (HR_SUCCEEDED(hResult))
+ {
+ LPENTRYID receiveFolderEntryID = NULL;
+ ULONG contactsFolderEntryIDByteCount = 0;
+ LPENTRYID contactsFolderEntryID = NULL;
+
+ hResult
+ = msgStore->GetReceiveFolder(
+ NULL,
+ 0,
+ &entryIDByteCount, &receiveFolderEntryID,
+ NULL);
+ if (HR_SUCCEEDED(hResult))
+ {
+ hResult
+ = MsOutlookAddrBookContactQuery_getContactsFolderEntryID(
+ msgStore,
+ entryIDByteCount, receiveFolderEntryID,
+ &contactsFolderEntryIDByteCount, &contactsFolderEntryID);
+ MAPIFreeBuffer(receiveFolderEntryID);
+ }
+ if (HR_FAILED(hResult))
+ {
+ hResult
+ = MsOutlookAddrBookContactQuery_getContactsFolderEntryID(
+ msgStore,
+ 0, NULL,
+ &contactsFolderEntryIDByteCount, &contactsFolderEntryID);
+ }
+ if (HR_SUCCEEDED(hResult))
+ {
+ ULONG contactsFolderObjType;
+ LPUNKNOWN contactsFolder;
+
+ hResult
+ = msgStore->OpenEntry(
+ contactsFolderEntryIDByteCount, contactsFolderEntryID,
+ NULL,
+ MsOutlookAddrBookContactQuery_openEntryUlFlags,
+ &contactsFolderObjType, &contactsFolder);
+ if (HR_SUCCEEDED(hResult))
+ {
+ proceed
+ = MsOutlookAddrBookContactQuery_foreachMailUser(
+ contactsFolderObjType, contactsFolder,
+ jniEnv, query, callback, callbackMethodID);
+ contactsFolder->Release();
+ }
+ else
+ {
+ /*
+ * We've failed but other parts of the hierarchy may still
+ * succeed.
+ */
+ proceed = JNI_TRUE;
+ }
+ MAPIFreeBuffer(contactsFolderEntryID);
+ }
+ else
+ {
+ /*
+ * We've failed but other parts of the hierarchy may still succeed.
+ */
+ proceed = JNI_TRUE;
+ }
+ msgStore->Release();
+ }
+ else
+ {
+ /* We've failed but other parts of the hierarchy may still succeed. */
+ proceed = JNI_TRUE;
+ }
+ return proceed;
+}
+
+static jboolean
+MsOutlookAddrBookContactQuery_onForeachMailUserInContainerTableRow
+ (LPUNKNOWN mapiContainer,
+ ULONG entryIDByteCount, LPENTRYID entryID, ULONG objType,
+ JNIEnv *jniEnv, jstring query, jobject callback, jmethodID callbackMethodID)
+{
+ HRESULT hResult;
+ LPUNKNOWN iUnknown;
+ jboolean proceed;
+
+ // Make write failed and image load.
+ hResult
+ = ((LPMAPICONTAINER) mapiContainer)->OpenEntry(
+ entryIDByteCount, entryID,
+ NULL,
+ MsOutlookAddrBookContactQuery_openEntryUlFlags,
+ &objType, &iUnknown);
+ if (HR_SUCCEEDED(hResult))
+ {
+ proceed
+ = MsOutlookAddrBookContactQuery_foreachMailUser(
+ objType, iUnknown,
+ jniEnv, query, callback, callbackMethodID);
+ iUnknown->Release();
+ }
+ else
+ {
+ /* We've failed but other parts of the hierarchy may still succeed. */
+ proceed = JNI_TRUE;
+ }
+ return proceed;
+}
+
+/**
+ * Opens an object based on the string representation of its entry id.
+ *
+ * @param entryIdStr The identifier of the entry to open.
+ *
+ * @return A pointer to the opened entry. NULL if anything goes wrong.
+ */
+LPUNKNOWN MsOutlookAddrBookContactQuery_openEntryIdStr(const char* entryIdStr)
+{
+ LPUNKNOWN entry = NULL;
+ ULONG entryIdSize = strlen(entryIdStr) / 2;
+ LPENTRYID entryId = (LPENTRYID) malloc(entryIdSize * sizeof(char));
+
+ if(FBinFromHex((LPSTR) entryIdStr, (LPBYTE) entryId))
+ {
+ entry = MsOutlookAddrBookContactQuery_openEntryId(entryIdSize, entryId);
+ }
+
+ ::free(entryId);
+ return entry;
+}
+
+/**
+ * Opens an object based on its entry id.
+ *
+ * @param entryIdSize The size of the identifier of the entry to open.
+ * @param entryId The identifier of the entry to open.
+ *
+ * @return A pointer to the opened entry. NULL if anything goes wrong.
+ */
+LPUNKNOWN MsOutlookAddrBookContactQuery_openEntryId
+ (ULONG entryIdSize, LPENTRYID entryId)
+{
+ LPMAPISESSION mapiSession = MAPISession_getMapiSession();
+ ULONG objType;
+ LPUNKNOWN entry = NULL;
+
+ mapiSession->OpenEntry(
+ entryIdSize,
+ entryId,
+ NULL,
+ MAPI_BEST_ACCESS,
+ &objType,
+ &entry);
+
+ return entry;
+}
+
+static jbyteArray
+MsOutlookAddrBookContactQuery_readAttachment
+ (LPMESSAGE message, LONG method, ULONG num, JNIEnv *jniEnv, ULONG cond)
+{
+ jbyteArray attachment = NULL;
+
+ if (ATTACH_BY_VALUE == method)
+ {
+ HRESULT hResult;
+ LPATTACH attach;
+
+ hResult = message->OpenAttach(num, NULL, 0, &attach);
+ if (HR_SUCCEEDED(hResult))
+ {
+ IStream *stream = NULL;
+
+ if (PT_BOOLEAN == PROP_TYPE(cond))
+ {
+ LPSPropValue condValue;
+
+ hResult = MsOutlookAddrBookContactQuery_HrGetOneProp(
+ (LPMAPIPROP) attach,
+ cond,
+ &condValue);
+ if (HR_SUCCEEDED(hResult))
+ {
+ if ((PT_BOOLEAN != PROP_TYPE(condValue->ulPropTag))
+ || !(condValue->Value.b))
+ hResult = MAPI_E_NOT_FOUND;
+ MAPIFreeBuffer(condValue);
+ }
+ }
+
+ if (HR_SUCCEEDED(hResult))
+ {
+ hResult
+ = ((LPMAPIPROP) attach)->OpenProperty(
+ PR_ATTACH_DATA_BIN,
+ &IID_IStream, 0,
+ 0,
+ (LPUNKNOWN *) &stream);
+ }
+ if (HR_SUCCEEDED(hResult) && stream)
+ {
+ STATSTG statstg;
+ ULONGLONG length;
+
+ hResult = stream->Stat(&statstg, STATFLAG_NONAME);
+ if ((S_OK == hResult) && ((length = statstg.cbSize.QuadPart)))
+ {
+ attachment = jniEnv->NewByteArray((jsize) length);
+ if (attachment)
+ {
+ jbyte *bytes
+ = jniEnv->GetByteArrayElements(attachment, NULL);
+
+ if (bytes)
+ {
+ ULONG read;
+ jint mode;
+
+ hResult
+ = stream->Read(bytes, (ULONG) length, &read);
+ mode
+ = ((S_OK == hResult) || (S_FALSE == hResult))
+ ? 0
+ : JNI_ABORT;
+ jniEnv->ReleaseByteArrayElements(
+ attachment, bytes,
+ mode);
+ if (0 != mode)
+ attachment = NULL;
+ }
+ }
+ }
+
+ stream->Release();
+ }
+
+ attach->Release();
+ }
+ }
+ return attachment;
+}
+
+/**
+ * Retrieves a string representation of the contact id. This string must be
+ * freed by the caller.
+ *
+ * @param contact A pointer to the instance of the contact.
+ *
+ * @return A string representation of the contact id. NULL if failed. This
+ * string must be freed by yhe caller.
+ */
+LPSTR MsOutlookAddrBookContactQuery_getContactId(LPMAPIPROP contact)
+{
+ LPSTR entryId = NULL;
+
+ SBinary binaryProp;
+ binaryProp.cb = 0;
+ MsOutlookAddrBookContactQuery_getBinaryProp(contact, 0x0FFF, &binaryProp);
+
+ if(binaryProp.cb != 0)
+ {
+ entryId = (LPSTR)::malloc(binaryProp.cb * 2 + 1);
+ HexFromBin(binaryProp.lpb, binaryProp.cb, entryId);
+ }
+
+ return entryId;
+}
diff --git a/src/native/addrbook/msoutlook/net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.h b/src/native/addrbook/msoutlook/net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.h
index c1a310c..3225853 100644
--- a/src/native/addrbook/msoutlook/net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.h
+++ b/src/native/addrbook/msoutlook/net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery.h
@@ -1,36 +1,42 @@
-/* DO NOT EDIT THIS FILE - it is machine generated */
-#include <jni.h>
-/* Header for class net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery */
-
-#ifndef _Included_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery
-#define _Included_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Class: net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery
- * Method: foreachMailUser
- * Signature: (Ljava/lang/String;Lnet/java/sip/communicator/plugin/addrbook/PtrCallback;)V
- */
-JNIEXPORT void JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_foreachMailUser
- (JNIEnv *, jclass, jstring, jobject);
-
-JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_IMAPIProp_1DeleteProp
- (JNIEnv *, jclass, jlong, jstring);
-
-/*
- * Class: net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery
- * Method: IMAPIProp_GetProps
- * Signature: (J[JJ)[Ljava/lang/Object;
- */
-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);
-
-#ifdef __cplusplus
-}
-#endif
-#endif
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery */
+
+#ifndef _Included_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery
+#define _Included_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT jstring JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_createContact
+ (JNIEnv *jniEnv, jclass clazz);
+
+JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_deleteContact
+ (JNIEnv *jniEnv, jclass clazz, jstring id);
+
+/*
+ * Class: net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery
+ * Method: foreachMailUser
+ * Signature: (Ljava/lang/String;Lnet/java/sip/communicator/plugin/addrbook/PtrCallback;)V
+ */
+JNIEXPORT void JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_foreachMailUser
+ (JNIEnv *, jclass, jstring, jobject);
+
+JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_IMAPIProp_1DeleteProp
+ (JNIEnv *, jclass, jlong, jstring);
+
+/*
+ * Class: net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery
+ * Method: IMAPIProp_GetProps
+ * Signature: (J[JJ)[Ljava/lang/Object;
+ */
+JNIEXPORT jobjectArray JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_IMAPIProp_1GetProps
+ (JNIEnv *, jclass, jstring, jlongArray, jlong);
+
+JNIEXPORT jboolean JNICALL Java_net_java_sip_communicator_plugin_addrbook_msoutlook_MsOutlookAddrBookContactQuery_IMAPIProp_1SetPropString
+ (JNIEnv *, jclass, jlong, jstring, jstring);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src/native/build.xml b/src/native/build.xml
index 9a4fc00..49a34b6 100644
--- a/src/native/build.xml
+++ b/src/native/build.xml
@@ -850,7 +850,8 @@
description="Build jmsoutlookaddrbook shared library for Windows"
if="is.running.windows"
depends="init-native">
- <fail message="Outlook2010MAPIHeaders not set!" unless="Outlook2010MAPIHeaders" />
+ <!--fail message="Outlook2010MAPIHeaders not set!"
+ unless="Outlook2010MAPIHeaders" /-->
<cc
name="gcc"
objdir="${obj}"
@@ -861,7 +862,7 @@
<compilerarg value="-DWINVER=0x0502" />
<compilerarg value="-I${system.JAVA_HOME}/include" />
<compilerarg value="-I${system.JAVA_HOME}/include/win32" />
- <compilerarg value="-I${Outlook2010MAPIHeaders}" />
+ <!--compilerarg value="-I${Outlook2010MAPIHeaders}" /-->
<compilerarg value="-m32" if="cross_32" />
<compilerarg value="-m64" if="cross_64" />
<compilerarg value="-O2" />
diff --git a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactDetail.java b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactDetail.java
index 354a361..8b211c2 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactDetail.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactDetail.java
@@ -185,6 +185,13 @@ extends EditableContactDetail
}
super.setDetail(value);
+
+ EditableSourceContact sourceContact = getSourceContact();
+ if(sourceContact != null
+ && sourceContact instanceof MacOSXAddrBookSourceContact)
+ {
+ ((MacOSXAddrBookSourceContact) sourceContact).updated();
+ }
}
/**
diff --git a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactQuery.java b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactQuery.java
index 3f6126a..ebf1f03 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactQuery.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactQuery.java
@@ -1,1352 +1,1405 @@
-/*
- * 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.macosx;
-
-import java.util.*;
-import java.util.regex.*;
-
-import net.java.sip.communicator.plugin.addrbook.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.contactsource.ContactDetail.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Implements <tt>ContactQuery</tt> for the Address Book of Mac OS X.
- *
- * @author Lyubomir Marinov
- */
-public class MacOSXAddrBookContactQuery
- extends AbstractAddrBookContactQuery<MacOSXAddrBookContactSourceService>
-{
- /**
- * The <tt>Logger</tt> used by the <tt>MacOSXAddrBookContactQuery</tt> class
- * and its instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(MacOSXAddrBookContactQuery.class);
-
- /**
- * The properties of <tt>ABPerson</tt> which are to be queried by the
- * <tt>MacOSXAddrBookContactQuery</tt> instances.
- */
- public static final long[] ABPERSON_PROPERTIES
- = new long[]
- {
- kABAIMInstantProperty(),
- kABEmailProperty(),
- kABFirstNameProperty(),
- kABFirstNamePhoneticProperty(),
- kABICQInstantProperty(),
- kABJabberInstantProperty(),
- kABLastNameProperty(),
- kABLastNamePhoneticProperty(),
- kABMiddleNameProperty(),
- kABMiddleNamePhoneticProperty(),
- kABMSNInstantProperty(),
- kABNicknameProperty(),
- kABPhoneProperty(),
- kABYahooInstantProperty(),
- kABPersonFlags(),
- kABOrganizationProperty(),
- kABMaidenNameProperty(),
- kABBirthdayProperty(),
- kABJobTitleProperty(),
- kABHomePageProperty(),
- kABURLsProperty(),
- kABCalendarURIsProperty(),
- kABAddressProperty(),
- kABOtherDatesProperty(),
- kABRelatedNamesProperty(),
- kABDepartmentProperty(),
- kABNoteProperty(),
- kABTitleProperty(),
- kABSuffixProperty()
- };
-
- /**
- * The index of the <tt>kABAIMInstantProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABAIMInstantProperty = 0;
-
- /**
- * The index of the <tt>kABEmailProperty</tt> <tt>ABPerson</tt> property in
- * {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABEmailProperty = 1;
-
- /**
- * The index of the <tt>kABFirstNameProperty</tt> <tt>ABPerson</tt> property
- * in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABFirstNameProperty = 2;
-
- /**
- * The index of the <tt>kABFirstNamePhoneticProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABFirstNamePhoneticProperty = 3;
-
- /**
- * The index of the <tt>kABICQInstantProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABICQInstantProperty = 4;
-
- /**
- * The index of the <tt>kABJabberInstantProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABJabberInstantProperty = 5;
-
- /**
- * The index of the <tt>kABLastNameProperty</tt> <tt>ABPerson</tt> property
- * in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABLastNameProperty = 6;
-
- /**
- * The index of the <tt>kABLastNamePhoneticProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABLastNamePhoneticProperty = 7;
-
- /**
- * The index of the <tt>kABMiddleNameProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABMiddleNameProperty = 8;
-
- /**
- * The index of the <tt>kABMiddleNamePhoneticProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABMiddleNamePhoneticProperty = 9;
-
- /**
- * The index of the <tt>kABMSNInstantProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABMSNInstantProperty = 10;
-
- /**
- * The index of the <tt>kABNicknameProperty</tt> <tt>ABPerson</tt> property
- * in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABNicknameProperty = 11;
-
- /**
- * The index of the <tt>kABOrganizationProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABOrganizationProperty = 15;
-
- /**
- * The index of the <tt>kABPersonFlags</tt> <tt>ABPerson</tt> property in
- * {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABPersonFlags = 14;
-
- /**
- * The index of the <tt>kABPhoneProperty</tt> <tt>ABPerson</tt> property in
- * {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABPhoneProperty = 12;
-
- /**
- * The flag which indicates that an <tt>ABRecord</tt> is to be displayed as
- * a company.
- */
- public static final long kABShowAsCompany = 1;
-
- /**
- * The mask which extracts the <tt>kABShowAsXXX</tt> flag from the
- * <tt>personFlags</tt> of an <tt>ABPerson</tt>.
- */
- public static final long kABShowAsMask = 7;
-
- /**
- * The index of the <tt>kABYahooInstantProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABYahooInstantProperty = 13;
-
- /**
- * The index of the <tt>kABMaidenNameProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABMaidenNameProperty = 16;
-
- /**
- * The index of the <tt>kABBirthdayProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABBirthdayProperty = 17;
-
- /**
- * The index of the <tt>kABJobTitleProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABJobTitleProperty = 18;
-
- /**
- * The index of the <tt>kABHomePageProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABHomePageProperty = 19;
-
- /**
- * The index of the <tt>kABURLsProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABURLsProperty = 20;
-
- /**
- * The index of the <tt>kABCalendarURIsProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABCalendarURIsProperty = 21;
-
- /**
- * The index of the <tt>kABAddressProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABAddressProperty = 22;
-
- /**
- * The index of the <tt>kABOtherDatesProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABOtherDatesProperty = 23;
-
- /**
- * The index of the <tt>kABRelatedNamesProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABRelatedNamesProperty = 24;
-
- /**
- * The index of the <tt>kABDepartmentProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABDepartmentProperty = 25;
-
- /**
- * The index of the <tt>kABNoteProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABNoteProperty = 26;
-
- /**
- * The index of the <tt>kABTitleProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABTitleProperty = 27;
-
- /**
- * The index of the <tt>kABSuffixProperty</tt> <tt>ABPerson</tt>
- * property in {@link #ABPERSON_PROPERTIES}.
- */
- public static final int kABSuffixProperty = 28;
-
- /**
- * The regex which matches the superfluous parts of an <tt>ABMultiValue</tt>
- * label.
- */
- private static final Pattern LABEL_PATTERN
- = Pattern.compile(
- "kAB|Email|Phone|Label|(\\p{Punct}*)",
- Pattern.CASE_INSENSITIVE);
-
- static
- {
- System.loadLibrary("jmacosxaddrbook");
- }
-
- /**
- * Initializes a new <tt>MacOSXAddrBookContactQuery</tt> which is to perform
- * a specific <tt>query</tt> in the Address Book of Mac OS X on behalf of a
- * specific <tt>MacOSXAddrBookContactSourceService</tt>.
- *
- * @param contactSource the <tt>MacOSXAddrBookContactSourceService</tt>
- * which is to perform the new <tt>ContactQuery</tt> instance
- * @param query the <tt>Pattern</tt> for which <tt>contactSource</tt> i.e.
- * the Address Book of Mac OS X is being queried
- */
- public MacOSXAddrBookContactQuery(
- MacOSXAddrBookContactSourceService contactSource,
- Pattern query)
- {
- super(contactSource, query);
- }
-
- /**
- * Gets the <tt>imageData</tt> of a specific <tt>ABPerson</tt> instance.
- *
- * @param person the pointer to the <tt>ABPerson</tt> instance to get the
- * <tt>imageData</tt> of
- * @return the <tt>imageData</tt> of the specified <tt>ABPerson</tt>
- * instance
- */
- public static native byte[] ABPerson_imageData(long person);
-
- /**
- * Gets the values of a specific set of <tt>ABRecord</tt> properties for a
- * specific <tt>ABRecord</tt> instance.
- *
- * @param record the pointer to the <tt>ABRecord</tt> to get the property
- * values of
- * @param properties the set of <tt>ABRecord</tt> properties to get the
- * values of
- * @return the values of the specified set of <tt>ABRecord</tt> properties
- * for the specified <tt>ABRecord</tt> instance
- */
- public static native Object[] ABRecord_valuesForProperties(
- long record,
- long[] properties);
-
- /**
- * Returns the unique id of a record.
- * @param record the record which id is retrieved.
- * @return the record id.
- */
- public static native String ABRecord_uniqueId(long record);
-
- /**
- * Sets property for the supplied person id.
- * @param id the person id
- * @param property the property to use.
- * @param subPropety any sub property if available.
- * @param value the value to set.
- * @return whether the result was successfully added.
- */
- public static native boolean setProperty(
- String id, long property, String subPropety, Object value);
-
- /**
- * Remove a property.
- * @param id the person id.
- * @param property the property.
- * @return whether the result was successfully removed.
- */
- public static native boolean removeProperty(String id, long property);
-
- /**
- * Initializes a new <tt>ContactDetail</tt> instance which is to reperesent
- * a specific contact address that is the value of a specific
- * <tt>ABPerson</tt> property and, optionally, has a specific label.
- *
- * @param property the index in {@link #ABPERSON_PROPERTIES} of the
- * <tt>ABPerson</tt> property to be represented by <tt>ContactDetail</tt>
- * @param contactAddress the contact address to be represented by the new
- * <tt>ContactDetail</tt> instance
- * @param label an optional label to be added to the set of labels, if any,
- * determined by <tt>property</tt>
- * @param id The id of the detail.
- *
- * @return a new <tt>ContactDetail</tt> instance which represents the
- * specified <tt>contactAddress</tt>
- */
- private ContactDetail createContactDetail(
- int property,
- String contactAddress,
- Object label,
- String additionalProperty,
- String id)
- {
- Category c;
- SubCategory sc = null;
-
- switch (property)
- {
- case kABEmailProperty:
- c = Category.Email;
- break;
- case kABPhoneProperty:
- c = Category.Phone;
- break;
- case kABAIMInstantProperty:
- sc = SubCategory.AIM;
- c = Category.InstantMessaging;
- break;
- case kABICQInstantProperty:
- sc = SubCategory.ICQ;
- c = Category.InstantMessaging;
- break;
- case kABJabberInstantProperty:
- sc = SubCategory.Jabber;
- c = Category.InstantMessaging;
- break;
- case kABMSNInstantProperty:
- sc = SubCategory.MSN;
- c = Category.InstantMessaging;
- break;
- case kABYahooInstantProperty:
- sc = SubCategory.Yahoo;
- c = Category.InstantMessaging;
- break;
- case kABMaidenNameProperty:
- case kABFirstNameProperty:
- sc = SubCategory.Name;
- c = Category.Personal;
- break;
- case kABFirstNamePhoneticProperty:
- sc = SubCategory.Name;
- c = Category.Personal;
- break;
- case kABLastNameProperty:
- sc = SubCategory.LastName;
- c = Category.Personal;
- break;
- case kABLastNamePhoneticProperty:
- sc = SubCategory.LastName;
- c = Category.Personal;
- break;
- case kABMiddleNameProperty:
- case kABMiddleNamePhoneticProperty:
- case kABNicknameProperty:
- sc = SubCategory.Nickname;
- c = Category.Personal;
- break;
- case kABBirthdayProperty:
- case kABURLsProperty:
- case kABHomePageProperty:
- sc = SubCategory.HomePage;
- c = Category.Personal;
- break;
- case kABOtherDatesProperty:
- case kABRelatedNamesProperty:
- case kABNoteProperty:
- case kABTitleProperty:
- case kABSuffixProperty:
- c = Category.Personal;
- break;
- case kABOrganizationProperty:
- case kABJobTitleProperty:
- sc = SubCategory.JobTitle;
- c = Category.Organization;
- break;
- case kABDepartmentProperty:
- c = Category.Organization;
- sc = SubCategory.Name;
- break;
- case kABAddressProperty:
- c = Category.Address;
- break;
- default:
- c = null;
- break;
- }
-
- if (sc == null)
- {
- if (label == null)
- sc = null;
- else
- {
- sc = getSubCategoryFromLabel(label);
- }
- }
-
- SubCategory[] subCategories;
- SubCategory additionalSubCategory = null;
-
- if(additionalProperty != null)
- additionalSubCategory = getSubCategoryFromLabel(additionalProperty);
-
- if(additionalSubCategory != null)
- subCategories = new SubCategory[]
- { sc, additionalSubCategory };
- else
- subCategories = new SubCategory[]{ sc };
-
- return new MacOSXAddrBookContactDetail(
- property,
- contactAddress,
- c,
- subCategories,
- additionalProperty,
- id);
- }
-
- /**
- * Returns the SubCategory corresponding to the given label.
- *
- * @param label the label to match to a <tt>SubDirectory</tt>
- * @return the <tt>SubDirectory</tt> corresponding to the
- * given label
- */
- private SubCategory getSubCategoryFromLabel(Object label)
- {
- String labelString
- = LABEL_PATTERN.matcher((String) label).replaceAll("").trim();
-
- if (labelString.length() < 1)
- return null;
-
- SubCategory subCategory = null;
-
- if (labelString.equalsIgnoreCase("home"))
- subCategory = SubCategory.Home;
- else if (labelString.equalsIgnoreCase("work"))
- subCategory = SubCategory.Work;
- else if (labelString.equalsIgnoreCase("other"))
- subCategory = SubCategory.Other;
- else if (labelString.equalsIgnoreCase("mobile"))
- subCategory = SubCategory.Mobile;
- else if (labelString.equalsIgnoreCase("homepage"))
- subCategory = SubCategory.HomePage;
- else if (labelString.equalsIgnoreCase("street"))
- subCategory = SubCategory.Street;
- else if (labelString.equalsIgnoreCase("state"))
- subCategory = SubCategory.State;
- else if (labelString.equalsIgnoreCase("ZIP"))
- subCategory = SubCategory.PostalCode;
- else if (labelString.equalsIgnoreCase("country"))
- subCategory = SubCategory.Country;
- else if (labelString.equalsIgnoreCase("city"))
- subCategory = SubCategory.City;
- else if (labelString.equalsIgnoreCase("InstantMessageUsername"))
- subCategory = SubCategory.Nickname;
- else if (labelString.equalsIgnoreCase("workfax"))
- subCategory = SubCategory.Fax;
- else if (labelString.equalsIgnoreCase("fax"))
- subCategory = SubCategory.Fax;
-
- return subCategory;
- }
-
- /**
- * Calls back to a specific <tt>PtrCallback</tt> for each <tt>ABPerson</tt>
- * found in the Address Book of Mac OS X which matches a specific
- * <tt>String</tt> query.
- *
- * @param query the <tt>String</tt> for which the Address Book of Mac OS X
- * is to be queried. <b>Warning</b>: Ignored at the time of this writing.
- * @param callback the <tt>PtrCallback</tt> to be notified about the
- * matching <tt>ABPerson</tt>s
- */
- private static native void foreachPerson(
- String query,
- PtrCallback callback);
-
- /**
- * 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
- * @param id The id of the detail.
- *
- * @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, String id)
- {
- List<ContactDetail> contactDetails = new LinkedList<ContactDetail>();
-
- for (int i = 0; i < ABPERSON_PROPERTIES.length; i++)
- {
- int property = i;
- Object value = values[property];
-
- if (value instanceof String)
- {
- String stringValue = (String) value;
-
- if (stringValue.length() != 0)
- {
- if (kABPhoneProperty == property)
- stringValue
- = PhoneNumberI18nService.normalize(stringValue);
-
- contactDetails.add(
- setCapabilities(
- createContactDetail(
- property,
- stringValue,
- null,
- null,
- id),
- property));
- }
- }
- else if (value instanceof Object[])
- {
- parseMultiDetails(contactDetails,
- (Object[]) value,
- property,
- null,
- id);
- }
- }
- return contactDetails;
- }
-
- /**
- * Parses the multi value data resulting it in contact details.
- * @param contactDetails the result list
- * @param multiValue the values to parse.
- * @param property the current property being parsed.
- * @param id The id of the detail.
- */
- private void parseMultiDetails(
- List<ContactDetail> contactDetails,
- Object[] multiValue,
- int property,
- String label,
- String id)
- {
- if(multiValue == null)
- return;
-
- for (int multiValueIndex = 0;
- multiValueIndex < multiValue.length;
- multiValueIndex += 2)
- {
- Object subValue = multiValue[multiValueIndex];
-
- if (subValue instanceof String)
- {
- String stringSubValue = (String) subValue;
-
- if (stringSubValue.length() != 0)
- {
- if (kABPhoneProperty == property)
- {
- stringSubValue = PhoneNumberI18nService
- .normalize(stringSubValue);
- }
-
- Object l = multiValue[multiValueIndex + 1];
-
- contactDetails.add(
- setCapabilities(
- createContactDetail(
- property,
- stringSubValue,
- l,
- label,
- id),
- property));
- }
- }
- else if (subValue instanceof Object[])
- {
- String l = null;
-
- Object lObject = multiValue[multiValueIndex + 1];
- if(lObject instanceof String)
- l = (String)lObject;
-
- parseMultiDetails(contactDetails,
- (Object[]) subValue,
- property,
- l,
- id);
- }
- }
- }
-
- /**
- * Gets the <tt>displayName</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>displayName</tt> of
- * @return the <tt>displayName</tt> to be set on a <tt>SourceContact</tt>
- * which is to represent the <tt>ABPerson</tt> specified by <tt>values</tt>
- */
- static String getDisplayName(Object[] values)
- {
- long personFlags
- = (values[kABPersonFlags] instanceof Long)
- ? ((Long) values[kABPersonFlags]).longValue()
- : 0;
- String displayName;
-
- if ((personFlags & kABShowAsMask) == kABShowAsCompany)
- {
- displayName
- = (values[kABOrganizationProperty] instanceof String)
- ? (String) values[kABOrganizationProperty]
- : "";
- if (displayName.length() != 0)
- return displayName;
- }
-
- displayName
- = (values[kABNicknameProperty] instanceof String)
- ? (String) values[kABNicknameProperty]
- : "";
- if (displayName.length() != 0)
- return displayName;
-
- String firstName
- = (values[kABFirstNameProperty] instanceof String)
- ? (String) values[kABFirstNameProperty]
- : "";
-
- if ((firstName.length() == 0)
- && (values[kABFirstNamePhoneticProperty] instanceof String))
- {
- firstName = (String) values[kABFirstNamePhoneticProperty];
- }
-
- String lastName
- = (values[kABLastNameProperty] instanceof String)
- ? (String) values[kABLastNameProperty]
- : "";
-
- if ((lastName.length() == 0)
- && (values[kABLastNamePhoneticProperty] instanceof String))
- lastName = (String) values[kABLastNamePhoneticProperty];
- if ((lastName.length() == 0)
- && (values[kABMiddleNameProperty] instanceof String))
- lastName = (String) values[kABMiddleNameProperty];
- if ((lastName.length() == 0)
- && (values[kABMiddleNamePhoneticProperty] instanceof String))
- lastName = (String) values[kABMiddleNamePhoneticProperty];
-
- if (firstName.length() == 0)
- displayName = lastName;
- else
- {
- displayName
- = (lastName.length() == 0)
- ? firstName
- : (firstName + " " + lastName);
- }
- if (displayName.length() != 0)
- return displayName;
-
- for (int i = 0; i < ABPERSON_PROPERTIES.length; i++)
- {
- Object value = values[i];
-
- if (value instanceof String)
- {
- String stringValue = (String) value;
-
- if (stringValue.length() != 0)
- {
- displayName = stringValue;
- break;
- }
- }
- else if (value instanceof Object[])
- {
- Object[] multiValue = (Object[]) value;
-
- for (int multiValueIndex = 0;
- multiValueIndex < multiValue.length;
- multiValueIndex += 2)
- {
- Object subValue = multiValue[multiValueIndex];
-
- if (subValue instanceof String)
- {
- String stringSubValue = (String) subValue;
-
- if (stringSubValue.length() != 0)
- {
- displayName = stringSubValue;
- break;
- }
- }
- }
- }
- }
- return displayName;
- }
-
- /**
- * Gets the value of the <tt>kABAIMInstantProperty</tt> constant.
- *
- * @return the value of the <tt>kABAIMInstantProperty</tt> constant
- */
- public static native long kABAIMInstantProperty();
-
- /**
- * Gets the value of the <tt>kABEmailProperty</tt> constant.
- *
- * @return the value of the <tt>kABEmailProperty</tt> constant
- */
- public static native long kABEmailProperty();
-
- /**
- * Gets the value of the <tt>kABFirstNameProperty</tt> constant.
- *
- * @return the value of the <tt>kABFirstNameProperty</tt> constant
- */
- public static native long kABFirstNameProperty();
-
- /**
- * Gets the value of the <tt>kABFirstNamePhoneticProperty</tt> constant.
- *
- * @return the value of the <tt>kABFirstNamePhoneticProperty</tt> constant
- */
- public static native long kABFirstNamePhoneticProperty();
-
- /**
- * Gets the value of the <tt>kABICQInstantProperty</tt> constant.
- *
- * @return the value of the <tt>kABICQInstantProperty</tt> constant
- */
- public static native long kABICQInstantProperty();
-
- /**
- * Gets the value of the <tt>kABJabberInstantProperty</tt> constant.
- *
- * @return the value of the <tt>kABJabberInstantProperty</tt> constant
- */
- public static native long kABJabberInstantProperty();
-
- /**
- * Gets the value of the <tt>kABLastNameProperty</tt> constant.
- *
- * @return the value of the <tt>kABLastNameProperty</tt> constant
- */
- public static native long kABLastNameProperty();
-
- /**
- * Gets the value of the <tt>kABLastNamePhoneticProperty</tt> constant.
- *
- * @return the value of the <tt>kABLastNamePhoneticProperty</tt> constant
- */
- public static native long kABLastNamePhoneticProperty();
-
- /**
- * Gets the value of the <tt>kABMiddleNameProperty</tt> constant.
- *
- * @return the value of the <tt>kABMiddleNameProperty</tt> constant
- */
- public static native long kABMiddleNameProperty();
-
- /**
- * Gets the value of the <tt>kABMiddleNamePhoneticProperty</tt> constant.
- *
- * @return the value of the <tt>kABMiddleNamePhoneticProperty</tt> constant
- */
- public static native long kABMiddleNamePhoneticProperty();
-
- /**
- * Gets the value of the <tt>kABMSNInstantProperty</tt> constant.
- *
- * @return the value of the <tt>kABMSNInstantProperty</tt> constant
- */
- public static native long kABMSNInstantProperty();
-
- /**
- * Gets the value of the <tt>kABNicknameProperty</tt> constant.
- *
- * @return the value of the <tt>kABNicknameProperty</tt> constant
- */
- public static native long kABNicknameProperty();
-
- /**
- * Gets the value of the <tt>kABOrganizationProperty</tt> constant.
- *
- * @return the value of the <tt>kABOrganizationProperty</tt> constant
- */
- public static native long kABOrganizationProperty();
-
- /**
- * Gets the value of the <tt>kABPersonFlags</tt> constant.
- *
- * @return the value of the <tt>kABPersonFlags</tt> constant
- */
- public static native long kABPersonFlags();
-
- /**
- * Gets the value of the <tt>kABPhoneProperty</tt> constant.
- *
- * @return the value of the <tt>kABPhoneProperty</tt> constant
- */
- public static native long kABPhoneProperty();
-
- /**
- * Gets the value of the <tt>kABYahooInstantProperty</tt> constant.
- *
- * @return the value of the <tt>kABYahooInstantProperty</tt> constant
- */
- public static native long kABYahooInstantProperty();
-
- /**
- * Gets the value of the <tt>kABMaidenNameProperty</tt> constant.
- *
- * @return the value of the <tt>kABMaidenNameProperty</tt> constant
- */
- public static native long kABMaidenNameProperty();
-
- /**
- * Gets the value of the <tt>kABBirthdayProperty</tt> constant.
- *
- * @return the value of the <tt>kABBirthdayProperty</tt> constant
- */
- public static native long kABBirthdayProperty();
-
- /**
- * Gets the value of the <tt>kABJobTitleProperty</tt> constant.
- *
- * @return the value of the <tt>kABJobTitleProperty</tt> constant
- */
- public static native long kABJobTitleProperty();
-
- /**
- * Gets the value of the <tt>kABHomePageProperty</tt> constant.
- *
- * @return the value of the <tt>kABHomePageProperty</tt> constant
- */
- public static native long kABHomePageProperty();
-
- /**
- * Gets the value of the <tt>kABURLsProperty</tt> constant.
- *
- * @return the value of the <tt>kABURLsProperty</tt> constant
- */
- public static native long kABURLsProperty();
-
- /**
- * Gets the value of the <tt>kABCalendarURIsProperty</tt> constant.
- *
- * @return the value of the <tt>kABCalendarURIsProperty</tt> constant
- */
- public static native long kABCalendarURIsProperty();
-
- /**
- * Gets the value of the <tt>kABAddressProperty</tt> constant.
- *
- * @return the value of the <tt>kABAddressProperty</tt> constant
- */
- public static native long kABAddressProperty();
-
- /**
- * Gets the value of the <tt>kABOtherDatesProperty</tt> constant.
- *
- * @return the value of the <tt>kABOtherDatesProperty</tt> constant
- */
- public static native long kABOtherDatesProperty();
-
- /**
- * Gets the value of the <tt>kABRelatedNamesProperty</tt> constant.
- *
- * @return the value of the <tt>kABRelatedNamesProperty</tt> constant
- */
- public static native long kABRelatedNamesProperty();
-
- /**
- * Gets the value of the <tt>kABDepartmentProperty</tt> constant.
- *
- * @return the value of the <tt>kABDepartmentProperty</tt> constant
- */
- public static native long kABDepartmentProperty();
-
- /**
- * Gets the value of the <tt>kABInstantMessageProperty</tt> constant.
- *
- * @return the value of the <tt>kABInstantMessageProperty</tt> constant
- */
- public static native long kABInstantMessageProperty();
-
- /**
- * Gets the value of the <tt>kABNoteProperty</tt> constant.
- *
- * @return the value of the <tt>kABNoteProperty</tt> constant
- */
- public static native long kABNoteProperty();
-
- /**
- * Gets the value of the <tt>kABTitleProperty</tt> constant.
- *
- * @return the value of the <tt>kABTitleProperty</tt> constant
- */
- public static native long kABTitleProperty();
-
- /**
- * Gets the value of the <tt>kABSuffixProperty</tt> constant.
- *
- * @return the value of the <tt>kABSuffixProperty</tt> constant
- */
- public static native long kABSuffixProperty();
-
- public static native String kABEmailWorkLabel();
- public static native String kABEmailHomeLabel();
- public static native String kABAddressHomeLabel();
- public static native String kABAddressWorkLabel();
- public static native String kABPhoneWorkLabel();
- public static native String kABPhoneHomeLabel();
- public static native String kABPhoneMobileLabel();
- public static native String kABPhoneMainLabel();
- public static native String kABPhoneWorkFAXLabel();
- public static native String kABHomeLabel();
- public static native String kABWorkLabel();
- public static native String kABOtherLabel();
- public static native String kABAddressStreetKey();
- public static native String kABAddressCityKey();
- public static native String kABAddressStateKey();
- public static native String kABAddressZIPKey();
- public static native String kABAddressCountryKey();
-
-
- /**
- * Determines whether a specific <tt>ABPerson</tt> property with a specific
- * <tt>value</tt> matches the {@link #query} of this
- * <tt>AsyncContactQuery</tt>.
- *
- * @param property the <tt>ABPerson</tt> property to check
- * @param value the value of the <tt>property</tt> to check
- * @return <tt>true</tt> if the specified <tt>value</tt> of the specified
- * <tt>property</tt> matches the <tt>query</tt> of this
- * <tt>AsyncContactQuery</tt>; otherwise, <tt>false</tt>
- */
- private boolean matches(int property, String value)
- {
- return
- query.matcher(value).find()
- || ((kABPhoneProperty == property) && phoneNumberMatches(value));
- }
-
- /**
- * Determines whether an <tt>ABPerson</tt> represented by the values of its
- * {@link #ABPERSON_PROPERTIES} matches {@link #query}.
- *
- * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which
- * represent the <tt>ABPerson</tt> to be determined whether it matches
- * <tt>query</tt>
- * @return <tt>true</tt> if the <tt>ABPerson</tt> represented by the
- * specified <tt>values</tt> matches <tt>query</tt>; otherwise,
- * <tt>false</tt>
- */
- private boolean matches(Object[] values)
- {
- int property = 0;
-
- for (Object value : values)
- {
- if (value instanceof String)
- {
- if (matches(property, (String) value))
- return true;
- }
- else if (value instanceof Object[])
- {
- Object[] multiValue = (Object[]) value;
-
- for (int multiValueIndex = 0;
- multiValueIndex < multiValue.length;
- multiValueIndex += 2)
- {
- Object subValue = multiValue[multiValueIndex];
- if ((subValue instanceof String)
- && matches(property, (String) subValue))
- return true;
- }
- }
- property++;
- }
- return false;
- }
-
- /**
- * Notifies this <tt>MacOSXAddrBookContactQuery</tt> about a specific
- * <tt>ABPerson</tt>.
- *
- * @param person a pointer to the <tt>ABPerson</tt> instance to notify about
- * @return <tt>true</tt> if this <tt>MacOSXAddrBookContactQuery</tt> is to
- * continue being called; otherwise, <tt>false</tt>
- */
- private boolean onPerson(long person)
- {
- Object[] values
- = ABRecord_valuesForProperties(person, ABPERSON_PROPERTIES);
- final String id = ABRecord_uniqueId(person);
-
- String displayName = getDisplayName(values);
- if ((displayName.length() != 0)
- && (query.matcher(displayName).find() || matches(values)))
- {
- List<ContactDetail> contactDetails = getContactDetails(values, id);
-
- if (!contactDetails.isEmpty())
- {
- final MacOSXAddrBookSourceContact sourceContact
- = new MacOSXAddrBookSourceContact(
- getContactSource(),
- displayName,
- contactDetails);
- sourceContact.setData(SourceContact.DATA_ID, id);
-
- try
- {
- byte[] image = ABPerson_imageData(person);
-
- if (image != null)
- sourceContact.setImage(image);
- }
- catch (OutOfMemoryError oome)
- {
- // Ignore it, the image is not vital.
- }
-
- addQueryResult(sourceContact);
- }
- }
- return (getStatus() == QUERY_IN_PROGRESS);
- }
-
- /**
- * Performs this <tt>AsyncContactQuery</tt> in a background <tt>Thread</tt>.
- *
- * @see AsyncContactQuery#run()
- */
- protected void run()
- {
- foreachPerson(
- query.toString(),
- new PtrCallback()
- {
- public boolean callback(long person)
- {
- return onPerson(person);
- }
- });
- }
-
- /**
- * Sets the capabilities of a specific <tt>ContactDetail</tt> (e.g.
- * <tt>supportedOpSets</tt>) depending on the <tt>ABPerson</tt> property
- * that it stands for.
- *
- * @param contactDetail the <tt>ContactDetail</tt> to set the capabilities
- * of
- * @param property the index in {@link #ABPERSON_PROPERTIES} of the
- * <tt>ABPerson</tt> property represented by <tt>ContactDetail</tt>
- * @return <tt>contactDetail</tt>
- */
- private ContactDetail setCapabilities(
- ContactDetail contactDetail,
- int property)
- {
- List<Class<? extends OperationSet>> supportedOpSets
- = new LinkedList<Class<? extends OperationSet>>();
- Map<Class<? extends OperationSet>, String> preferredProtocols
- = new HashMap<Class<? extends OperationSet>, String>();
-
- // can be added as contacts
- supportedOpSets.add(OperationSetPersistentPresence.class);
-
- switch (property)
- {
- case kABAIMInstantProperty:
- supportedOpSets.add(OperationSetBasicInstantMessaging.class);
- preferredProtocols.put(
- OperationSetBasicInstantMessaging.class,
- ProtocolNames.AIM);
- break;
- case kABEmailProperty:
- break;
- case kABICQInstantProperty:
- supportedOpSets.add(OperationSetBasicInstantMessaging.class);
- preferredProtocols.put(
- OperationSetBasicInstantMessaging.class,
- ProtocolNames.ICQ);
- break;
- case kABJabberInstantProperty:
- supportedOpSets.add(OperationSetBasicInstantMessaging.class);
- preferredProtocols.put(
- OperationSetBasicInstantMessaging.class,
- ProtocolNames.JABBER);
- supportedOpSets.add(OperationSetBasicTelephony.class);
- preferredProtocols.put(
- OperationSetBasicTelephony.class,
- ProtocolNames.JABBER);
- break;
- case kABPhoneProperty:
- supportedOpSets.add(OperationSetBasicTelephony.class);
- break;
- case kABMSNInstantProperty:
- supportedOpSets.add(OperationSetBasicInstantMessaging.class);
- preferredProtocols.put(
- OperationSetBasicInstantMessaging.class,
- ProtocolNames.MSN);
- break;
- case kABYahooInstantProperty:
- supportedOpSets.add(OperationSetBasicInstantMessaging.class);
- preferredProtocols.put(
- OperationSetBasicInstantMessaging.class,
- ProtocolNames.YAHOO);
- break;
- default:
- break;
- }
- contactDetail.setSupportedOpSets(supportedOpSets);
- if (!preferredProtocols.isEmpty())
- contactDetail.setPreferredProtocols(preferredProtocols);
-
- return contactDetail;
- }
-
- /**
- * Callback method when receiving notifications for inserted items.
- */
- public void inserted(long person)
- {
- onPerson(person);
- }
-
- /**
- * Callback method when receiving notifications for updated items.
- */
- public void updated(long person)
- {
- SourceContact sourceContact =
- findSourceContactByID(ABRecord_uniqueId(person));
- if(sourceContact != null
- && sourceContact instanceof MacOSXAddrBookSourceContact)
- {
- // let's update the the details
- Object[] values
- = ABRecord_valuesForProperties(person, ABPERSON_PROPERTIES);
- String displayName = getDisplayName(values);
- final String id = ABRecord_uniqueId(person);
-
-
- MacOSXAddrBookSourceContact editableSourceContact
- = (MacOSXAddrBookSourceContact)sourceContact;
-
- editableSourceContact.setDisplayName(displayName);
-
- List<ContactDetail> contactDetails = getContactDetails(values, id);
- editableSourceContact.setDetails(contactDetails);
-
- fireContactChanged(sourceContact);
- }
- }
-
- /**
- * Callback method when receiving notifications for deleted items.
- */
- public void deleted(String id)
- {
- SourceContact sourceContact = findSourceContactByID(id);
-
- if(sourceContact != null)
- fireContactRemoved(sourceContact);
- }
-
- /**
- * Find the property from category and subcategories.
- *
- * @param category
- * @param subCategories
- * @return
- */
- public static int getProperty(
- Category category,
- Collection<SubCategory> subCategories)
- {
- switch(category)
- {
- case Personal:
- if(subCategories.contains(SubCategory.Name))
- return kABFirstNameProperty;
- else if(subCategories.contains(SubCategory.LastName))
- return kABLastNameProperty;
- else if(subCategories.contains(SubCategory.Nickname))
- return kABNicknameProperty;
- else if(subCategories.contains(SubCategory.HomePage))
- return kABHomePageProperty;
- break;
- case Organization:
- if(subCategories.contains(SubCategory.JobTitle))
- return kABJobTitleProperty;
- else
- return kABDepartmentProperty;
- case Email:
- return kABEmailProperty;
- case InstantMessaging:
- if(subCategories.contains(SubCategory.AIM))
- return kABAIMInstantProperty;
- else if(subCategories.contains(SubCategory.ICQ))
- return kABICQInstantProperty;
- else if(subCategories.contains(SubCategory.MSN))
- return kABMSNInstantProperty;
- else if(subCategories.contains(
- SubCategory.Jabber))
- return kABJabberInstantProperty;
- else if(subCategories.contains(
- SubCategory.Yahoo))
- return kABYahooInstantProperty;
- break;
- case Phone:
- return kABPhoneProperty;
- case Address:
- return kABAddressProperty;
- default: return -1;
- }
-
- return -1;
- }
-
- /**
- * Finds the label from category and sub categories.
- * @param subCategory
- * @return
- */
- public static String getLabel(
- int property,
- SubCategory subCategory,
- String subProperty)
- {
- switch(property)
- {
- case kABEmailProperty:
- if(subCategory == SubCategory.Home)
- return kABEmailHomeLabel();
- if(subCategory == SubCategory.Work)
- return kABEmailWorkLabel();
- break;
- case kABICQInstantProperty:
- case kABAIMInstantProperty:
- case kABYahooInstantProperty:
- case kABMSNInstantProperty:
- case kABJabberInstantProperty:
- return subProperty;
- case kABPhoneProperty:
- if(subCategory == SubCategory.Home)
- return kABPhoneHomeLabel();
- if(subCategory == SubCategory.Work)
- return kABPhoneWorkLabel();
- if(subCategory == SubCategory.Fax)
- return kABPhoneWorkFAXLabel();
- if(subCategory == SubCategory.Mobile)
- return kABPhoneMobileLabel();
- if(subCategory == SubCategory.Other)
- return "other";
- break;
- case kABAddressProperty:
- if(subCategory == SubCategory.Street)
- return kABAddressStreetKey();
- if(subCategory == SubCategory.City)
- return kABAddressCityKey();
- if(subCategory == SubCategory.State)
- return kABAddressStateKey();
- if(subCategory == SubCategory.Country)
- return kABAddressCountryKey();
- if(subCategory == SubCategory.PostalCode)
- return kABAddressZIPKey();
- break;
- default: return null;
- }
-
- return null;
- }
-}
+/*
+ * 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.macosx;
+
+import java.util.*;
+import java.util.regex.*;
+
+import net.java.sip.communicator.plugin.addrbook.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.contactsource.ContactDetail.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implements <tt>ContactQuery</tt> for the Address Book of Mac OS X.
+ *
+ * @author Lyubomir Marinov
+ */
+public class MacOSXAddrBookContactQuery
+ extends AbstractAddrBookContactQuery<MacOSXAddrBookContactSourceService>
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>MacOSXAddrBookContactQuery</tt> class
+ * and its instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(MacOSXAddrBookContactQuery.class);
+
+ /**
+ * The properties of <tt>ABPerson</tt> which are to be queried by the
+ * <tt>MacOSXAddrBookContactQuery</tt> instances.
+ */
+ public static final long[] ABPERSON_PROPERTIES
+ = new long[]
+ {
+ kABAIMInstantProperty(),
+ kABEmailProperty(),
+ kABFirstNameProperty(),
+ kABFirstNamePhoneticProperty(),
+ kABICQInstantProperty(),
+ kABJabberInstantProperty(),
+ kABLastNameProperty(),
+ kABLastNamePhoneticProperty(),
+ kABMiddleNameProperty(),
+ kABMiddleNamePhoneticProperty(),
+ kABMSNInstantProperty(),
+ kABNicknameProperty(),
+ kABPhoneProperty(),
+ kABYahooInstantProperty(),
+ kABPersonFlags(),
+ kABOrganizationProperty(),
+ kABMaidenNameProperty(),
+ kABBirthdayProperty(),
+ kABJobTitleProperty(),
+ kABHomePageProperty(),
+ kABURLsProperty(),
+ kABCalendarURIsProperty(),
+ kABAddressProperty(),
+ kABOtherDatesProperty(),
+ kABRelatedNamesProperty(),
+ kABDepartmentProperty(),
+ kABNoteProperty(),
+ kABTitleProperty(),
+ kABSuffixProperty()
+ };
+
+ /**
+ * The index of the <tt>kABAIMInstantProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABAIMInstantProperty = 0;
+
+ /**
+ * The index of the <tt>kABEmailProperty</tt> <tt>ABPerson</tt> property in
+ * {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABEmailProperty = 1;
+
+ /**
+ * The index of the <tt>kABFirstNameProperty</tt> <tt>ABPerson</tt> property
+ * in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABFirstNameProperty = 2;
+
+ /**
+ * The index of the <tt>kABFirstNamePhoneticProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABFirstNamePhoneticProperty = 3;
+
+ /**
+ * The index of the <tt>kABICQInstantProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABICQInstantProperty = 4;
+
+ /**
+ * The index of the <tt>kABJabberInstantProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABJabberInstantProperty = 5;
+
+ /**
+ * The index of the <tt>kABLastNameProperty</tt> <tt>ABPerson</tt> property
+ * in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABLastNameProperty = 6;
+
+ /**
+ * The index of the <tt>kABLastNamePhoneticProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABLastNamePhoneticProperty = 7;
+
+ /**
+ * The index of the <tt>kABMiddleNameProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABMiddleNameProperty = 8;
+
+ /**
+ * The index of the <tt>kABMiddleNamePhoneticProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABMiddleNamePhoneticProperty = 9;
+
+ /**
+ * The index of the <tt>kABMSNInstantProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABMSNInstantProperty = 10;
+
+ /**
+ * The index of the <tt>kABNicknameProperty</tt> <tt>ABPerson</tt> property
+ * in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABNicknameProperty = 11;
+
+ /**
+ * The index of the <tt>kABOrganizationProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABOrganizationProperty = 15;
+
+ /**
+ * The index of the <tt>kABPersonFlags</tt> <tt>ABPerson</tt> property in
+ * {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABPersonFlags = 14;
+
+ /**
+ * The index of the <tt>kABPhoneProperty</tt> <tt>ABPerson</tt> property in
+ * {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABPhoneProperty = 12;
+
+ /**
+ * The flag which indicates that an <tt>ABRecord</tt> is to be displayed as
+ * a company.
+ */
+ public static final long kABShowAsCompany = 1;
+
+ /**
+ * The mask which extracts the <tt>kABShowAsXXX</tt> flag from the
+ * <tt>personFlags</tt> of an <tt>ABPerson</tt>.
+ */
+ public static final long kABShowAsMask = 7;
+
+ /**
+ * The index of the <tt>kABYahooInstantProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABYahooInstantProperty = 13;
+
+ /**
+ * The index of the <tt>kABMaidenNameProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABMaidenNameProperty = 16;
+
+ /**
+ * The index of the <tt>kABBirthdayProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABBirthdayProperty = 17;
+
+ /**
+ * The index of the <tt>kABJobTitleProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABJobTitleProperty = 18;
+
+ /**
+ * The index of the <tt>kABHomePageProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABHomePageProperty = 19;
+
+ /**
+ * The index of the <tt>kABURLsProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABURLsProperty = 20;
+
+ /**
+ * The index of the <tt>kABCalendarURIsProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABCalendarURIsProperty = 21;
+
+ /**
+ * The index of the <tt>kABAddressProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABAddressProperty = 22;
+
+ /**
+ * The index of the <tt>kABOtherDatesProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABOtherDatesProperty = 23;
+
+ /**
+ * The index of the <tt>kABRelatedNamesProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABRelatedNamesProperty = 24;
+
+ /**
+ * The index of the <tt>kABDepartmentProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABDepartmentProperty = 25;
+
+ /**
+ * The index of the <tt>kABNoteProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABNoteProperty = 26;
+
+ /**
+ * The index of the <tt>kABTitleProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABTitleProperty = 27;
+
+ /**
+ * The index of the <tt>kABSuffixProperty</tt> <tt>ABPerson</tt>
+ * property in {@link #ABPERSON_PROPERTIES}.
+ */
+ public static final int kABSuffixProperty = 28;
+
+ /**
+ * The regex which matches the superfluous parts of an <tt>ABMultiValue</tt>
+ * label.
+ */
+ private static final Pattern LABEL_PATTERN
+ = Pattern.compile(
+ "kAB|Email|Phone|Label|(\\p{Punct}*)",
+ Pattern.CASE_INSENSITIVE);
+
+ static
+ {
+ System.loadLibrary("jmacosxaddrbook");
+ }
+
+ /**
+ * Initializes a new <tt>MacOSXAddrBookContactQuery</tt> which is to perform
+ * a specific <tt>query</tt> in the Address Book of Mac OS X on behalf of a
+ * specific <tt>MacOSXAddrBookContactSourceService</tt>.
+ *
+ * @param contactSource the <tt>MacOSXAddrBookContactSourceService</tt>
+ * which is to perform the new <tt>ContactQuery</tt> instance
+ * @param query the <tt>Pattern</tt> for which <tt>contactSource</tt> i.e.
+ * the Address Book of Mac OS X is being queried
+ */
+ public MacOSXAddrBookContactQuery(
+ MacOSXAddrBookContactSourceService contactSource,
+ Pattern query)
+ {
+ super(contactSource, query);
+ }
+
+ /**
+ * Gets the <tt>imageData</tt> of a specific <tt>ABPerson</tt> instance.
+ *
+ * @param person the pointer to the <tt>ABPerson</tt> instance to get the
+ * <tt>imageData</tt> of
+ * @return the <tt>imageData</tt> of the specified <tt>ABPerson</tt>
+ * instance
+ */
+ public static native byte[] ABPerson_imageData(long person);
+
+ /**
+ * Gets the values of a specific set of <tt>ABRecord</tt> properties for a
+ * specific <tt>ABRecord</tt> instance.
+ *
+ * @param record the pointer to the <tt>ABRecord</tt> to get the property
+ * values of
+ * @param properties the set of <tt>ABRecord</tt> properties to get the
+ * values of
+ * @return the values of the specified set of <tt>ABRecord</tt> properties
+ * for the specified <tt>ABRecord</tt> instance
+ */
+ public static native Object[] ABRecord_valuesForProperties(
+ long record,
+ long[] properties);
+
+ /**
+ * Returns the unique id of a record.
+ * @param record the record which id is retrieved.
+ * @return the record id.
+ */
+ public static native String ABRecord_uniqueId(long record);
+
+ /**
+ * Sets property for the supplied person id.
+ * @param id the person id
+ * @param property the property to use.
+ * @param subPropety any sub property if available.
+ * @param value the value to set.
+ * @return whether the result was successfully added.
+ */
+ public static native boolean setProperty(
+ String id, long property, String subPropety, Object value);
+
+ /**
+ * Remove a property.
+ * @param id the person id.
+ * @param property the property.
+ * @return whether the result was successfully removed.
+ */
+ public static native boolean removeProperty(String id, long property);
+
+ /**
+ * Removes a contact from the address book.
+ *
+ * @param id the person id.
+ *
+ * @return whether the contact was successfully removed.
+ */
+ public static native boolean deleteContact(String id);
+
+ /**
+ * Creates a new address book contact.
+ *
+ * @return The identifier of the created contact. null if failed.
+ */
+ public static native String createContact();
+
+ /**
+ * Gets the pointer of the given contact.
+ *
+ * @param id the person id.
+ *
+ * @return The pointer of the given contact. Null if failed.
+ */
+ public static native long getContactPointer(String id);
+
+ /**
+ * Initializes a new <tt>ContactDetail</tt> instance which is to reperesent
+ * a specific contact address that is the value of a specific
+ * <tt>ABPerson</tt> property and, optionally, has a specific label.
+ *
+ * @param property the index in {@link #ABPERSON_PROPERTIES} of the
+ * <tt>ABPerson</tt> property to be represented by <tt>ContactDetail</tt>
+ * @param contactAddress the contact address to be represented by the new
+ * <tt>ContactDetail</tt> instance
+ * @param label an optional label to be added to the set of labels, if any,
+ * determined by <tt>property</tt>
+ * @param id The id of the detail.
+ *
+ * @return a new <tt>ContactDetail</tt> instance which represents the
+ * specified <tt>contactAddress</tt>
+ */
+ private ContactDetail createContactDetail(
+ int property,
+ String contactAddress,
+ Object label,
+ String additionalProperty,
+ String id)
+ {
+ Category c;
+ SubCategory sc = null;
+
+ switch (property)
+ {
+ case kABEmailProperty:
+ c = Category.Email;
+ break;
+ case kABPhoneProperty:
+ c = Category.Phone;
+ break;
+ case kABAIMInstantProperty:
+ sc = SubCategory.AIM;
+ c = Category.InstantMessaging;
+ break;
+ case kABICQInstantProperty:
+ sc = SubCategory.ICQ;
+ c = Category.InstantMessaging;
+ break;
+ case kABJabberInstantProperty:
+ sc = SubCategory.Jabber;
+ c = Category.InstantMessaging;
+ break;
+ case kABMSNInstantProperty:
+ sc = SubCategory.MSN;
+ c = Category.InstantMessaging;
+ break;
+ case kABYahooInstantProperty:
+ sc = SubCategory.Yahoo;
+ c = Category.InstantMessaging;
+ break;
+ case kABMaidenNameProperty:
+ case kABFirstNameProperty:
+ sc = SubCategory.Name;
+ c = Category.Personal;
+ break;
+ case kABFirstNamePhoneticProperty:
+ sc = SubCategory.Name;
+ c = Category.Personal;
+ break;
+ case kABLastNameProperty:
+ sc = SubCategory.LastName;
+ c = Category.Personal;
+ break;
+ case kABLastNamePhoneticProperty:
+ sc = SubCategory.LastName;
+ c = Category.Personal;
+ break;
+ case kABMiddleNameProperty:
+ case kABMiddleNamePhoneticProperty:
+ case kABNicknameProperty:
+ sc = SubCategory.Nickname;
+ c = Category.Personal;
+ break;
+ case kABBirthdayProperty:
+ case kABURLsProperty:
+ case kABHomePageProperty:
+ sc = SubCategory.HomePage;
+ c = Category.Personal;
+ break;
+ case kABOtherDatesProperty:
+ case kABRelatedNamesProperty:
+ case kABNoteProperty:
+ case kABTitleProperty:
+ case kABSuffixProperty:
+ c = Category.Personal;
+ break;
+ case kABOrganizationProperty:
+ case kABJobTitleProperty:
+ sc = SubCategory.JobTitle;
+ c = Category.Organization;
+ break;
+ case kABDepartmentProperty:
+ c = Category.Organization;
+ sc = SubCategory.Name;
+ break;
+ case kABAddressProperty:
+ c = Category.Address;
+ break;
+ default:
+ c = null;
+ break;
+ }
+
+ if (sc == null)
+ {
+ if (label == null)
+ sc = null;
+ else
+ {
+ sc = getSubCategoryFromLabel(label);
+ }
+ }
+
+ SubCategory[] subCategories;
+ SubCategory additionalSubCategory = null;
+
+ if(additionalProperty != null)
+ additionalSubCategory = getSubCategoryFromLabel(additionalProperty);
+
+ if(additionalSubCategory != null)
+ subCategories = new SubCategory[]
+ { sc, additionalSubCategory };
+ else
+ subCategories = new SubCategory[]{ sc };
+
+ return new MacOSXAddrBookContactDetail(
+ property,
+ contactAddress,
+ c,
+ subCategories,
+ additionalProperty,
+ id);
+ }
+
+ /**
+ * Returns the SubCategory corresponding to the given label.
+ *
+ * @param label the label to match to a <tt>SubDirectory</tt>
+ * @return the <tt>SubDirectory</tt> corresponding to the
+ * given label
+ */
+ private SubCategory getSubCategoryFromLabel(Object label)
+ {
+ String labelString
+ = LABEL_PATTERN.matcher((String) label).replaceAll("").trim();
+
+ if (labelString.length() < 1)
+ return null;
+
+ SubCategory subCategory = null;
+
+ if (labelString.equalsIgnoreCase("home"))
+ subCategory = SubCategory.Home;
+ else if (labelString.equalsIgnoreCase("work"))
+ subCategory = SubCategory.Work;
+ else if (labelString.equalsIgnoreCase("other"))
+ subCategory = SubCategory.Other;
+ else if (labelString.equalsIgnoreCase("mobile"))
+ subCategory = SubCategory.Mobile;
+ else if (labelString.equalsIgnoreCase("homepage"))
+ subCategory = SubCategory.HomePage;
+ else if (labelString.equalsIgnoreCase("street"))
+ subCategory = SubCategory.Street;
+ else if (labelString.equalsIgnoreCase("state"))
+ subCategory = SubCategory.State;
+ else if (labelString.equalsIgnoreCase("ZIP"))
+ subCategory = SubCategory.PostalCode;
+ else if (labelString.equalsIgnoreCase("country"))
+ subCategory = SubCategory.Country;
+ else if (labelString.equalsIgnoreCase("city"))
+ subCategory = SubCategory.City;
+ else if (labelString.equalsIgnoreCase("InstantMessageUsername"))
+ subCategory = SubCategory.Nickname;
+ else if (labelString.equalsIgnoreCase("workfax"))
+ subCategory = SubCategory.Fax;
+ else if (labelString.equalsIgnoreCase("fax"))
+ subCategory = SubCategory.Fax;
+
+ return subCategory;
+ }
+
+ /**
+ * Calls back to a specific <tt>PtrCallback</tt> for each <tt>ABPerson</tt>
+ * found in the Address Book of Mac OS X which matches a specific
+ * <tt>String</tt> query.
+ *
+ * @param query the <tt>String</tt> for which the Address Book of Mac OS X
+ * is to be queried. <b>Warning</b>: Ignored at the time of this writing.
+ * @param callback the <tt>PtrCallback</tt> to be notified about the
+ * matching <tt>ABPerson</tt>s
+ */
+ private static native void foreachPerson(
+ String query,
+ PtrCallback callback);
+
+ /**
+ * 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
+ * @param id The id of the detail.
+ *
+ * @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, String id)
+ {
+ List<ContactDetail> contactDetails = new LinkedList<ContactDetail>();
+
+ for (int i = 0; i < ABPERSON_PROPERTIES.length; i++)
+ {
+ int property = i;
+ Object value = values[property];
+
+ if (value instanceof String)
+ {
+ String stringValue = (String) value;
+
+ if (stringValue.length() != 0)
+ {
+ if (kABPhoneProperty == property)
+ stringValue
+ = PhoneNumberI18nService.normalize(stringValue);
+
+ contactDetails.add(
+ setCapabilities(
+ createContactDetail(
+ property,
+ stringValue,
+ null,
+ null,
+ id),
+ property));
+ }
+ }
+ else if (value instanceof Object[])
+ {
+ parseMultiDetails(contactDetails,
+ (Object[]) value,
+ property,
+ null,
+ id);
+ }
+ }
+ return contactDetails;
+ }
+
+ /**
+ * Parses the multi value data resulting it in contact details.
+ * @param contactDetails the result list
+ * @param multiValue the values to parse.
+ * @param property the current property being parsed.
+ * @param id The id of the detail.
+ */
+ private void parseMultiDetails(
+ List<ContactDetail> contactDetails,
+ Object[] multiValue,
+ int property,
+ String label,
+ String id)
+ {
+ if(multiValue == null)
+ return;
+
+ for (int multiValueIndex = 0;
+ multiValueIndex < multiValue.length;
+ multiValueIndex += 2)
+ {
+ Object subValue = multiValue[multiValueIndex];
+
+ if (subValue instanceof String)
+ {
+ String stringSubValue = (String) subValue;
+
+ if (stringSubValue.length() != 0)
+ {
+ if (kABPhoneProperty == property)
+ {
+ stringSubValue = PhoneNumberI18nService
+ .normalize(stringSubValue);
+ }
+
+ Object l = multiValue[multiValueIndex + 1];
+
+ contactDetails.add(
+ setCapabilities(
+ createContactDetail(
+ property,
+ stringSubValue,
+ l,
+ label,
+ id),
+ property));
+ }
+ }
+ else if (subValue instanceof Object[])
+ {
+ String l = null;
+
+ Object lObject = multiValue[multiValueIndex + 1];
+ if(lObject instanceof String)
+ l = (String)lObject;
+
+ parseMultiDetails(contactDetails,
+ (Object[]) subValue,
+ property,
+ l,
+ id);
+ }
+ }
+ }
+
+ /**
+ * Gets the <tt>displayName</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>displayName</tt> of
+ * @return the <tt>displayName</tt> to be set on a <tt>SourceContact</tt>
+ * which is to represent the <tt>ABPerson</tt> specified by <tt>values</tt>
+ */
+ static String getDisplayName(Object[] values)
+ {
+ long personFlags
+ = (values[kABPersonFlags] instanceof Long)
+ ? ((Long) values[kABPersonFlags]).longValue()
+ : 0;
+ String displayName;
+
+ if ((personFlags & kABShowAsMask) == kABShowAsCompany)
+ {
+ displayName
+ = (values[kABOrganizationProperty] instanceof String)
+ ? (String) values[kABOrganizationProperty]
+ : "";
+ if (displayName.length() != 0)
+ return displayName;
+ }
+
+ displayName
+ = (values[kABNicknameProperty] instanceof String)
+ ? (String) values[kABNicknameProperty]
+ : "";
+ if (displayName.length() != 0)
+ return displayName;
+
+ String firstName
+ = (values[kABFirstNameProperty] instanceof String)
+ ? (String) values[kABFirstNameProperty]
+ : "";
+
+ if ((firstName.length() == 0)
+ && (values[kABFirstNamePhoneticProperty] instanceof String))
+ {
+ firstName = (String) values[kABFirstNamePhoneticProperty];
+ }
+
+ String lastName
+ = (values[kABLastNameProperty] instanceof String)
+ ? (String) values[kABLastNameProperty]
+ : "";
+
+ if ((lastName.length() == 0)
+ && (values[kABLastNamePhoneticProperty] instanceof String))
+ lastName = (String) values[kABLastNamePhoneticProperty];
+ if ((lastName.length() == 0)
+ && (values[kABMiddleNameProperty] instanceof String))
+ lastName = (String) values[kABMiddleNameProperty];
+ if ((lastName.length() == 0)
+ && (values[kABMiddleNamePhoneticProperty] instanceof String))
+ lastName = (String) values[kABMiddleNamePhoneticProperty];
+
+ if (firstName.length() == 0)
+ displayName = lastName;
+ else
+ {
+ displayName
+ = (lastName.length() == 0)
+ ? firstName
+ : (firstName + " " + lastName);
+ }
+ if (displayName.length() != 0)
+ return displayName;
+
+ for (int i = 0; i < ABPERSON_PROPERTIES.length; i++)
+ {
+ Object value = values[i];
+
+ if (value instanceof String)
+ {
+ String stringValue = (String) value;
+
+ if (stringValue.length() != 0)
+ {
+ displayName = stringValue;
+ break;
+ }
+ }
+ else if (value instanceof Object[])
+ {
+ Object[] multiValue = (Object[]) value;
+
+ for (int multiValueIndex = 0;
+ multiValueIndex < multiValue.length;
+ multiValueIndex += 2)
+ {
+ Object subValue = multiValue[multiValueIndex];
+
+ if (subValue instanceof String)
+ {
+ String stringSubValue = (String) subValue;
+
+ if (stringSubValue.length() != 0)
+ {
+ displayName = stringSubValue;
+ break;
+ }
+ }
+ }
+ }
+ }
+ return displayName;
+ }
+
+ /**
+ * Gets the value of the <tt>kABAIMInstantProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABAIMInstantProperty</tt> constant
+ */
+ public static native long kABAIMInstantProperty();
+
+ /**
+ * Gets the value of the <tt>kABEmailProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABEmailProperty</tt> constant
+ */
+ public static native long kABEmailProperty();
+
+ /**
+ * Gets the value of the <tt>kABFirstNameProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABFirstNameProperty</tt> constant
+ */
+ public static native long kABFirstNameProperty();
+
+ /**
+ * Gets the value of the <tt>kABFirstNamePhoneticProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABFirstNamePhoneticProperty</tt> constant
+ */
+ public static native long kABFirstNamePhoneticProperty();
+
+ /**
+ * Gets the value of the <tt>kABICQInstantProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABICQInstantProperty</tt> constant
+ */
+ public static native long kABICQInstantProperty();
+
+ /**
+ * Gets the value of the <tt>kABJabberInstantProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABJabberInstantProperty</tt> constant
+ */
+ public static native long kABJabberInstantProperty();
+
+ /**
+ * Gets the value of the <tt>kABLastNameProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABLastNameProperty</tt> constant
+ */
+ public static native long kABLastNameProperty();
+
+ /**
+ * Gets the value of the <tt>kABLastNamePhoneticProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABLastNamePhoneticProperty</tt> constant
+ */
+ public static native long kABLastNamePhoneticProperty();
+
+ /**
+ * Gets the value of the <tt>kABMiddleNameProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABMiddleNameProperty</tt> constant
+ */
+ public static native long kABMiddleNameProperty();
+
+ /**
+ * Gets the value of the <tt>kABMiddleNamePhoneticProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABMiddleNamePhoneticProperty</tt> constant
+ */
+ public static native long kABMiddleNamePhoneticProperty();
+
+ /**
+ * Gets the value of the <tt>kABMSNInstantProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABMSNInstantProperty</tt> constant
+ */
+ public static native long kABMSNInstantProperty();
+
+ /**
+ * Gets the value of the <tt>kABNicknameProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABNicknameProperty</tt> constant
+ */
+ public static native long kABNicknameProperty();
+
+ /**
+ * Gets the value of the <tt>kABOrganizationProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABOrganizationProperty</tt> constant
+ */
+ public static native long kABOrganizationProperty();
+
+ /**
+ * Gets the value of the <tt>kABPersonFlags</tt> constant.
+ *
+ * @return the value of the <tt>kABPersonFlags</tt> constant
+ */
+ public static native long kABPersonFlags();
+
+ /**
+ * Gets the value of the <tt>kABPhoneProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABPhoneProperty</tt> constant
+ */
+ public static native long kABPhoneProperty();
+
+ /**
+ * Gets the value of the <tt>kABYahooInstantProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABYahooInstantProperty</tt> constant
+ */
+ public static native long kABYahooInstantProperty();
+
+ /**
+ * Gets the value of the <tt>kABMaidenNameProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABMaidenNameProperty</tt> constant
+ */
+ public static native long kABMaidenNameProperty();
+
+ /**
+ * Gets the value of the <tt>kABBirthdayProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABBirthdayProperty</tt> constant
+ */
+ public static native long kABBirthdayProperty();
+
+ /**
+ * Gets the value of the <tt>kABJobTitleProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABJobTitleProperty</tt> constant
+ */
+ public static native long kABJobTitleProperty();
+
+ /**
+ * Gets the value of the <tt>kABHomePageProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABHomePageProperty</tt> constant
+ */
+ public static native long kABHomePageProperty();
+
+ /**
+ * Gets the value of the <tt>kABURLsProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABURLsProperty</tt> constant
+ */
+ public static native long kABURLsProperty();
+
+ /**
+ * Gets the value of the <tt>kABCalendarURIsProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABCalendarURIsProperty</tt> constant
+ */
+ public static native long kABCalendarURIsProperty();
+
+ /**
+ * Gets the value of the <tt>kABAddressProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABAddressProperty</tt> constant
+ */
+ public static native long kABAddressProperty();
+
+ /**
+ * Gets the value of the <tt>kABOtherDatesProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABOtherDatesProperty</tt> constant
+ */
+ public static native long kABOtherDatesProperty();
+
+ /**
+ * Gets the value of the <tt>kABRelatedNamesProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABRelatedNamesProperty</tt> constant
+ */
+ public static native long kABRelatedNamesProperty();
+
+ /**
+ * Gets the value of the <tt>kABDepartmentProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABDepartmentProperty</tt> constant
+ */
+ public static native long kABDepartmentProperty();
+
+ /**
+ * Gets the value of the <tt>kABInstantMessageProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABInstantMessageProperty</tt> constant
+ */
+ public static native long kABInstantMessageProperty();
+
+ /**
+ * Gets the value of the <tt>kABNoteProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABNoteProperty</tt> constant
+ */
+ public static native long kABNoteProperty();
+
+ /**
+ * Gets the value of the <tt>kABTitleProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABTitleProperty</tt> constant
+ */
+ public static native long kABTitleProperty();
+
+ /**
+ * Gets the value of the <tt>kABSuffixProperty</tt> constant.
+ *
+ * @return the value of the <tt>kABSuffixProperty</tt> constant
+ */
+ public static native long kABSuffixProperty();
+
+ public static native String kABEmailWorkLabel();
+ public static native String kABEmailHomeLabel();
+ public static native String kABAddressHomeLabel();
+ public static native String kABAddressWorkLabel();
+ public static native String kABPhoneWorkLabel();
+ public static native String kABPhoneHomeLabel();
+ public static native String kABPhoneMobileLabel();
+ public static native String kABPhoneMainLabel();
+ public static native String kABPhoneWorkFAXLabel();
+ public static native String kABHomeLabel();
+ public static native String kABWorkLabel();
+ public static native String kABOtherLabel();
+ public static native String kABAddressStreetKey();
+ public static native String kABAddressCityKey();
+ public static native String kABAddressStateKey();
+ public static native String kABAddressZIPKey();
+ public static native String kABAddressCountryKey();
+
+
+ /**
+ * Determines whether a specific <tt>ABPerson</tt> property with a specific
+ * <tt>value</tt> matches the {@link #query} of this
+ * <tt>AsyncContactQuery</tt>.
+ *
+ * @param property the <tt>ABPerson</tt> property to check
+ * @param value the value of the <tt>property</tt> to check
+ * @return <tt>true</tt> if the specified <tt>value</tt> of the specified
+ * <tt>property</tt> matches the <tt>query</tt> of this
+ * <tt>AsyncContactQuery</tt>; otherwise, <tt>false</tt>
+ */
+ private boolean matches(int property, String value)
+ {
+ return
+ query.matcher(value).find()
+ || ((kABPhoneProperty == property) && phoneNumberMatches(value));
+ }
+
+ /**
+ * Determines whether an <tt>ABPerson</tt> represented by the values of its
+ * {@link #ABPERSON_PROPERTIES} matches {@link #query}.
+ *
+ * @param values the values of the <tt>ABPERSON_PROPERTIES</tt> which
+ * represent the <tt>ABPerson</tt> to be determined whether it matches
+ * <tt>query</tt>
+ * @return <tt>true</tt> if the <tt>ABPerson</tt> represented by the
+ * specified <tt>values</tt> matches <tt>query</tt>; otherwise,
+ * <tt>false</tt>
+ */
+ private boolean matches(Object[] values)
+ {
+ int property = 0;
+
+ for (Object value : values)
+ {
+ if (value instanceof String)
+ {
+ if (matches(property, (String) value))
+ return true;
+ }
+ else if (value instanceof Object[])
+ {
+ Object[] multiValue = (Object[]) value;
+
+ for (int multiValueIndex = 0;
+ multiValueIndex < multiValue.length;
+ multiValueIndex += 2)
+ {
+ Object subValue = multiValue[multiValueIndex];
+ if ((subValue instanceof String)
+ && matches(property, (String) subValue))
+ return true;
+ }
+ }
+ property++;
+ }
+ return false;
+ }
+
+ /**
+ * Notifies this <tt>MacOSXAddrBookContactQuery</tt> about a specific
+ * <tt>ABPerson</tt>.
+ *
+ * @param person a pointer to the <tt>ABPerson</tt> instance to notify about
+ * @return <tt>true</tt> if this <tt>MacOSXAddrBookContactQuery</tt> is to
+ * continue being called; otherwise, <tt>false</tt>
+ */
+ private boolean onPerson(long person)
+ {
+ Object[] values
+ = ABRecord_valuesForProperties(person, ABPERSON_PROPERTIES);
+ final String id = ABRecord_uniqueId(person);
+
+ String displayName = getDisplayName(values);
+ if ((displayName.length() != 0)
+ && (query.matcher(displayName).find() || matches(values)))
+ {
+ List<ContactDetail> contactDetails = getContactDetails(values, id);
+
+ if (!contactDetails.isEmpty())
+ {
+ final MacOSXAddrBookSourceContact sourceContact
+ = new MacOSXAddrBookSourceContact(
+ getContactSource(),
+ displayName,
+ contactDetails);
+ sourceContact.setData(SourceContact.DATA_ID, id);
+
+ try
+ {
+ byte[] image = ABPerson_imageData(person);
+
+ if (image != null)
+ sourceContact.setImage(image);
+ }
+ catch (OutOfMemoryError oome)
+ {
+ // Ignore it, the image is not vital.
+ }
+
+ addQueryResult(sourceContact);
+ }
+ }
+ return (getStatus() == QUERY_IN_PROGRESS);
+ }
+
+ /**
+ * Performs this <tt>AsyncContactQuery</tt> in a background <tt>Thread</tt>.
+ *
+ * @see AsyncContactQuery#run()
+ */
+ protected void run()
+ {
+ foreachPerson(
+ query.toString(),
+ new PtrCallback()
+ {
+ public boolean callback(long person)
+ {
+ return onPerson(person);
+ }
+ });
+ }
+
+ /**
+ * Sets the capabilities of a specific <tt>ContactDetail</tt> (e.g.
+ * <tt>supportedOpSets</tt>) depending on the <tt>ABPerson</tt> property
+ * that it stands for.
+ *
+ * @param contactDetail the <tt>ContactDetail</tt> to set the capabilities
+ * of
+ * @param property the index in {@link #ABPERSON_PROPERTIES} of the
+ * <tt>ABPerson</tt> property represented by <tt>ContactDetail</tt>
+ * @return <tt>contactDetail</tt>
+ */
+ private ContactDetail setCapabilities(
+ ContactDetail contactDetail,
+ int property)
+ {
+ List<Class<? extends OperationSet>> supportedOpSets
+ = new LinkedList<Class<? extends OperationSet>>();
+ Map<Class<? extends OperationSet>, String> preferredProtocols
+ = new HashMap<Class<? extends OperationSet>, String>();
+
+ // can be added as contacts
+ supportedOpSets.add(OperationSetPersistentPresence.class);
+
+ switch (property)
+ {
+ case kABAIMInstantProperty:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.AIM);
+ break;
+ case kABEmailProperty:
+ break;
+ case kABICQInstantProperty:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.ICQ);
+ break;
+ case kABJabberInstantProperty:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.JABBER);
+ supportedOpSets.add(OperationSetBasicTelephony.class);
+ preferredProtocols.put(
+ OperationSetBasicTelephony.class,
+ ProtocolNames.JABBER);
+ break;
+ case kABPhoneProperty:
+ supportedOpSets.add(OperationSetBasicTelephony.class);
+ break;
+ case kABMSNInstantProperty:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.MSN);
+ break;
+ case kABYahooInstantProperty:
+ supportedOpSets.add(OperationSetBasicInstantMessaging.class);
+ preferredProtocols.put(
+ OperationSetBasicInstantMessaging.class,
+ ProtocolNames.YAHOO);
+ break;
+ default:
+ break;
+ }
+ contactDetail.setSupportedOpSets(supportedOpSets);
+ if (!preferredProtocols.isEmpty())
+ contactDetail.setPreferredProtocols(preferredProtocols);
+
+ return contactDetail;
+ }
+
+ /**
+ * Callback method when receiving notifications for inserted items.
+ */
+ public void inserted(long person)
+ {
+ onPerson(person);
+ }
+
+ /**
+ * Callback method when receiving notifications for updated items.
+ */
+ public void updated(long person)
+ {
+ SourceContact sourceContact =
+ findSourceContactByID(ABRecord_uniqueId(person));
+ if(sourceContact != null
+ && sourceContact instanceof MacOSXAddrBookSourceContact)
+ {
+ // let's update the the details
+ Object[] values
+ = ABRecord_valuesForProperties(person, ABPERSON_PROPERTIES);
+ String displayName = getDisplayName(values);
+ final String id = ABRecord_uniqueId(person);
+
+ MacOSXAddrBookSourceContact editableSourceContact
+ = (MacOSXAddrBookSourceContact)sourceContact;
+
+ editableSourceContact.setDisplayName(displayName);
+
+ List<ContactDetail> contactDetails = getContactDetails(values, id);
+ editableSourceContact.setDetails(contactDetails);
+
+ fireContactChanged(sourceContact);
+ }
+ }
+
+ /**
+ * Callback method when receiving notifications for deleted items.
+ */
+ public void deleted(String id)
+ {
+ SourceContact sourceContact = findSourceContactByID(id);
+
+ if(sourceContact != null)
+ fireContactRemoved(sourceContact);
+ }
+
+ /**
+ * Find the property from category and subcategories.
+ *
+ * @param category
+ * @param subCategories
+ * @return
+ */
+ public static int getProperty(
+ Category category,
+ Collection<SubCategory> subCategories)
+ {
+ switch(category)
+ {
+ case Personal:
+ if(subCategories.contains(SubCategory.Name))
+ return kABFirstNameProperty;
+ else if(subCategories.contains(SubCategory.LastName))
+ return kABLastNameProperty;
+ else if(subCategories.contains(SubCategory.Nickname))
+ return kABNicknameProperty;
+ else if(subCategories.contains(SubCategory.HomePage))
+ return kABHomePageProperty;
+ break;
+ case Organization:
+ if(subCategories.contains(SubCategory.JobTitle))
+ return kABJobTitleProperty;
+ else
+ return kABDepartmentProperty;
+ case Email:
+ return kABEmailProperty;
+ case InstantMessaging:
+ if(subCategories.contains(SubCategory.AIM))
+ return kABAIMInstantProperty;
+ else if(subCategories.contains(SubCategory.ICQ))
+ return kABICQInstantProperty;
+ else if(subCategories.contains(SubCategory.MSN))
+ return kABMSNInstantProperty;
+ else if(subCategories.contains(
+ SubCategory.Jabber))
+ return kABJabberInstantProperty;
+ else if(subCategories.contains(
+ SubCategory.Yahoo))
+ return kABYahooInstantProperty;
+ break;
+ case Phone:
+ return kABPhoneProperty;
+ case Address:
+ return kABAddressProperty;
+ default: return -1;
+ }
+
+ return -1;
+ }
+
+ /**
+ * Finds the label from category and sub categories.
+ * @param subCategory
+ * @return
+ */
+ public static String getLabel(
+ int property,
+ SubCategory subCategory,
+ String subProperty)
+ {
+ switch(property)
+ {
+ case kABEmailProperty:
+ if(subCategory == SubCategory.Home)
+ return kABEmailHomeLabel();
+ if(subCategory == SubCategory.Work)
+ return kABEmailWorkLabel();
+ break;
+ case kABICQInstantProperty:
+ case kABAIMInstantProperty:
+ case kABYahooInstantProperty:
+ case kABMSNInstantProperty:
+ case kABJabberInstantProperty:
+ return subProperty;
+ case kABPhoneProperty:
+ if(subCategory == SubCategory.Home)
+ return kABPhoneHomeLabel();
+ if(subCategory == SubCategory.Work)
+ return kABPhoneWorkLabel();
+ if(subCategory == SubCategory.Fax)
+ return kABPhoneWorkFAXLabel();
+ if(subCategory == SubCategory.Mobile)
+ return kABPhoneMobileLabel();
+ if(subCategory == SubCategory.Other)
+ return "other";
+ break;
+ case kABAddressProperty:
+ if(subCategory == SubCategory.Street)
+ return kABAddressStreetKey();
+ if(subCategory == SubCategory.City)
+ return kABAddressCityKey();
+ if(subCategory == SubCategory.State)
+ return kABAddressStateKey();
+ if(subCategory == SubCategory.Country)
+ return kABAddressCountryKey();
+ if(subCategory == SubCategory.PostalCode)
+ return kABAddressZIPKey();
+ break;
+ default: return null;
+ }
+
+ return null;
+ }
+
+ /**
+ * Adds a new empty contact, which will be filled in later.
+ *
+ * @param id The ID of the contact to add.
+ */
+ public void addEmptyContact(String id)
+ {
+ if(id != null)
+ {
+ final MacOSXAddrBookSourceContact sourceContact
+ = new MacOSXAddrBookSourceContact(
+ getContactSource(),
+ null,
+ new LinkedList<ContactDetail>());
+ sourceContact.setData(SourceContact.DATA_ID, id);
+ addQueryResult(sourceContact);
+ }
+ }
+
+ /**
+ * Fires a contact changed event for the given contact.
+ *
+ * @param sourceContact The contact which has changed.
+ */
+ public void contactChanged(SourceContact sourceContact)
+ {
+ fireContactChanged(sourceContact);
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactSourceService.java b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactSourceService.java
index 7129ad2..0150913 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactSourceService.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookContactSourceService.java
@@ -1,202 +1,255 @@
-/*
- * 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.macosx;
-
-import java.util.regex.*;
-
-import net.java.sip.communicator.plugin.addrbook.*;
-import net.java.sip.communicator.service.contactsource.*;
-
-/**
- * Implements <tt>ContactSourceService</tt> for the Address Book of Mac OS X.
- *
- * @author Lyubomir Marinov
- */
-public class MacOSXAddrBookContactSourceService
- extends AsyncContactSourceService
-{
- /**
- * the Mac OS X address book prefix.
- */
- public static final String MACOSX_ADDR_BOOK_PREFIX
- = "net.java.sip.communicator.plugin.addrbook.MACOSX_ADDR_BOOK_PREFIX";
-
- static
- {
- System.loadLibrary("jmacosxaddrbook");
- }
-
- /**
- * The pointer to the native counterpart of this
- * <tt>MacOSXAddrBookContactSourceService</tt>.
- */
- private long ptr;
-
- /**
- * The latest query created.
- */
- private MacOSXAddrBookContactQuery latestQuery;
-
- /**
- * Initializes a new <tt>MacOSXAddrBookContactSourceService</tt> instance.
- */
- public MacOSXAddrBookContactSourceService()
- {
- ptr = start();
- if (0 == ptr)
- throw new IllegalStateException("ptr");
- setDelegate(ptr, new NotificationsDelegate());
- }
-
- /**
- * Gets a human-readable <tt>String</tt> which names this
- * <tt>ContactSourceService</tt> implementation.
- *
- * @return a human-readable <tt>String</tt> which names this
- * <tt>ContactSourceService</tt> implementation
- * @see ContactSourceService#getDisplayName()
- */
- public String getDisplayName()
- {
- return "Address Book";
- }
-
- /**
- * Gets a <tt>String</tt> which uniquely identifies the instances of the
- * <tt>MacOSXAddrBookContactSourceService</tt> implementation.
- *
- * @return a <tt>String</tt> which uniquely identifies the instances of the
- * <tt>MacOSXAddrBookContactSourceService</tt> implementation
- * @see ContactSourceService#getType()
- */
- public int getType()
- {
- return SEARCH_TYPE;
- }
-
- /**
- * Queries this <tt>ContactSourceService</tt> for <tt>SourceContact</tt>s
- * which match a specific <tt>query</tt> <tt>Pattern</tt>.
- *
- * @param query the <tt>Pattern</tt> which this
- * <tt>ContactSourceService</tt> is being queried for
- * @return a <tt>ContactQuery</tt> which represents the query of this
- * <tt>ContactSourceService</tt> implementation for the specified
- * <tt>Pattern</tt> and via which the matching <tt>SourceContact</tt>s (if
- * any) will be returned
- * @see ExtendedContactSourceService#queryContactSource(Pattern)
- */
- public ContactQuery queryContactSource(Pattern query)
- {
- if(latestQuery != null)
- latestQuery.clear();
-
- latestQuery = new MacOSXAddrBookContactQuery(this, query);
-
- latestQuery.start();
- return latestQuery;
- }
-
- /**
- * Starts a new native <tt>MacOSXAddrBookContactSourceService</tt> instance.
- *
- * @return a pointer to the newly-started native
- * <tt>MacOSXAddrBookContactSourceService</tt> instance
- */
- private static native long start();
-
- /**
- * Stops this <tt>ContactSourceService</tt> implementation and prepares it
- * for garbage collection.
- *
- * @see AsyncContactSourceService#stop()
- */
- public synchronized void stop()
- {
- if (0 != ptr)
- {
- if(latestQuery != null)
- {
- latestQuery.clear();
- latestQuery = null;
- }
-
- stop(ptr);
- ptr = 0;
- }
- }
-
- /**
- * Returns the global phone number prefix to be used when calling contacts
- * from this contact source.
- *
- * @return the global phone number prefix
- */
- public String getPhoneNumberPrefix()
- {
- return AddrBookActivator.getConfigService()
- .getString(MACOSX_ADDR_BOOK_PREFIX);
- }
-
- /**
- * Returns the index of the contact source in the result list.
- *
- * @return the index of the contact source in the result list
- */
- public int getIndex()
- {
- return -1;
- }
-
- /**
- * Stops a native <tt>MacOSXAddrBookContactSourceService</tt>.
- *
- * @param ptr the pointer to the native
- * <tt>MacOSXAddrBookContactSourceService</tt> to stop
- */
- private static native void stop(long ptr);
-
- /**
- * Sets notifier delegate.
- * @param ptr
- * @param delegate
- */
- public static native void setDelegate(long ptr, NotificationsDelegate delegate);
-
- /**
- * Delegate class to be notified for addressbook changes.
- */
- public class NotificationsDelegate
- {
- /**
- * 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);
- }
- }
-}
+/*
+ * 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.macosx;
+
+import java.util.regex.*;
+
+import net.java.sip.communicator.plugin.addrbook.*;
+import net.java.sip.communicator.service.contactsource.*;
+
+/**
+ * Implements <tt>ContactSourceService</tt> for the Address Book of Mac OS X.
+ *
+ * @author Lyubomir Marinov
+ */
+public class MacOSXAddrBookContactSourceService
+ extends AsyncContactSourceService
+ implements EditableContactSourceService
+{
+ /**
+ * the Mac OS X address book prefix.
+ */
+ public static final String MACOSX_ADDR_BOOK_PREFIX
+ = "net.java.sip.communicator.plugin.addrbook.MACOSX_ADDR_BOOK_PREFIX";
+
+ static
+ {
+ System.loadLibrary("jmacosxaddrbook");
+ }
+
+ /**
+ * The pointer to the native counterpart of this
+ * <tt>MacOSXAddrBookContactSourceService</tt>.
+ */
+ private long ptr;
+
+ /**
+ * The latest query created.
+ */
+ private MacOSXAddrBookContactQuery latestQuery;
+
+ /**
+ * Initializes a new <tt>MacOSXAddrBookContactSourceService</tt> instance.
+ */
+ public MacOSXAddrBookContactSourceService()
+ {
+ ptr = start();
+ if (0 == ptr)
+ throw new IllegalStateException("ptr");
+ setDelegate(ptr, new NotificationsDelegate());
+ }
+
+ /**
+ * Gets a human-readable <tt>String</tt> which names this
+ * <tt>ContactSourceService</tt> implementation.
+ *
+ * @return a human-readable <tt>String</tt> which names this
+ * <tt>ContactSourceService</tt> implementation
+ * @see ContactSourceService#getDisplayName()
+ */
+ public String getDisplayName()
+ {
+ return "Address Book";
+ }
+
+ /**
+ * Gets a <tt>String</tt> which uniquely identifies the instances of the
+ * <tt>MacOSXAddrBookContactSourceService</tt> implementation.
+ *
+ * @return a <tt>String</tt> which uniquely identifies the instances of the
+ * <tt>MacOSXAddrBookContactSourceService</tt> implementation
+ * @see ContactSourceService#getType()
+ */
+ public int getType()
+ {
+ return SEARCH_TYPE;
+ }
+
+ /**
+ * Queries this <tt>ContactSourceService</tt> for <tt>SourceContact</tt>s
+ * which match a specific <tt>query</tt> <tt>Pattern</tt>.
+ *
+ * @param query the <tt>Pattern</tt> which this
+ * <tt>ContactSourceService</tt> is being queried for
+ * @return a <tt>ContactQuery</tt> which represents the query of this
+ * <tt>ContactSourceService</tt> implementation for the specified
+ * <tt>Pattern</tt> and via which the matching <tt>SourceContact</tt>s (if
+ * any) will be returned
+ * @see ExtendedContactSourceService#queryContactSource(Pattern)
+ */
+ public ContactQuery queryContactSource(Pattern query)
+ {
+ if(latestQuery != null)
+ latestQuery.clear();
+
+ latestQuery = new MacOSXAddrBookContactQuery(this, query);
+
+ latestQuery.start();
+ return latestQuery;
+ }
+
+ /**
+ * Starts a new native <tt>MacOSXAddrBookContactSourceService</tt> instance.
+ *
+ * @return a pointer to the newly-started native
+ * <tt>MacOSXAddrBookContactSourceService</tt> instance
+ */
+ private static native long start();
+
+ /**
+ * Stops this <tt>ContactSourceService</tt> implementation and prepares it
+ * for garbage collection.
+ *
+ * @see AsyncContactSourceService#stop()
+ */
+ public synchronized void stop()
+ {
+ if (0 != ptr)
+ {
+ if(latestQuery != null)
+ {
+ latestQuery.clear();
+ latestQuery = null;
+ }
+
+ stop(ptr);
+ ptr = 0;
+ }
+ }
+
+ /**
+ * Returns the global phone number prefix to be used when calling contacts
+ * from this contact source.
+ *
+ * @return the global phone number prefix
+ */
+ public String getPhoneNumberPrefix()
+ {
+ return AddrBookActivator.getConfigService()
+ .getString(MACOSX_ADDR_BOOK_PREFIX);
+ }
+
+ /**
+ * Returns the index of the contact source in the result list.
+ *
+ * @return the index of the contact source in the result list
+ */
+ public int getIndex()
+ {
+ return -1;
+ }
+
+ /**
+ * Stops a native <tt>MacOSXAddrBookContactSourceService</tt>.
+ *
+ * @param ptr the pointer to the native
+ * <tt>MacOSXAddrBookContactSourceService</tt> to stop
+ */
+ private static native void stop(long ptr);
+
+ /**
+ * Sets notifier delegate.
+ * @param ptr
+ * @param delegate
+ */
+ public static native void setDelegate(long ptr, NotificationsDelegate delegate);
+
+ /**
+ * Delegate class to be notified for addressbook changes.
+ */
+ public class NotificationsDelegate
+ {
+ /**
+ * 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);
+ }
+ }
+
+ /**
+ * Returns the latest query created.
+ *
+ * @return the latest query created.
+ */
+ public MacOSXAddrBookContactQuery getLatestQuery()
+ {
+ return this.latestQuery;
+ }
+
+ /**
+ * Creates a new contact from the database (i.e "contacts" or
+ * "msoutlook", etc.).
+ *
+ * @return The ID of the contact to created. NULL if failed to create a new
+ * contact.
+ */
+ public String createContact()
+ {
+ return MacOSXAddrBookContactQuery.createContact();
+ }
+
+ /**
+ * Adds a new empty contact, which will be filled in later.
+ *
+ * @param id The ID of the contact to add.
+ */
+ public void addEmptyContact(String id)
+ {
+ if(id != null && latestQuery != null)
+ {
+ latestQuery.addEmptyContact(id);
+ }
+ }
+
+ /**
+ * Removes the given contact from the database (i.e "contacts" or
+ * "msoutlook", etc.).
+ *
+ * @param id The ID of the contact to remove.
+ */
+ public void deleteContact(String id)
+ {
+ if(id != null && MacOSXAddrBookContactQuery.deleteContact(id))
+ {
+ if(latestQuery != null)
+ {
+ latestQuery.deleted(id);
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookSourceContact.java b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookSourceContact.java
index 274b749..3342731 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookSourceContact.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/macosx/MacOSXAddrBookSourceContact.java
@@ -32,6 +32,14 @@ public class MacOSXAddrBookSourceContact
= Logger.getLogger(MacOSXAddrBookSourceContact.class);
/**
+ * Boolean used to temporarily lock the access to a single modification
+ * source (jitsi or contact). i.e. it can be useful if Jitsi modifies a
+ * batch of details and do not want to receive contact update notification
+ * which can produce concurrent changes of the details.
+ */
+ private Boolean locked = Boolean.FALSE;
+
+ /**
* Initializes a new <tt>AddrBookSourceContact</tt> instance.
*
* @param contactSource the <tt>ContactSourceService</tt> which is creating
@@ -64,7 +72,7 @@ public class MacOSXAddrBookSourceContact
*/
public void addContactDetail(ContactDetail detail)
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
String id = (String)getData(SourceContact.DATA_ID);
@@ -160,6 +168,7 @@ public class MacOSXAddrBookSourceContact
}
contactDetails.add(index, contactDetail);
+ this.updated();
}
}
@@ -316,7 +325,7 @@ public class MacOSXAddrBookSourceContact
*/
public void removeContactDetail(ContactDetail detail)
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
//remove the detail from the addressbook
String id = (String)getData(SourceContact.DATA_ID);
@@ -360,6 +369,7 @@ public class MacOSXAddrBookSourceContact
logger.warn("No id or wrong ContactDetail " + detail);
contactDetails.remove(detail);
+ this.updated();
}
}
@@ -369,7 +379,7 @@ public class MacOSXAddrBookSourceContact
*/
public void setDetails(List<ContactDetail> details)
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
contactDetails.clear();
contactDetails.addAll(details);
@@ -377,6 +387,82 @@ public class MacOSXAddrBookSourceContact
}
/**
+ * Function called by the native part (contact) when this contact has been
+ * updated.
+ */
+ public void updated()
+ {
+ synchronized(this)
+ {
+ waitUnlock();
+
+ String id = (String)getData(SourceContact.DATA_ID);
+ ContactSourceService sourceService = getContactSource();
+ if(id != null
+ && sourceService instanceof
+ MacOSXAddrBookContactSourceService)
+ {
+ MacOSXAddrBookContactSourceService macOSXSourceService
+ = (MacOSXAddrBookContactSourceService) sourceService;
+ MacOSXAddrBookContactQuery macOSXContactQuery
+ = macOSXSourceService.getLatestQuery();
+ if(macOSXContactQuery != null)
+ {
+ long contactPointer
+ = MacOSXAddrBookContactQuery.getContactPointer(id);
+ macOSXContactQuery.updated(contactPointer);
+ //macOSXContactQuery.contactChanged(this);
+ }
+ }
+ }
+ }
+
+ /**
+ * Locks this object before adding or removing several contact details.
+ */
+ public void lock()
+ {
+ synchronized(this)
+ {
+ locked = Boolean.TRUE;
+ }
+ }
+
+ /**
+ * Unlocks this object before after or removing several contact details.
+ */
+ public void unlock()
+ {
+ synchronized(this)
+ {
+ locked = Boolean.FALSE;
+ notify();
+ }
+ }
+
+ /**
+ * Waits to be unlocked. This object must be synchronized before calling
+ * this function.
+ */
+ private void waitUnlock()
+ {
+ boolean continueToWait = this.locked;
+
+ while(continueToWait)
+ {
+ try
+ {
+ wait();
+ continueToWait = false;
+ }
+ catch(InterruptedException ie)
+ {
+ // Nothing to do, we will wait until the notify.
+ }
+ }
+ }
+
+ /**
* Returns the index of this source contact in its parent.
*
* @return the index of this source contact in its parent
diff --git a/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactQuery.java b/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactQuery.java
index 2ae7c51..b886ddb 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactQuery.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactQuery.java
@@ -1,889 +1,1116 @@
-/*
- * 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 java.util.*;
-import java.util.regex.*;
-
-import net.java.sip.communicator.plugin.addrbook.*;
-import net.java.sip.communicator.service.contactsource.*;
-import net.java.sip.communicator.service.protocol.*;
-import net.java.sip.communicator.util.*;
-
-/**
- * Implements <tt>ContactQuery</tt> for the Address Book of Microsoft Outlook.
- *
- * @author Lyubomir Marinov
- * @author Vincent Lucas
- */
-public class MsOutlookAddrBookContactQuery
- extends AbstractAddrBookContactQuery<MsOutlookAddrBookContactSourceService>
-{
- /**
- * The <tt>Logger</tt> used by the <tt>MsOutlookAddrBookContactQuery</tt>
- * class and its instances for logging output.
- */
- private static final Logger logger
- = Logger.getLogger(MsOutlookAddrBookContactQuery.class);
-
- private static final int dispidEmail1EmailAddress = 11;
-
- private static final int dispidEmail2EmailAddress = 12;
-
- private static final int dispidEmail3EmailAddress = 13;
-
- /**
- * The object type of a <tt>SourceContact</tt> in the Address Book of
- * Microsoft Outlook.
- */
- private static final long MAPI_MAILUSER = 0x00000006;
-
- /**
- * The IDs of the properties of <tt>MAPI_MAILUSER</tt> which are to be
- * queried by the <tt>MsOutlookAddrBookContactQuery</tt> instances.
- */
- private static final long[] MAPI_MAILUSER_PROP_IDS
- = new long[]
- {
- 0x3001 /* PR_DISPLAY_NAME */,
- 0x3003 /* PR_EMAIL_ADDRESS */,
- 0x3A06 /* PR_GIVEN_NAME */,
- 0x3A44 /* PR_MIDDLE_NAME */,
- 0x3A11 /* PR_SURNAME */,
- 0x3A08 /* PR_BUSINESS_TELEPHONE_NUMBER */,
- 0x3A1B /* PR_BUSINESS2_TELEPHONE_NUMBER */,
- 0x3A09 /* PR_HOME_TELEPHONE_NUMBER */,
- 0x3A2F /* PR_HOME2_TELEPHONE_NUMBER */,
- 0x3A1C /* PR_MOBILE_TELEPHONE_NUMBER */,
- 0x0FFE /* PR_OBJECT_TYPE */,
- 0x00008083 /* dispidEmail1EmailAddress */,
- 0x00008093 /* dispidEmail2EmailAddress */,
- 0x000080A3 /* dispidEmail3EmailAddress */,
- 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
- };
-
- /**
- * The object type of a <tt>SourceContact</tt> in a Contacts folder of
- * Microsoft Outlook.
- */
- private static final long MAPI_MESSAGE = 0x00000005;
-
- /**
- * The flag which signals that MAPI strings should be returned in the
- * Unicode character set.
- */
- private static final long MAPI_UNICODE = 0x80000000;
-
- /**
- * The id of the <tt>PR_ATTACHMENT_CONTACTPHOTO</tt> MAPI property.
- */
- private static final long PR_ATTACHMENT_CONTACTPHOTO = 0x7FFF;
-
- /**
- * The index of the id of the <tt>PR_BUSINESS_TELEPHONE_NUMBER</tt> property
- * in {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_BUSINESS_TELEPHONE_NUMBER = 5;
-
- /**
- * The index of the id of the <tt>PR_BUSINESS2_TELEPHONE_NUMBER</tt>
- * property in {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_BUSINESS2_TELEPHONE_NUMBER = 6;
-
- private static final int PR_COMPANY_NAME = 14;
-
- /**
- * The index of the id of the <tt>PR_DISPLAY_NAME</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_DISPLAY_NAME = 0;
-
- /**
- * The index of the id of the <tt>PR_EMAIL_ADDRESS</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_EMAIL_ADDRESS = 1;
-
- /**
- * The index of the id of the <tt>PR_GIVEN_NAME</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_GIVEN_NAME = 2;
-
- /**
- * The index of the id of the <tt>PR_HOME_TELEPHONE_NUMBER</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_HOME_TELEPHONE_NUMBER = 7;
-
- /**
- * The index of the id of the <tt>PR_HOME2_TELEPHONE_NUMBER</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_HOME2_TELEPHONE_NUMBER = 8;
-
- /**
- * The index of the id of the <tt>PR_MIDDLE_NAME</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_MIDDLE_NAME = 3;
-
- /**
- * The index of the id of the <tt>PR_MOBILE_TELEPHONE_NUMBER</tt> property
- * in {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_MOBILE_TELEPHONE_NUMBER = 9;
-
- /**
- * The index of the id of the <tt>PR_OBJECT_TYPE</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- private static final int PR_OBJECT_TYPE = 10;
-
- /**
- * The index of the id of the <tt>PR_SURNAME</tt> property in
- * {@link #MAPI_MAILUSER_PROP_IDS}.
- */
- 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
- * <tt>ContactDetail</tt>s.
- */
- private static final int[] CONTACT_DETAIL_PROP_INDEXES
- = 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,
- PR_HOME2_TELEPHONE_NUMBER,
- PR_MOBILE_TELEPHONE_NUMBER,
- dispidEmail1EmailAddress,
- dispidEmail2EmailAddress,
- 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
- };
-
- /**
- * The indexes in {@link #MAPI_MAILUSER_PROP_IDS} of the property IDs which
- * represent an identifier which can be used for telephony or persistent
- * presence.
- */
- private static final int[] CONTACT_OPERATION_SET_ABLE_PROP_INDEXES
- = new int[]
- {
- PR_EMAIL_ADDRESS,
- PR_BUSINESS_TELEPHONE_NUMBER,
- PR_BUSINESS2_TELEPHONE_NUMBER,
- PR_HOME_TELEPHONE_NUMBER,
- PR_HOME2_TELEPHONE_NUMBER,
- PR_MOBILE_TELEPHONE_NUMBER,
- dispidEmail1EmailAddress,
- dispidEmail2EmailAddress,
- dispidEmail3EmailAddress,
- dispidFax1EmailAddress,
- dispidFax2EmailAddress,
- dispidFax3EmailAddress
- //dispidInstMsg
- };
-
- static
- {
- System.loadLibrary("jmsoutlookaddrbook");
- }
-
- /**
- * The number of <tt>SourceContact</tt>s matching this <tt>ContactQuery</tt>
- * which have been retrieved from Contacts folders. Since each one of them
- * may appear multiple times in the Address Book as well, no matching in the
- * Address Book will be performed if there is at least one matching
- * <tt>SourceContact</tt> in a Contacts folder.
- */
- private int mapiMessageCount;
-
- /**
- * Initializes a new <tt>MsOutlookAddrBookContactQuery</tt> instance to
- * be performed by a specific
- * <tt>MsOutlookAddrBookContactSourceService</tt>.
- *
- * @param msoabcss the <tt>MsOutlookAddrBookContactSourceService</tt>
- * which is to perform the new <tt>ContactQuery</tt>
- * @param query the <tt>Pattern</tt> for which <tt>msoabcss</tt> is being
- * queried
- */
- public MsOutlookAddrBookContactQuery(
- MsOutlookAddrBookContactSourceService msoabcss,
- Pattern query)
- {
- super(msoabcss, query);
- }
-
- /**
- * Calls back to a specific <tt>PtrCallback</tt> for each
- * <tt>MAPI_MAILUSER</tt> found in the Address Book of Microsoft Outlook
- * which matches a specific <tt>String</tt> query.
- *
- * @param query the <tt>String</tt> for which the Address Book of Microsoft
- * Outlook is to be queried. <b>Warning</b>: Ignored at the time of this
- * writing.
- * @param callback the <tt>PtrCallback</tt> to be notified about the
- * matching <tt>MAPI_MAILUSER</tt>s
- */
- public static native void foreachMailUser(
- String query,
- PtrCallback callback);
-
- 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:
- case PR_EMAIL_ADDRESS:
- return ContactDetail.Category.Email;
- case PR_BUSINESS2_TELEPHONE_NUMBER:
- case PR_BUSINESS_TELEPHONE_NUMBER:
- 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;
- }
- }
-
- /**
- * Gets the set of <tt>ContactDetail</tt> labels to be assigned to a
- * property specified by its index in {@link #MAPI_MAILUSER_PROP_IDS}.
- *
- * @param propIndex the index in <tt>MAPI_MAILUSER_PROP_IDS</tt> of the
- * property to get the <tt>ContactDetail</tt> labels of
- * @return the set of <tt>ContactDetail</tt> labels to be assigned to the
- * property specified by its index in <tt>MAPI_MAILUSER_PROP_IDS</tt>
- */
- private ContactDetail.SubCategory[] getSubCategories(int propIndex)
- {
- switch (propIndex)
- {
- case PR_GIVEN_NAME:
- case PR_MIDDLE_NAME:
- case PR_COMPANY_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_BUSINESS2_TELEPHONE_NUMBER:
- case PR_BUSINESS_TELEPHONE_NUMBER:
- case dispidEmail1EmailAddress:
- case PR_EMAIL_ADDRESS:
- //case dispidWorkAddress:
- return
- new ContactDetail.SubCategory[]
- {
- ContactDetail.SubCategory.Work
- };
- case PR_HOME2_TELEPHONE_NUMBER:
- case PR_HOME_TELEPHONE_NUMBER:
- case dispidEmail2EmailAddress:
- //case dispidHomeAddress:
- return
- new ContactDetail.SubCategory[]
- {
- ContactDetail.SubCategory.Home
- };
- case PR_MOBILE_TELEPHONE_NUMBER:
- return
- new ContactDetail.SubCategory[]
- {
- 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
- // };
- case dispidEmail3EmailAddress:
- 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.Name))
- return MAPI_MAILUSER_PROP_IDS[PR_COMPANY_NAME];
- else
- return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_HOME_PAGE];
- case Email:
- if(subCategories.contains(ContactDetail.SubCategory.Work))
- return MAPI_MAILUSER_PROP_IDS[dispidEmail1EmailAddress];
- // PR_EMAIL_ADDRESS:
- else if(subCategories.contains(
- ContactDetail.SubCategory.Home))
- return MAPI_MAILUSER_PROP_IDS[dispidEmail2EmailAddress];
- else if(subCategories.contains(
- ContactDetail.SubCategory.Other))
- return MAPI_MAILUSER_PROP_IDS[dispidEmail3EmailAddress];
- break;
- 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;
- default:
- // Silence the compiler.
- 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.
- *
- * @param propIndex the index in <tt>MAPI_MAILUSER_PROP_IDS</tt> of the
- * property to check
- * @return <tt>true</tt> if <tt>propIndex</tt> stands for a property with a
- * phone number value; otherwise, <tt>false</tt>
- */
- private boolean isPhoneNumber(int propIndex)
- {
- switch (propIndex)
- {
- case PR_BUSINESS2_TELEPHONE_NUMBER:
- case PR_BUSINESS_TELEPHONE_NUMBER:
- case PR_HOME2_TELEPHONE_NUMBER:
- case PR_HOME_TELEPHONE_NUMBER:
- case PR_MOBILE_TELEPHONE_NUMBER:
- return true;
- default:
- return false;
- }
- }
-
- /**
- * Determines whether a specific <tt>MAPI_MAILUSER</tt> property with a
- * specific <tt>value</tt> matches the {@link #query} of this
- * <tt>AsyncContactQuery</tt>.
- *
- * @param property the <tt>MAPI_MAILUSER</tt> property to check
- * @param value the value of the <tt>property</tt> to check
- * @return <tt>true</tt> if the specified <tt>value</tt> of the specified
- * <tt>property</tt> matches the <tt>query</tt> of this
- * <tt>AsyncContactQuery</tt>; otherwise, <tt>false</tt>
- */
- private boolean matches(int property, String value)
- {
- return
- query.matcher(value).find()
- || (isPhoneNumber(property) && phoneNumberMatches(value));
- }
-
- /**
- * Notifies this <tt>MsOutlookAddrBookContactQuery</tt> about a specific
- * <tt>MAPI_MAILUSER</tt>.
- *
- * @param iUnknown a pointer to an <tt>IUnknown</tt> instance for the
- * <tt>MAPI_MAILUSER</tt> to notify about
- * @return <tt>true</tt> if this <tt>MsOutlookAddrBookContactQuery</tt>
- * is to continue being called; otherwise, <tt>false</tt>
- * @throws MsOutlookMAPIHResultException if anything goes wrong while
- * getting the properties of the specified <tt>MAPI_MAILUSER</tt>
- */
- private boolean onMailUser(long iUnknown)
- throws MsOutlookMAPIHResultException
- {
- Object[] props
- = IMAPIProp_GetProps(
- iUnknown,
- MAPI_MAILUSER_PROP_IDS,
- MAPI_UNICODE);
- long objType
- = (props[PR_OBJECT_TYPE] instanceof Long)
- ? ((Long) props[PR_OBJECT_TYPE]).longValue()
- : 0;
-
- /*
- * If we have results from the Contacts folder(s), don't read from the
- * Address Book because there may be duplicates.
- */
- if ((MAPI_MAILUSER == objType) && (mapiMessageCount != 0))
- return false;
-
- int propIndex = 0;
- boolean matches = false;
-
- for (Object prop : props)
- {
- if ((prop instanceof String) && matches(propIndex, (String) prop))
- {
- matches = true;
- break;
- }
- propIndex++;
- }
- if (matches)
- {
- List<ContactDetail> contactDetails = getContactDetails(props);
-
- /*
- * What's the point of showing a contact who has no contact details?
- */
- if (!contactDetails.isEmpty())
- {
- String displayName = (String) props[PR_DISPLAY_NAME];
-
- if ((displayName == null) || (displayName.length() == 0))
- displayName = (String) props[PR_COMPANY_NAME];
-
- MsOutlookAddrBookSourceContact sourceContact
- = new MsOutlookAddrBookSourceContact(
- getContactSource(),
- (String) props[PR_ORIGINAL_ENTRYID],
- displayName,
- contactDetails);
-
- if (MAPI_MESSAGE == objType)
- {
- ++mapiMessageCount;
-
- try
- {
- Object[] images
- = IMAPIProp_GetProps(
- iUnknown,
- new long[] { PR_ATTACHMENT_CONTACTPHOTO },
- 0);
- Object image = images[0];
-
- if (image instanceof byte[])
- sourceContact.setImage((byte[]) image);
- }
- catch (MsOutlookMAPIHResultException ex)
- {
- /*
- * Ignore it, the image isn't as vital as the
- * SourceContact.
- */
- }
- }
-
- addQueryResult(sourceContact);
- }
- }
- 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]);
-
- // Check if this contact detail support the telephony and
- // the persistent presence operation set.
- for(int j = 0;
- j < CONTACT_OPERATION_SET_ABLE_PROP_INDEXES.length;
- ++j)
- {
- if(property
- == CONTACT_OPERATION_SET_ABLE_PROP_INDEXES[j])
- {
- contactDetail.setSupportedOpSets(supportedOpSets);
- // Found, then break the loop.
- j = CONTACT_OPERATION_SET_ABLE_PROP_INDEXES.length;
- }
- }
- contactDetails.add(contactDetail);
- }
- }
- }
-
- return contactDetails;
- }
-
- /**
- * Performs this <tt>AsyncContactQuery</tt> in a background <tt>Thread</tt>.
- *
- * @see AsyncContactQuery#run()
- */
- protected void run()
- {
- synchronized (MsOutlookAddrBookContactQuery.class)
- {
- foreachMailUser(
- query.toString(),
- new PtrCallback()
- {
- public boolean callback(long iUnknown)
- {
- try
- {
- return onMailUser(iUnknown);
- }
- catch (MsOutlookMAPIHResultException e)
- {
- if (logger.isDebugEnabled())
- {
- logger.debug(
- MsOutlookAddrBookContactQuery.class
- .getSimpleName()
- + "#onMailUser(long)",
- e);
- }
- return false;
- }
- }
- });
- }
- }
-
- /**
- * 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 person The pointer to the outlook contact object.
- */
- public void updated(long person)
- {
- Object[] props = null;
- try
- {
- props = IMAPIProp_GetProps(
- person,
- MAPI_MAILUSER_PROP_IDS,
- MAPI_UNICODE);
- }
- catch (MsOutlookMAPIHResultException e)
- {
- 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);
- }
- }
- }
-}
+/*
+ * 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 java.util.*;
+import java.util.regex.*;
+
+import net.java.sip.communicator.plugin.addrbook.*;
+import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.service.protocol.*;
+import net.java.sip.communicator.util.*;
+
+/**
+ * Implements <tt>ContactQuery</tt> for the Address Book of Microsoft Outlook.
+ *
+ * @author Lyubomir Marinov
+ * @author Vincent Lucas
+ */
+public class MsOutlookAddrBookContactQuery
+ extends AbstractAddrBookContactQuery<MsOutlookAddrBookContactSourceService>
+{
+ /**
+ * The <tt>Logger</tt> used by the <tt>MsOutlookAddrBookContactQuery</tt>
+ * class and its instances for logging output.
+ */
+ private static final Logger logger
+ = Logger.getLogger(MsOutlookAddrBookContactQuery.class);
+
+ private static final int dispidEmail1EmailAddress = 12;
+
+ private static final int dispidEmail2EmailAddress = 13;
+
+ private static final int dispidEmail3EmailAddress = 14;
+
+ /**
+ * The object type of a <tt>SourceContact</tt> in the Address Book of
+ * Microsoft Outlook.
+ */
+ private static final long MAPI_MAILUSER = 0x00000006;
+
+ /**
+ * The IDs of the properties of <tt>MAPI_MAILUSER</tt> which are to be
+ * queried by the <tt>MsOutlookAddrBookContactQuery</tt> instances.
+ */
+ public static final long[] MAPI_MAILUSER_PROP_IDS
+ = new long[]
+ {
+ 0x3001 /* PR_DISPLAY_NAME */,
+ 0x3003 /* PR_EMAIL_ADDRESS */,
+ 0x3A06 /* PR_GIVEN_NAME */,
+ 0x3A44 /* PR_MIDDLE_NAME */,
+ 0x3A11 /* PR_SURNAME */,
+ 0x3A08 /* PR_BUSINESS_TELEPHONE_NUMBER */,
+ 0x3A1B /* PR_BUSINESS2_TELEPHONE_NUMBER */,
+ 0x3A09 /* PR_HOME_TELEPHONE_NUMBER */,
+ 0x3A2F /* PR_HOME2_TELEPHONE_NUMBER */,
+ 0x3A1C /* PR_MOBILE_TELEPHONE_NUMBER */,
+ 0x3A1F /* PR_OTHER_TELEPHONE_NUMBER */,
+ 0x0FFE /* PR_OBJECT_TYPE */,
+ 0x00008083 /* dispidEmail1EmailAddress */,
+ 0x00008093 /* dispidEmail2EmailAddress */,
+ 0x000080A3 /* dispidEmail3EmailAddress */,
+ 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 */,
+ 0x3A17 /* PR_TITLE */,
+ 0x00008062 /* dispidInstMsg */,
+ 0x3A27, // PR_BUSINESS_ADDRESS_CITY
+ 0x3A26, // PR_BUSINESS_ADDRESS_COUNTRY
+ 0x3A2A, // PR_BUSINESS_ADDRESS_POSTAL_CODE
+ 0x3A28, // PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE
+ 0x3A29, // PR_BUSINESS_ADDRESS_STREET
+
+ 0x3A59, // PR_HOME_ADDRESS_CITY
+ 0x3A5A, // PR_HOME_ADDRESS_COUNTRY
+ 0x3A5B, // PR_HOME_ADDRESS_POSTAL_CODE
+ 0x3A5C, // PR_HOME_ADDRESS_STATE_OR_PROVINCE
+ 0x3A5D // PR_HOME_ADDRESS_STREET
+ };
+
+ /**
+ * The object type of a <tt>SourceContact</tt> in a Contacts folder of
+ * Microsoft Outlook.
+ */
+ private static final long MAPI_MESSAGE = 0x00000005;
+
+ /**
+ * The flag which signals that MAPI strings should be returned in the
+ * Unicode character set.
+ */
+ public static final long MAPI_UNICODE = 0x80000000;
+
+ /**
+ * The id of the <tt>PR_ATTACHMENT_CONTACTPHOTO</tt> MAPI property.
+ */
+ private static final long PR_ATTACHMENT_CONTACTPHOTO = 0x7FFF;
+
+ /**
+ * The index of the id of the <tt>PR_BUSINESS_TELEPHONE_NUMBER</tt> property
+ * in {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_BUSINESS_TELEPHONE_NUMBER = 5;
+
+ /**
+ * The index of the id of the <tt>PR_BUSINESS2_TELEPHONE_NUMBER</tt>
+ * property in {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_BUSINESS2_TELEPHONE_NUMBER = 6;
+
+ private static final int PR_COMPANY_NAME = 15;
+
+ /**
+ * The index of the id of the <tt>PR_DISPLAY_NAME</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_DISPLAY_NAME = 0;
+
+ /**
+ * The index of the id of the <tt>PR_EMAIL_ADDRESS</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_EMAIL_ADDRESS = 1;
+
+ /**
+ * The index of the id of the <tt>PR_GIVEN_NAME</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_GIVEN_NAME = 2;
+
+ /**
+ * The index of the id of the <tt>PR_HOME_TELEPHONE_NUMBER</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_HOME_TELEPHONE_NUMBER = 7;
+
+ /**
+ * The index of the id of the <tt>PR_HOME2_TELEPHONE_NUMBER</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_HOME2_TELEPHONE_NUMBER = 8;
+
+ /**
+ * The index of the id of the <tt>PR_MIDDLE_NAME</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_MIDDLE_NAME = 3;
+
+ /**
+ * The index of the id of the <tt>PR_MOBILE_TELEPHONE_NUMBER</tt> property
+ * in {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_MOBILE_TELEPHONE_NUMBER = 9;
+
+ /**
+ * The index of the id of the <tt>PR_OTHER_TELEPHONE_NUMBER</tt> property
+ * in {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_OTHER_TELEPHONE_NUMBER = 10;
+
+ /**
+ * The index of the id of the <tt>PR_OBJECT_TYPE</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ private static final int PR_OBJECT_TYPE = 11;
+
+ /**
+ * The index of the id of the <tt>PR_SURNAME</tt> property in
+ * {@link #MAPI_MAILUSER_PROP_IDS}.
+ */
+ 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 = 16;
+
+ /**
+ * The index of the 1st fax telephone number (business fax).
+ */
+ private static final int dispidFax1EmailAddress = 17;
+
+ /**
+ * The index of the 2nd fax telephone number (home fax).
+ */
+ private static final int dispidFax2EmailAddress = 18;
+
+ /**
+ * The index of the 3rd fax telephone number (other fax).
+ */
+ private static final int dispidFax3EmailAddress = 19;
+
+ /**
+ * The index of the nickname.
+ */
+ private static final int PR_NICKNAME = 20;
+
+ /**
+ * The index of the name prefix.
+ */
+ private static final int PR_DISPLAY_NAME_PREFIX = 21;
+
+ /**
+ * The index of the personnal home page
+ */
+ private static final int PR_PERSONAL_HOME_PAGE = 22;
+
+ /**
+ * The index of the business home page
+ */
+ private static final int PR_BUSINESS_HOME_PAGE = 23;
+
+ /**
+ * The index of the job title.
+ */
+ private static final int PR_TITLE = 24;
+
+ /**
+ * The index of the instant messaging address.
+ */
+ private static final int dispidInstMsg = 25;
+
+ /**
+ * The index of the business city of the postal address.
+ */
+ private static final int PR_BUSINESS_ADDRESS_CITY = 26;
+
+ /**
+ * The index of the business country of the postal address.
+ */
+ private static final int PR_BUSINESS_ADDRESS_COUNTRY = 27;
+
+ /**
+ * The index of the business postal code of the postal address.
+ */
+ private static final int PR_BUSINESS_ADDRESS_POSTAL_CODE = 28;
+
+ /**
+ * The index of the business state or province of the postal address.
+ */
+ private static final int PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE = 29;
+
+ /**
+ * The index of the business street of the postal address.
+ */
+ private static final int PR_BUSINESS_ADDRESS_STREET = 30;
+
+ /**
+ * The index of the home city of the postal address.
+ */
+ private static final int PR_HOME_ADDRESS_CITY = 31;
+
+ /**
+ * The index of the home country of the postal address.
+ */
+ private static final int PR_HOME_ADDRESS_COUNTRY = 32;
+
+ /**
+ * The index of the home postal code of the postal address.
+ */
+ private static final int PR_HOME_ADDRESS_POSTAL_CODE = 33;
+
+ /**
+ * The index of the home state or province of the postal address.
+ */
+ private static final int PR_HOME_ADDRESS_STATE_OR_PROVINCE = 34;
+
+ /**
+ * The index of the home street of the postal address.
+ */
+ private static final int PR_HOME_ADDRESS_STREET = 35;
+
+ /**
+ * The indexes in {@link #MAPI_MAILUSER_PROP_IDS} of the property IDs which
+ * are to be represented in <tt>SourceContact</tt> as
+ * <tt>ContactDetail</tt>s.
+ */
+ private static final int[] CONTACT_DETAIL_PROP_INDEXES
+ = 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,
+ PR_HOME2_TELEPHONE_NUMBER,
+ PR_MOBILE_TELEPHONE_NUMBER,
+ PR_OTHER_TELEPHONE_NUMBER,
+ dispidEmail1EmailAddress,
+ dispidEmail2EmailAddress,
+ dispidEmail3EmailAddress,
+ PR_COMPANY_NAME,
+ dispidFax1EmailAddress,
+ dispidFax2EmailAddress,
+ dispidFax3EmailAddress,
+ PR_NICKNAME,
+ PR_DISPLAY_NAME_PREFIX,
+ PR_PERSONAL_HOME_PAGE,
+ PR_BUSINESS_HOME_PAGE,
+ PR_TITLE,
+ dispidInstMsg,
+ PR_BUSINESS_ADDRESS_CITY,
+ PR_BUSINESS_ADDRESS_COUNTRY,
+ PR_BUSINESS_ADDRESS_POSTAL_CODE,
+ PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE,
+ PR_BUSINESS_ADDRESS_STREET,
+ PR_HOME_ADDRESS_CITY,
+ PR_HOME_ADDRESS_COUNTRY,
+ PR_HOME_ADDRESS_POSTAL_CODE,
+ PR_HOME_ADDRESS_STATE_OR_PROVINCE,
+ PR_HOME_ADDRESS_STREET
+ };
+
+ /**
+ * The indexes in {@link #MAPI_MAILUSER_PROP_IDS} of the property IDs which
+ * represent an identifier which can be used for telephony or persistent
+ * presence.
+ */
+ private static final int[] CONTACT_OPERATION_SET_ABLE_PROP_INDEXES
+ = new int[]
+ {
+ PR_EMAIL_ADDRESS,
+ PR_BUSINESS_TELEPHONE_NUMBER,
+ PR_BUSINESS2_TELEPHONE_NUMBER,
+ PR_HOME_TELEPHONE_NUMBER,
+ PR_HOME2_TELEPHONE_NUMBER,
+ PR_MOBILE_TELEPHONE_NUMBER,
+ PR_OTHER_TELEPHONE_NUMBER,
+ dispidEmail1EmailAddress,
+ dispidEmail2EmailAddress,
+ dispidEmail3EmailAddress,
+ dispidFax1EmailAddress,
+ dispidFax2EmailAddress,
+ dispidFax3EmailAddress,
+ dispidInstMsg
+ };
+
+ static
+ {
+ System.loadLibrary("jmsoutlookaddrbook");
+ }
+
+ /**
+ * The number of <tt>SourceContact</tt>s matching this <tt>ContactQuery</tt>
+ * which have been retrieved from Contacts folders. Since each one of them
+ * may appear multiple times in the Address Book as well, no matching in the
+ * Address Book will be performed if there is at least one matching
+ * <tt>SourceContact</tt> in a Contacts folder.
+ */
+ private int mapiMessageCount;
+
+ /**
+ * Initializes a new <tt>MsOutlookAddrBookContactQuery</tt> instance to
+ * be performed by a specific
+ * <tt>MsOutlookAddrBookContactSourceService</tt>.
+ *
+ * @param msoabcss the <tt>MsOutlookAddrBookContactSourceService</tt>
+ * which is to perform the new <tt>ContactQuery</tt>
+ * @param query the <tt>Pattern</tt> for which <tt>msoabcss</tt> is being
+ * queried
+ */
+ public MsOutlookAddrBookContactQuery(
+ MsOutlookAddrBookContactSourceService msoabcss,
+ Pattern query)
+ {
+ super(msoabcss, query);
+ }
+
+ /**
+ * Calls back to a specific <tt>PtrCallback</tt> for each
+ * <tt>MAPI_MAILUSER</tt> found in the Address Book of Microsoft Outlook
+ * which matches a specific <tt>String</tt> query.
+ *
+ * @param query the <tt>String</tt> for which the Address Book of Microsoft
+ * Outlook is to be queried. <b>Warning</b>: Ignored at the time of this
+ * writing.
+ * @param callback the <tt>PtrOutlookContactCallback</tt> to be notified
+ * about the matching <tt>MAPI_MAILUSER</tt>s
+ */
+ public static native void foreachMailUser(
+ String query,
+ PtrOutlookContactCallback callback);
+
+ private static 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:
+ case PR_TITLE:
+ return ContactDetail.Category.Organization;
+ case dispidEmail1EmailAddress:
+ case dispidEmail2EmailAddress:
+ case dispidEmail3EmailAddress:
+ case PR_EMAIL_ADDRESS:
+ return ContactDetail.Category.Email;
+ case PR_BUSINESS2_TELEPHONE_NUMBER:
+ case PR_BUSINESS_TELEPHONE_NUMBER:
+ case PR_HOME2_TELEPHONE_NUMBER:
+ case PR_HOME_TELEPHONE_NUMBER:
+ case PR_MOBILE_TELEPHONE_NUMBER:
+ case PR_OTHER_TELEPHONE_NUMBER:
+ case dispidFax1EmailAddress:
+ case dispidFax2EmailAddress:
+ case dispidFax3EmailAddress:
+ return ContactDetail.Category.Phone;
+ case dispidInstMsg:
+ return ContactDetail.Category.InstantMessaging;
+ case PR_BUSINESS_ADDRESS_CITY:
+ case PR_BUSINESS_ADDRESS_COUNTRY:
+ case PR_BUSINESS_ADDRESS_POSTAL_CODE:
+ case PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE:
+ case PR_BUSINESS_ADDRESS_STREET:
+ case PR_HOME_ADDRESS_CITY:
+ case PR_HOME_ADDRESS_COUNTRY:
+ case PR_HOME_ADDRESS_POSTAL_CODE:
+ case PR_HOME_ADDRESS_STATE_OR_PROVINCE:
+ case PR_HOME_ADDRESS_STREET:
+ return ContactDetail.Category.Address;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * Gets the set of <tt>ContactDetail</tt> labels to be assigned to a
+ * property specified by its index in {@link #MAPI_MAILUSER_PROP_IDS}.
+ *
+ * @param propIndex the index in <tt>MAPI_MAILUSER_PROP_IDS</tt> of the
+ * property to get the <tt>ContactDetail</tt> labels of
+ * @return the set of <tt>ContactDetail</tt> labels to be assigned to the
+ * property specified by its index in <tt>MAPI_MAILUSER_PROP_IDS</tt>
+ */
+ private static ContactDetail.SubCategory[] getSubCategories(int propIndex)
+ {
+ switch (propIndex)
+ {
+ case PR_GIVEN_NAME:
+ case PR_MIDDLE_NAME:
+ case PR_COMPANY_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_BUSINESS2_TELEPHONE_NUMBER:
+ case PR_BUSINESS_TELEPHONE_NUMBER:
+ case dispidEmail2EmailAddress:
+ case PR_EMAIL_ADDRESS:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Work
+ };
+ case PR_HOME2_TELEPHONE_NUMBER:
+ case PR_HOME_TELEPHONE_NUMBER:
+ case dispidEmail1EmailAddress:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Home
+ };
+ case PR_MOBILE_TELEPHONE_NUMBER:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Mobile
+ };
+ case PR_OTHER_TELEPHONE_NUMBER:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Other
+ };
+ case dispidFax1EmailAddress:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Fax,
+ };
+ case dispidEmail3EmailAddress:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Other
+ };
+ case PR_TITLE:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.JobTitle
+ };
+ case PR_BUSINESS_ADDRESS_CITY:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Work,
+ ContactDetail.SubCategory.City
+ };
+ case PR_BUSINESS_ADDRESS_COUNTRY:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Work,
+ ContactDetail.SubCategory.Country
+ };
+ case PR_BUSINESS_ADDRESS_POSTAL_CODE:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Work,
+ ContactDetail.SubCategory.PostalCode
+ };
+ case PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Work,
+ ContactDetail.SubCategory.State
+ };
+ case PR_BUSINESS_ADDRESS_STREET:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Work,
+ ContactDetail.SubCategory.Street
+ };
+ case PR_HOME_ADDRESS_CITY:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Home,
+ ContactDetail.SubCategory.City
+ };
+ case PR_HOME_ADDRESS_COUNTRY:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Home,
+ ContactDetail.SubCategory.Country
+ };
+ case PR_HOME_ADDRESS_POSTAL_CODE:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Home,
+ ContactDetail.SubCategory.PostalCode
+ };
+ case PR_HOME_ADDRESS_STATE_OR_PROVINCE:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Home,
+ ContactDetail.SubCategory.State
+ };
+ case PR_HOME_ADDRESS_STREET:
+ return
+ new ContactDetail.SubCategory[]
+ {
+ ContactDetail.SubCategory.Home,
+ ContactDetail.SubCategory.Street
+ };
+ 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];
+ 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.Name))
+ return MAPI_MAILUSER_PROP_IDS[PR_COMPANY_NAME];
+ else if(subCategories.contains(ContactDetail.SubCategory.JobTitle))
+ return MAPI_MAILUSER_PROP_IDS[PR_TITLE];
+ else
+ return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_HOME_PAGE];
+ case Email:
+ if(subCategories.contains(ContactDetail.SubCategory.Work))
+ return MAPI_MAILUSER_PROP_IDS[dispidEmail2EmailAddress];
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Home))
+ return MAPI_MAILUSER_PROP_IDS[dispidEmail1EmailAddress];
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Other))
+ return MAPI_MAILUSER_PROP_IDS[dispidEmail3EmailAddress];
+ break;
+ case Phone:
+ if(subCategories.contains(ContactDetail.SubCategory.Fax))
+ return MAPI_MAILUSER_PROP_IDS[dispidFax1EmailAddress];
+ else if(subCategories.contains(ContactDetail.SubCategory.Work))
+ return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_TELEPHONE_NUMBER];
+ else if(subCategories.contains(ContactDetail.SubCategory.Home))
+ return MAPI_MAILUSER_PROP_IDS[PR_HOME_TELEPHONE_NUMBER];
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Mobile))
+ return MAPI_MAILUSER_PROP_IDS[PR_MOBILE_TELEPHONE_NUMBER];
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Other))
+ return MAPI_MAILUSER_PROP_IDS[PR_OTHER_TELEPHONE_NUMBER];
+ break;
+ case InstantMessaging:
+ return MAPI_MAILUSER_PROP_IDS[dispidInstMsg];
+ case Address:
+ if(subCategories.contains(ContactDetail.SubCategory.Work))
+ {
+ if(subCategories.contains(ContactDetail.SubCategory.City))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_ADDRESS_CITY];
+ }
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Country))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_ADDRESS_COUNTRY];
+ }
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.PostalCode))
+ {
+ return MAPI_MAILUSER_PROP_IDS[
+ PR_BUSINESS_ADDRESS_POSTAL_CODE];
+ }
+ else if(subCategories.contains(ContactDetail.SubCategory.State))
+ {
+ return MAPI_MAILUSER_PROP_IDS[
+ PR_BUSINESS_ADDRESS_STATE_OR_PROVINCE];
+ }
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Street))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_BUSINESS_ADDRESS_STREET];
+ }
+ }
+ else if(subCategories.contains(ContactDetail.SubCategory.Home))
+ {
+ if(subCategories.contains(ContactDetail.SubCategory.City))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_HOME_ADDRESS_CITY];
+ }
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Country))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_HOME_ADDRESS_COUNTRY];
+ }
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.PostalCode))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_HOME_ADDRESS_POSTAL_CODE];
+ }
+ else if(subCategories.contains(ContactDetail.SubCategory.State))
+ {
+ return MAPI_MAILUSER_PROP_IDS[
+ PR_HOME_ADDRESS_STATE_OR_PROVINCE];
+ }
+ else if(subCategories.contains(
+ ContactDetail.SubCategory.Street))
+ {
+ return MAPI_MAILUSER_PROP_IDS[PR_HOME_ADDRESS_STREET];
+ }
+ }
+ }
+ return -1;
+ }
+
+ public static native Object[] IMAPIProp_GetProps(
+ String entryId,
+ 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);
+
+ /**
+ * Removes a contact from the address book.
+ *
+ * @param id the person id.
+ *
+ * @return whether the contact was successfully removed.
+ */
+ public static native boolean deleteContact(String id);
+
+ /**
+ * Creates an empty contact from the address book.
+ *
+ * @return The id of the new contact created. Or NULL if the ceration
+ * failed.
+ */
+ public static native String createContact();
+
+ /**
+ * Determines whether a specific index in {@link #MAPI_MAILUSER_PROP_IDS}
+ * stands for a property with a phone number value.
+ *
+ * @param propIndex the index in <tt>MAPI_MAILUSER_PROP_IDS</tt> of the
+ * property to check
+ * @return <tt>true</tt> if <tt>propIndex</tt> stands for a property with a
+ * phone number value; otherwise, <tt>false</tt>
+ */
+ private static boolean isPhoneNumber(int propIndex)
+ {
+ switch (propIndex)
+ {
+ case PR_BUSINESS2_TELEPHONE_NUMBER:
+ case PR_BUSINESS_TELEPHONE_NUMBER:
+ case PR_HOME2_TELEPHONE_NUMBER:
+ case PR_HOME_TELEPHONE_NUMBER:
+ case PR_MOBILE_TELEPHONE_NUMBER:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Determines whether a specific <tt>MAPI_MAILUSER</tt> property with a
+ * specific <tt>value</tt> matches the {@link #query} of this
+ * <tt>AsyncContactQuery</tt>.
+ *
+ * @param property the <tt>MAPI_MAILUSER</tt> property to check
+ * @param value the value of the <tt>property</tt> to check
+ * @return <tt>true</tt> if the specified <tt>value</tt> of the specified
+ * <tt>property</tt> matches the <tt>query</tt> of this
+ * <tt>AsyncContactQuery</tt>; otherwise, <tt>false</tt>
+ */
+ private boolean matches(int property, String value)
+ {
+ return
+ query.matcher(value).find()
+ || (isPhoneNumber(property) && phoneNumberMatches(value));
+ }
+
+ /**
+ * Notifies this <tt>MsOutlookAddrBookContactQuery</tt> about a specific
+ * <tt>MAPI_MAILUSER</tt>.
+ *
+ * @param id The outlook contact identifier.
+ *
+ * @return <tt>true</tt> if this <tt>MsOutlookAddrBookContactQuery</tt>
+ * is to continue being called; otherwise, <tt>false</tt>
+ * @throws MsOutlookMAPIHResultException if anything goes wrong while
+ * getting the properties of the specified <tt>MAPI_MAILUSER</tt>
+ */
+ private boolean onMailUser(String id)
+ throws MsOutlookMAPIHResultException
+ {
+ Object[] props
+ = IMAPIProp_GetProps(
+ id,
+ MAPI_MAILUSER_PROP_IDS,
+ MAPI_UNICODE);
+ long objType
+ = (props[PR_OBJECT_TYPE] instanceof Long)
+ ? ((Long) props[PR_OBJECT_TYPE]).longValue()
+ : 0;
+
+ // If we have results from the Contacts folder(s), don't read from the
+ // Address Book because there may be duplicates.
+ if ((MAPI_MAILUSER == objType) && (mapiMessageCount != 0))
+ return false;
+
+ int propIndex = 0;
+ boolean matches = false;
+
+ for (Object prop : props)
+ {
+ if ((prop instanceof String) && matches(propIndex, (String) prop))
+ {
+ matches = true;
+ break;
+ }
+ propIndex++;
+ }
+ if (matches)
+ {
+ List<ContactDetail> contactDetails = getContactDetails(props);
+
+ // What's the point of showing a contact who has no contact details?
+ if (!contactDetails.isEmpty())
+ {
+ String displayName = getDisplayName(props);
+
+ MsOutlookAddrBookSourceContact sourceContact
+ = new MsOutlookAddrBookSourceContact(
+ getContactSource(),
+ (String) props[PR_ORIGINAL_ENTRYID],
+ displayName,
+ contactDetails);
+
+ if (MAPI_MESSAGE == objType)
+ {
+ ++mapiMessageCount;
+
+ try
+ {
+ Object[] images
+ = IMAPIProp_GetProps(
+ id,
+ new long[] { PR_ATTACHMENT_CONTACTPHOTO },
+ 0);
+ Object image = images[0];
+
+ if (image instanceof byte[])
+ sourceContact.setImage((byte[]) image);
+ }
+ catch (MsOutlookMAPIHResultException ex)
+ {
+ // Ignore it, the image isn't as vital as the
+ // SourceContact.
+ }
+ }
+
+ addQueryResult(sourceContact);
+ }
+ }
+ 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>
+ */
+ public static 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]);
+
+ // Check if this contact detail support the telephony and
+ // the persistent presence operation set.
+ for(int j = 0;
+ j < CONTACT_OPERATION_SET_ABLE_PROP_INDEXES.length;
+ ++j)
+ {
+ if(property
+ == CONTACT_OPERATION_SET_ABLE_PROP_INDEXES[j])
+ {
+ contactDetail.setSupportedOpSets(supportedOpSets);
+ // Found, then break the loop.
+ j = CONTACT_OPERATION_SET_ABLE_PROP_INDEXES.length;
+ }
+ }
+ contactDetails.add(contactDetail);
+ }
+ }
+ }
+
+ return contactDetails;
+ }
+
+ /**
+ * Performs this <tt>AsyncContactQuery</tt> in a background <tt>Thread</tt>.
+ *
+ * @see AsyncContactQuery#run()
+ */
+ protected void run()
+ {
+ synchronized (MsOutlookAddrBookContactQuery.class)
+ {
+ foreachMailUser(
+ query.toString(),
+ new PtrOutlookContactCallback());
+ }
+ }
+
+ /**
+ * Callback method when receiving notifications for inserted items.
+ *
+ * @param id The outlook contact identifier.
+ */
+ public void inserted(String id)
+ {
+ try
+ {
+ onMailUser(id);
+ }
+ catch (MsOutlookMAPIHResultException e)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ MsOutlookAddrBookContactQuery.class.getSimpleName()
+ + "#onMailUser(String)",
+ e);
+ }
+ }
+ }
+
+ /**
+ * Callback method when receiving notifications for updated items.
+ *
+ * @param id The outlook contact identifier.
+ */
+ public void updated(String id)
+ {
+ SourceContact sourceContact
+ = findSourceContactByID(id);
+ if(sourceContact != null
+ && sourceContact instanceof MsOutlookAddrBookSourceContact)
+ {
+ ((MsOutlookAddrBookSourceContact) sourceContact).updated();
+ 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);
+ }
+ }
+ }
+
+ /**
+ * Callback to called by the native outlook part with a contact id as
+ * argument.
+ */
+ public class PtrOutlookContactCallback
+ {
+ /**
+ * Notifies this callback about a specific contact.
+ *
+ * @param id The outlook contact identifier.
+ *
+ * @return <tt>true</tt> if this <tt>PtrCallback</tt> is to continue
+ * being called; otherwise, <tt>false</tt>
+ */
+ boolean callback(String id)
+ {
+ try
+ {
+ return onMailUser(id);
+ }
+ catch (MsOutlookMAPIHResultException e)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ MsOutlookAddrBookContactQuery.class.getSimpleName()
+ + "#onMailUser(String)",
+ e);
+ }
+ return false;
+ }
+ }
+ }
+
+ /**
+ * Adds a new empty contact, which will be filled in later.
+ *
+ * @param id The ID of the contact to add.
+ */
+ public void addEmptyContact(String id)
+ {
+ if(id != null)
+ {
+ final MsOutlookAddrBookSourceContact sourceContact
+ = new MsOutlookAddrBookSourceContact(
+ getContactSource(),
+ id,
+ null,
+ new LinkedList<ContactDetail>());
+ addQueryResult(sourceContact);
+ }
+ }
+
+ /**
+ * Gets the <tt>displayName</tt> to be set on a <tt>SourceContact</tt>.
+ *
+ * @param values the values of the contact properties.
+ *
+ * @return the <tt>displayName</tt> to be set on a <tt>SourceContact</tt>.
+ */
+ static String getDisplayName(Object[] values)
+ {
+ String displayName = (String) values[PR_NICKNAME];
+
+ if ((displayName == null) || (displayName.length() == 0))
+ {
+ String firstName = (String) values[PR_GIVEN_NAME];
+ String lastName = (String) values[PR_SURNAME];
+ if ((lastName == null) || (lastName.length() == 0))
+ lastName = (String) values[PR_MIDDLE_NAME];
+
+ if ((firstName == null) || (firstName.length() == 0))
+ displayName = lastName;
+ else
+ {
+ displayName = firstName;
+ if ((lastName != null) && (lastName.length() != 0))
+ displayName += " " + lastName;
+ }
+ }
+
+ if ((displayName == null) || (displayName.length() == 0))
+ displayName = (String) values[PR_COMPANY_NAME];
+
+ if ((displayName == null) || (displayName.length() == 0))
+ {
+ for(int i = 0; i < values.length; ++i)
+ {
+ if(values[i] instanceof String)
+ {
+ displayName = (String) values[i];
+ if ((displayName != null) && (displayName.length() != 0))
+ return displayName;
+ }
+ }
+ }
+
+ return displayName;
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactSourceService.java b/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactSourceService.java
index cc6abe1..85f6d6f 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactSourceService.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookContactSourceService.java
@@ -1,222 +1,265 @@
-/*
- * 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 java.util.regex.*;
-
-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.
- */
- public static final String OUTLOOK_ADDR_BOOK_PREFIX
- = "net.java.sip.communicator.plugin.addrbook.OUTLOOK_ADDR_BOOK_PREFIX";
-
- private static final long MAPI_INIT_VERSION = 0;
-
- private static final long MAPI_MULTITHREAD_NOTIFICATIONS = 0x00000001;
-
- /**
- * The latest query created.
- */
- private MsOutlookAddrBookContactQuery latestQuery = null;
-
- static
- {
- try
- {
- System.loadLibrary("jmsoutlookaddrbook");
- }
- catch (Throwable ex)
- {
- logger.error("Unable to load outlook native lib", ex);
- throw new RuntimeException(ex);
- }
-
- /*
- * We have multiple reports of an "UnsatisfiedLinkError: no
- * jmsoutlookaddrbook in java.library.path" at
- * MsOutlookAddrBookContactSourceService#queryContactSource() which
- * seems strange since getting there means that we have already
- * successfully gone through the System.loadLibrary() above. Try to load
- * MsOutlookAddrBookContactQuery here and see how it goes.
- */
- try
- {
- Class.forName(MsOutlookAddrBookContactQuery.class.getName());
- }
- catch (ClassNotFoundException cnfe)
- {
- throw new RuntimeException(cnfe);
- }
- }
-
- /**
- * Initializes a new <tt>MsOutlookAddrBookContactSourceService</tt>
- * instance.
- *
- * @throws MsOutlookMAPIHResultException if anything goes wrong while
- * initializing the new <tt>MsOutlookAddrBookContactSourceService</tt>
- * instance
- */
- public MsOutlookAddrBookContactSourceService()
- throws MsOutlookMAPIHResultException
- {
- MAPIInitialize(
- MAPI_INIT_VERSION,
- MAPI_MULTITHREAD_NOTIFICATIONS,
- new NotificationsDelegate());
- }
-
- /**
- * Gets a human-readable <tt>String</tt> which names this
- * <tt>ContactSourceService</tt> implementation.
- *
- * @return a human-readable <tt>String</tt> which names this
- * <tt>ContactSourceService</tt> implementation
- * @see ContactSourceService#getDisplayName()
- */
- public String getDisplayName()
- {
- return "Microsoft Outlook";
- }
-
- /**
- * Gets a <tt>String</tt> which uniquely identifies the instances of the
- * <tt>MsOutlookAddrBookContactSourceService</tt> implementation.
- *
- * @return a <tt>String</tt> which uniquely identifies the instances of the
- * <tt>MsOutlookAddrBookContactSourceService</tt> implementation
- * @see ContactSourceService#getType()
- */
- public int getType()
- {
- return SEARCH_TYPE;
- }
-
- private static native void MAPIInitialize(
- long version,
- long flags,
- NotificationsDelegate callback)
- throws MsOutlookMAPIHResultException;
-
- private static native void MAPIUninitialize();
-
- /**
- * Queries this <tt>ContactSourceService</tt> for <tt>SourceContact</tt>s
- * which match a specific <tt>query</tt> <tt>Pattern</tt>.
- *
- * @param query the <tt>Pattern</tt> which this
- * <tt>ContactSourceService</tt> is being queried for
- * @return a <tt>ContactQuery</tt> which represents the query of this
- * <tt>ContactSourceService</tt> implementation for the specified
- * <tt>Pattern</tt> and via which the matching <tt>SourceContact</tt>s (if
- * any) will be returned
- * @see ExtendedContactSourceService#queryContactSource(Pattern)
- */
- public ContactQuery queryContactSource(Pattern query)
- {
- if(latestQuery != null)
- latestQuery.clear();
-
- latestQuery = new MsOutlookAddrBookContactQuery(this, query);
-
- latestQuery.start();
- return latestQuery;
- }
-
- /**
- * Stops this <tt>ContactSourceService</tt> implementation and prepares it
- * for garbage collection.
- *
- * @see AsyncContactSourceService#stop()
- */
- public void stop()
- {
- if(latestQuery != null)
- {
- latestQuery.clear();
- latestQuery = null;
- }
- MAPIUninitialize();
- }
-
- /**
- * Returns the global phone number prefix to be used when calling contacts
- * from this contact source.
- *
- * @return the global phone number prefix
- */
- public String getPhoneNumberPrefix()
- {
- return AddrBookActivator.getConfigService()
- .getString(OUTLOOK_ADDR_BOOK_PREFIX);
- }
-
- /**
- * Returns the index of the contact source in the result list.
- *
- * @return the index of the contact source in the result list
- */
- public int getIndex()
- {
- return -1;
- }
-
- /**
- * Delegate class to be notified for addressbook changes.
- */
- public class NotificationsDelegate
- {
- /**
- * 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);
- }
- }
-}
+/*
+ * 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 java.util.regex.*;
+
+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
+ implements EditableContactSourceService
+{
+ /**
+ * 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.
+ */
+ public static final String OUTLOOK_ADDR_BOOK_PREFIX
+ = "net.java.sip.communicator.plugin.addrbook.OUTLOOK_ADDR_BOOK_PREFIX";
+
+ private static final long MAPI_INIT_VERSION = 0;
+
+ private static final long MAPI_MULTITHREAD_NOTIFICATIONS = 0x00000001;
+
+ /**
+ * The latest query created.
+ */
+ private MsOutlookAddrBookContactQuery latestQuery = null;
+
+ static
+ {
+ try
+ {
+ System.loadLibrary("jmsoutlookaddrbook");
+ }
+ catch (Throwable ex)
+ {
+ logger.error("Unable to load outlook native lib", ex);
+ throw new RuntimeException(ex);
+ }
+
+ /*
+ * We have multiple reports of an "UnsatisfiedLinkError: no
+ * jmsoutlookaddrbook in java.library.path" at
+ * MsOutlookAddrBookContactSourceService#queryContactSource() which
+ * seems strange since getting there means that we have already
+ * successfully gone through the System.loadLibrary() above. Try to load
+ * MsOutlookAddrBookContactQuery here and see how it goes.
+ */
+ try
+ {
+ Class.forName(MsOutlookAddrBookContactQuery.class.getName());
+ }
+ catch (ClassNotFoundException cnfe)
+ {
+ throw new RuntimeException(cnfe);
+ }
+ }
+
+ /**
+ * Initializes a new <tt>MsOutlookAddrBookContactSourceService</tt>
+ * instance.
+ *
+ * @throws MsOutlookMAPIHResultException if anything goes wrong while
+ * initializing the new <tt>MsOutlookAddrBookContactSourceService</tt>
+ * instance
+ */
+ public MsOutlookAddrBookContactSourceService()
+ throws MsOutlookMAPIHResultException
+ {
+ MAPIInitialize(
+ MAPI_INIT_VERSION,
+ MAPI_MULTITHREAD_NOTIFICATIONS,
+ new NotificationsDelegate());
+ }
+
+ /**
+ * Gets a human-readable <tt>String</tt> which names this
+ * <tt>ContactSourceService</tt> implementation.
+ *
+ * @return a human-readable <tt>String</tt> which names this
+ * <tt>ContactSourceService</tt> implementation
+ * @see ContactSourceService#getDisplayName()
+ */
+ public String getDisplayName()
+ {
+ return "Microsoft Outlook";
+ }
+
+ /**
+ * Gets a <tt>String</tt> which uniquely identifies the instances of the
+ * <tt>MsOutlookAddrBookContactSourceService</tt> implementation.
+ *
+ * @return a <tt>String</tt> which uniquely identifies the instances of the
+ * <tt>MsOutlookAddrBookContactSourceService</tt> implementation
+ * @see ContactSourceService#getType()
+ */
+ public int getType()
+ {
+ return SEARCH_TYPE;
+ }
+
+ private static native void MAPIInitialize(
+ long version,
+ long flags,
+ NotificationsDelegate callback)
+ throws MsOutlookMAPIHResultException;
+
+ private static native void MAPIUninitialize();
+
+ /**
+ * Queries this <tt>ContactSourceService</tt> for <tt>SourceContact</tt>s
+ * which match a specific <tt>query</tt> <tt>Pattern</tt>.
+ *
+ * @param query the <tt>Pattern</tt> which this
+ * <tt>ContactSourceService</tt> is being queried for
+ * @return a <tt>ContactQuery</tt> which represents the query of this
+ * <tt>ContactSourceService</tt> implementation for the specified
+ * <tt>Pattern</tt> and via which the matching <tt>SourceContact</tt>s (if
+ * any) will be returned
+ * @see ExtendedContactSourceService#queryContactSource(Pattern)
+ */
+ public ContactQuery queryContactSource(Pattern query)
+ {
+ if(latestQuery != null)
+ latestQuery.clear();
+
+ latestQuery = new MsOutlookAddrBookContactQuery(this, query);
+
+ latestQuery.start();
+ return latestQuery;
+ }
+
+ /**
+ * Stops this <tt>ContactSourceService</tt> implementation and prepares it
+ * for garbage collection.
+ *
+ * @see AsyncContactSourceService#stop()
+ */
+ public void stop()
+ {
+ if(latestQuery != null)
+ {
+ latestQuery.clear();
+ latestQuery = null;
+ }
+ MAPIUninitialize();
+ }
+
+ /**
+ * Returns the global phone number prefix to be used when calling contacts
+ * from this contact source.
+ *
+ * @return the global phone number prefix
+ */
+ public String getPhoneNumberPrefix()
+ {
+ return AddrBookActivator.getConfigService()
+ .getString(OUTLOOK_ADDR_BOOK_PREFIX);
+ }
+
+ /**
+ * Returns the index of the contact source in the result list.
+ *
+ * @return the index of the contact source in the result list
+ */
+ public int getIndex()
+ {
+ return -1;
+ }
+
+ /**
+ * Delegate class to be notified for addressbook changes.
+ */
+ public class NotificationsDelegate
+ {
+ /**
+ * Callback method when receiving notifications for inserted items.
+ */
+ public void inserted(String id)
+ {
+ if(latestQuery != null)
+ latestQuery.inserted(id);
+ }
+
+ /**
+ * Callback method when receiving notifications for updated items.
+ */
+ public void updated(String id)
+ {
+ if(latestQuery != null)
+ latestQuery.updated(id);
+ }
+
+ /**
+ * Callback method when receiving notifications for deleted items.
+ */
+ public void deleted(String id)
+ {
+ if(latestQuery != null)
+ latestQuery.deleted(id);
+ }
+ }
+
+ /**
+ * Creates a new contact from the database (i.e "contacts" or
+ * "msoutlook", etc.).
+ *
+ * @return The ID of the contact to remove. NULL if failed to create a new
+ * contact.
+ */
+ public String createContact()
+ {
+ return MsOutlookAddrBookContactQuery.createContact();
+ }
+
+ /**
+ * Adds a new empty contact, which will be filled in later.
+ *
+ * @param id The ID of the contact to add.
+ */
+ public void addEmptyContact(String id)
+ {
+ if(id != null && latestQuery != null)
+ {
+ latestQuery.addEmptyContact(id);
+ }
+ }
+
+ /**
+ * Removes the given contact from the database (i.e "contacts" or
+ * "msoutlook", etc.).
+ *
+ * @param id The ID of the contact to remove.
+ */
+ public void deleteContact(String id)
+ {
+ if(id != null && MsOutlookAddrBookContactQuery.deleteContact(id))
+ {
+ if(latestQuery != null)
+ {
+ latestQuery.deleted(id);
+ }
+ }
+ }
+}
diff --git a/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookSourceContact.java b/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookSourceContact.java
index 6b132f3..4158e31 100644
--- a/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookSourceContact.java
+++ b/src/net/java/sip/communicator/plugin/addrbook/msoutlook/MsOutlookAddrBookSourceContact.java
@@ -9,6 +9,7 @@ package net.java.sip.communicator.plugin.addrbook.msoutlook;
import java.util.*;
import net.java.sip.communicator.service.contactsource.*;
+import net.java.sip.communicator.util.*;
/**
* Implements a custom <tt>SourceContact</tt> for the Address Book of Microsoft
@@ -21,6 +22,21 @@ public class MsOutlookAddrBookSourceContact
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);
+
+ /**
+ * Boolean used to temporarily lock the access to a single modification
+ * source (jitsi or contact). i.e. it can be useful if Jitsi modifies a
+ * batch of details and do not want to receive contact update notification
+ * which can produce concurrent changes of the details.
+ */
+ private Boolean locked = Boolean.FALSE;
+
+ /**
* Initializes a new MsOutlookAddrBookSourceContact instance.
*
* @param contactSource The ContactSourceService which is creating the new
@@ -58,7 +74,7 @@ public class MsOutlookAddrBookSourceContact
*/
public void setDetails(List<ContactDetail> details)
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
contactDetails.clear();
contactDetails.addAll(details);
@@ -71,7 +87,7 @@ public class MsOutlookAddrBookSourceContact
*/
public void save()
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
MsOutlookAddrBookContactDetail outlookContactDetail;
@@ -101,7 +117,7 @@ public class MsOutlookAddrBookSourceContact
*/
public void removeContactDetail(ContactDetail detail)
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
int i = 0;
while(i < this.contactDetails.size())
@@ -145,7 +161,7 @@ public class MsOutlookAddrBookSourceContact
*/
public void addContactDetail(ContactDetail detail)
{
- synchronized(this.contactDetails)
+ synchronized(this)
{
MsOutlookAddrBookContactDetail addDetail;
if(!(detail instanceof MsOutlookAddrBookContactDetail))
@@ -185,6 +201,125 @@ public class MsOutlookAddrBookSourceContact
}
/**
+ * Sets the display name for this contact.
+ *
+ * @param displayName The new display name for this contact.
+ */
+ public void setDisplayName(String displayName)
+ {
+ if(displayName != null && !displayName.equals(this.getDisplayName()))
+ {
+ // Be sure that the new determined display named is saved under all
+ // the requireed properties.
+ long[] displayNamePropIdList =
+ {
+ 0x3001, // PR_DISPLAY_NAME
+ 0x0037, // PR_SUBJECT
+ 0x803F, // Do not know, but set by the MFCMAPI application.
+ 0x0E1D // PR_NORMALIZED_SUBJECT
+ };
+
+ for(int i = 0; i < displayNamePropIdList.length; ++i)
+ {
+ MsOutlookAddrBookContactQuery.IMAPIProp_SetPropString(
+ displayNamePropIdList[i],
+ displayName,
+ this.getId());
+ }
+ }
+
+ super.setDisplayName(displayName);
+ }
+
+ /**
+ * Function called by the native part (msoutlook) when this contact has been
+ * updated.
+ */
+ public void updated()
+ {
+ // Synchronize before the GetProps in order to let other operation (i.e.
+ // save) to write/change all desired values (and not override new saved
+ // values iwth old ones).
+ synchronized(this)
+ {
+ waitUnlock();
+
+ Object[] props = null;
+ try
+ {
+ props = MsOutlookAddrBookContactQuery.IMAPIProp_GetProps(
+ this.getId(),
+ MsOutlookAddrBookContactQuery.MAPI_MAILUSER_PROP_IDS,
+ MsOutlookAddrBookContactQuery.MAPI_UNICODE);
+ }
+ catch (MsOutlookMAPIHResultException e)
+ {
+ if (logger.isDebugEnabled())
+ {
+ logger.debug(
+ MsOutlookAddrBookContactQuery.class.getSimpleName()
+ + "#IMAPIProp_GetProps(long, long[], long)",
+ e);
+ }
+ }
+
+ // let's update the the details
+ List<ContactDetail> contactDetails
+ = MsOutlookAddrBookContactQuery.getContactDetails(props);
+ this.setDetails(contactDetails);
+
+ String displayName
+ = MsOutlookAddrBookContactQuery.getDisplayName(props);
+ this.setDisplayName(displayName);
+ }
+ }
+
+ /**
+ * Locks this object before adding or removing several contact details.
+ */
+ public void lock()
+ {
+ synchronized(this)
+ {
+ locked = Boolean.TRUE;
+ }
+ }
+
+ /**
+ * Unlocks this object before after or removing several contact details.
+ */
+ public void unlock()
+ {
+ synchronized(this)
+ {
+ locked = Boolean.FALSE;
+ notify();
+ }
+ }
+
+ /**
+ * Waits to be unlocked. This object must be synchronized before calling
+ * this function.
+ */
+ private void waitUnlock()
+ {
+ boolean continueToWait = this.locked;
+
+ while(continueToWait)
+ {
+ try
+ {
+ wait();
+ continueToWait = false;
+ }
+ catch(InterruptedException ie)
+ {
+ // Nothing to do, we will wait until the notify.
+ }
+ }
+ }
+
+ /**
* Returns the index of this source contact in its parent.
*
* @return the index of this source contact in its parent
diff --git a/src/net/java/sip/communicator/service/contactsource/EditableContactSourceService.java b/src/net/java/sip/communicator/service/contactsource/EditableContactSourceService.java
new file mode 100644
index 0000000..1734a40
--- /dev/null
+++ b/src/net/java/sip/communicator/service/contactsource/EditableContactSourceService.java
@@ -0,0 +1,41 @@
+/*
+ * 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.service.contactsource;
+
+/**
+ * Provides an interface to create or delete contact for a given contact source
+ * service.
+ *
+ * @author Vincent Lucas
+ */
+public interface EditableContactSourceService
+ extends ContactSourceService
+{
+ /**
+ * Creates a new contact from the database (i.e "contacts" or
+ * "msoutlook", etc.).
+ *
+ * @return The ID of the contact to remove. NULL if failed to create a new
+ * contact.
+ */
+ public String createContact();
+
+ /**
+ * Adds a new empty contact, which will be filled in later.
+ *
+ * @param id The ID of the contact to add.
+ */
+ public void addEmptyContact(String id);
+
+ /**
+ * Removes the given contact from the database (i.e "contacts" or
+ * "msoutlook", etc.).
+ *
+ * @param id The ID of the contact to remove.
+ */
+ public void deleteContact(String id);
+}
diff --git a/src/net/java/sip/communicator/service/contactsource/EditableSourceContact.java b/src/net/java/sip/communicator/service/contactsource/EditableSourceContact.java
index d10f081..5da8ff9 100644
--- a/src/net/java/sip/communicator/service/contactsource/EditableSourceContact.java
+++ b/src/net/java/sip/communicator/service/contactsource/EditableSourceContact.java
@@ -31,4 +31,15 @@ public interface EditableSourceContact
* @param detail the <tt>ContactDetail</tt> to remove
*/
public void removeContactDetail(ContactDetail detail);
+
+ /**
+ * Locks this object before adding or removing several contact details.
+ */
+ public void lock();
+
+ /**
+ * Unlocks this object before after or removing several contact details.
+ */
+ public void unlock();
+
}