diff options
Diffstat (limited to 'src')
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(); + } |