1 line
40 KiB
C++
Executable File
1 line
40 KiB
C++
Executable File
/* Copyright (c) 2017, Computer History Museum
|
|
All rights reserved.
|
|
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to
|
|
the limitations in the disclaimer below) provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
|
|
disclaimer in the documentation and/or other materials provided with the distribution.
|
|
* Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
|
|
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
|
|
DAMAGE. */
|
|
|
|
#include "osxabsync.h"
|
|
#define FILE_NUM 148
|
|
/* Copyright Qualcomm, Inc. 2003 */
|
|
|
|
#include "MachOWrapper.h"
|
|
#include "osxabsyncinc.h"
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// osxabsync.c
|
|
//
|
|
// This file provides services to slurp in all the contacts from the
|
|
// Mac OS X address book into a Eudora nicknames file. One way synching
|
|
// is supported, with two way not too far away.
|
|
//
|
|
// An interesting mix of C and C++. C++ was used in the original
|
|
// OSXAddressBookSync EMSAPI pluging. Rather than rewriting all
|
|
// of that code, I've chosen to live in a muddled world.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// OS X Address Book Type Declarations.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
/*
|
|
* ABSyncRecordField
|
|
*/
|
|
|
|
typedef struct _ABSyncRecordField
|
|
{
|
|
long m_EudoraABFieldName;
|
|
const char *m_pOSXABProperty;
|
|
const char *m_pOSXABKey;
|
|
const char *m_pOSXABLabel;
|
|
} ABSyncRecordField;
|
|
|
|
/*
|
|
* ABSyncRecord
|
|
*/
|
|
|
|
// ABSyncRecord class
|
|
class ABSyncRecord
|
|
{
|
|
private:
|
|
|
|
// are we a group?
|
|
Boolean fIsGroup;
|
|
|
|
// nickname to use for this record
|
|
Str255 fEudoraNickName;
|
|
|
|
// a table of field mapping data
|
|
const ABSyncRecordField *fFieldMappingTable;
|
|
|
|
// an array of the address book field values
|
|
CFStringRef fOSXABFieldValues[ABReservedTagsLimit];
|
|
|
|
// the nickname pulled from the AddressBook
|
|
CFStringRef fOSXABNickName;
|
|
|
|
// an array of the address book field multi values
|
|
ABMultiValueRef fOSXABFieldMultiValues[ABReservedTagsLimit];
|
|
|
|
// other email addresses
|
|
CFMutableStringRef fOtherEmailAddresses;
|
|
|
|
// initialize internals
|
|
void ABSyncRecord_Initialize(void);
|
|
|
|
// recursive group import
|
|
void ABSyncRecord_ImportGroupMembers(ABGroupRef abRecord);
|
|
|
|
|
|
public:
|
|
|
|
// constructor
|
|
ABSyncRecord();
|
|
|
|
// destructor
|
|
~ABSyncRecord();
|
|
|
|
// release internals
|
|
void ABSyncRecord_Reset(void);
|
|
|
|
// import a contact from the OS X Address Book
|
|
void ABSyncRecord_ImportContact(ABPersonRef abRecord);
|
|
|
|
// import a group from the OS X Address Book
|
|
void ABSyncRecord_ImportGroup(ABGroupRef abRecord);
|
|
|
|
// generate a Nickname from a record
|
|
unsigned char *ABSyncRecord_GenerateNickName(void);
|
|
|
|
// Generate a Eudora Address Book compatible alias line
|
|
Handle ABSyncRecord_GenerateAddressLine(void);
|
|
|
|
// Generate a Eudora Address Book compatible notes line
|
|
Handle ABSyncRecord_GenerateNotesLine(void);
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Globals
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
// MachOWrappage. Global so we only make 'em once.
|
|
|
|
// AddressBook.framework
|
|
MachOWrapper<ABAddressBookRef (*)(void)>
|
|
ABGetSharedAddressBook ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABGetSharedAddressBook" ));
|
|
|
|
MachOWrapper<CFArrayRef (*)(ABAddressBookRef)>
|
|
ABCopyArrayOfAllPeople ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABCopyArrayOfAllPeople" ));
|
|
|
|
MachOWrapper<CFArrayRef (*)(ABAddressBookRef)>
|
|
ABCopyArrayOfAllGroups ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABCopyArrayOfAllGroups" ));
|
|
|
|
MachOWrapper<CFArrayRef (*)(ABGroupRef)>
|
|
ABGroupCopyArrayOfAllMembers ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABGroupCopyArrayOfAllMembers" ));
|
|
|
|
MachOWrapper<CFArrayRef (*)(ABGroupRef)>
|
|
ABGroupCopyArrayOfAllSubgroups ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABGroupCopyArrayOfAllSubgroups" ));
|
|
|
|
MachOWrapper<ABPropertyType (*)(ABAddressBookRef, CFStringRef, CFStringRef)>
|
|
ABTypeOfProperty ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABTypeOfProperty" ));
|
|
|
|
MachOWrapper<unsigned (*)(ABMultiValueRef)>
|
|
ABMultiValueCount ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABMultiValueCount" ));
|
|
|
|
MachOWrapper<CFTypeRef (*)(ABRecordRef, CFStringRef)>
|
|
ABRecordCopyValue ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABRecordCopyValue" ));
|
|
|
|
MachOWrapper<ABMultiValueRef (*)(ABMultiValueRef)>
|
|
ABMultiValueCreateCopy ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABMultiValueCreateCopy" ));
|
|
|
|
MachOWrapper<CFTypeRef (*)(ABMultiValueRef, int)>
|
|
ABMultiValueCopyValueAtIndex ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABMultiValueCopyValueAtIndex" ));
|
|
|
|
MachOWrapper<int (*)(ABMultiValueRef, CFStringRef)>
|
|
ABMultiValueIndexForIdentifier ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABMultiValueIndexForIdentifier" ));
|
|
|
|
MachOWrapper<CFStringRef (*)(ABMultiValueRef, int)>
|
|
ABMultiValueCopyLabelAtIndex ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABMultiValueCopyLabelAtIndex" ));
|
|
|
|
MachOWrapper<CFStringRef (*)(ABMultiValueRef)>
|
|
ABMultiValueCopyPrimaryIdentifier ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABMultiValueCopyPrimaryIdentifier" ));
|
|
|
|
MachOWrapper<CFStringRef (*)(ABGroupRef, ABPersonRef, CFStringRef)>
|
|
ABGroupCopyDistributionIdentifier ( CFSTR ( "AddressBook.framework" ), CFSTR ( "ABGroupCopyDistributionIdentifier" ));
|
|
|
|
|
|
// Notifications
|
|
MachOWrapper<CFNotificationCenterRef (*)(void)>
|
|
CFNotificationCenterGetDistributedCenter ( CFSTR ( "CoreFoundation.framework" ), CFSTR ( "CFNotificationCenterGetDistributedCenter" ));
|
|
|
|
MachOWrapper<void (*)(CFNotificationCenterRef, const void *, CFNotificationCallback, CFStringRef, const void *, CFNotificationSuspensionBehavior)>
|
|
CFNotificationCenterAddObserver ( CFSTR ( "CoreFoundation.framework" ), CFSTR ( "CFNotificationCenterAddObserver" ));
|
|
|
|
MachOWrapper<void (*)(CFNotificationCenterRef center, const void *observer, CFStringRef name, const void *object)>
|
|
CFNotificationCenterRemoveObserver ( CFSTR ( "CoreFoundation.framework" ), CFSTR ( "CFNotificationCenterRemoveObserver" ));
|
|
|
|
|
|
|
|
// nickname file command strings and such
|
|
#define kEndOfLine 0x0D
|
|
#define kSpace 0x20
|
|
#define kNoEmailAddressChar ','
|
|
#define pDoubleQuoteChar "\p\""
|
|
#define pUnderbarChar "\p_"
|
|
|
|
// Table to map OSX AB contact fields to Eudora AB contact fields.
|
|
static const ABSyncRecordField gFieldMappingTable[ABReservedTagsLimit] =
|
|
{
|
|
{ abTagName, NULL, NULL, NULL }, // full name
|
|
{ abTagFirst, kABFirstNameProperty, NULL, NULL }, // first
|
|
{ abTagLast, kABLastNameProperty, NULL, NULL }, // last
|
|
{ abTagEmail, kABEmailProperty, NULL, NULL }, // email address
|
|
{ abTagAddress, kABAddressProperty, kABAddressStreetKey, kABAddressHomeLabel }, // address
|
|
{ abTagCity, kABAddressProperty, kABAddressCityKey, kABAddressHomeLabel }, // city
|
|
{ abTagState, kABAddressProperty, kABAddressStateKey, kABAddressHomeLabel }, // state
|
|
{ abTagCountry, kABAddressProperty, kABAddressCountryKey, kABAddressHomeLabel }, // country
|
|
{ abTagZip, kABAddressProperty, kABAddressZIPKey, kABAddressHomeLabel }, // zip
|
|
{ abTagPhone, kABPhoneProperty, kABPhoneProperty, kABPhoneHomeLabel }, // phone
|
|
{ abTagFax, kABPhoneProperty, kABPhoneProperty, kABPhoneHomeFAXLabel }, // fax
|
|
{ abTagMobile, kABPhoneProperty, kABPhoneProperty, kABPhoneMobileLabel }, // mobile
|
|
{ abTagPrimary, kABPhoneProperty, kABPhoneProperty, kABPhoneMainLabel }, // primary
|
|
{ abTagAIM, kABAIMInstantProperty, kABAIMInstantProperty, kABAIMHomeLabel }, // home aim
|
|
{ abTagWeb, kABHomePageProperty, NULL, NULL }, // web
|
|
{ abTagTitle, kABJobTitleProperty, NULL, NULL }, // title
|
|
{ abTagCompany, kABOrganizationProperty, NULL, NULL }, // company
|
|
{ abTagAddress2, kABAddressProperty, kABAddressStreetKey, kABAddressWorkLabel }, // address
|
|
{ abTagCity2, kABAddressProperty, kABAddressCityKey, kABAddressWorkLabel }, // city
|
|
{ abTagState2, kABAddressProperty, kABAddressStateKey, kABAddressWorkLabel }, // state
|
|
{ abTagCountry2, kABAddressProperty, kABAddressCountryKey, kABAddressWorkLabel }, // country
|
|
{ abTagZip2, kABAddressProperty, kABAddressZIPKey, kABAddressWorkLabel }, // zip
|
|
{ abTagPhone2, kABPhoneProperty, kABPhoneProperty, kABPhoneWorkLabel }, // phone
|
|
{ abTagFax2, kABPhoneProperty, kABPhoneProperty, kABPhoneWorkFAXLabel }, // fax
|
|
{ abTagMobile2, kABPhoneProperty, kABPhoneProperty, kABPhonePagerLabel }, // mobile
|
|
{ abTagAIM2, kABAIMInstantProperty, kABAIMInstantProperty, kABAIMWorkLabel }, // work aim
|
|
{ abTagWeb2, kABHomePageProperty, NULL, NULL }, // web
|
|
{ abTagOtherEmail, kABEmailProperty, NULL, NULL }, // otheremail
|
|
{ abTagOtherPhone, kABPhoneProperty, NULL, kABPhoneMainLabel }, // otherphone
|
|
{ abTagOtherWeb, NULL, NULL, NULL }, // otherweb
|
|
{ abTagNote, kABNoteProperty, NULL, NULL }, // notes,
|
|
{ abTagPicture, NULL, NULL, NULL }, // picture
|
|
{ abNickname, NULL, NULL, NULL }, // nickname
|
|
{ ABReservedTagsLimit, NULL, NULL, NULL } // EudoraABFieldNamesLimit
|
|
};
|
|
|
|
// has address book syncing been initialized?
|
|
Boolean gOSXABSyncInit = false;
|
|
|
|
// notification callback
|
|
CFNotificationCallback gABCallback = NULL;
|
|
|
|
// schedule sync
|
|
Boolean bNeedsSync = false;
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Prototypes
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
// AddressBookSync
|
|
short GetOSXAddressBookFile(void);
|
|
OSStatus DoOSXAddressBookSync(short which);
|
|
void RemoveOSXAddressBookFile(void);
|
|
|
|
// notifications
|
|
void OSXAddressBookNotificationReg(void);
|
|
void OSXABSyncNotificationCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo);
|
|
void OSXAddressBookNotificationDeReg(void);
|
|
|
|
// Utilities
|
|
short SafeStrLen(const char *s);
|
|
void ConvertLineBreaks(Str255 in);
|
|
#define HaveOS102() (GetOSVersion() >= 0x1020)
|
|
#define HaveOS103() (GetOSVersion() >= 0x1030)
|
|
void *MachOFunctionPointerForCFMFunctionPointer( void *cfmfp );
|
|
OSErr MakeEudoraABEntry(short which, Str255 nickname, Handle addresses, Handle notes);
|
|
OSErr ConvertFieldValueToPString(CFStringRef fieldValue, Str255 pValue);
|
|
|
|
// externs
|
|
extern "C"
|
|
{
|
|
void GenAliasList(void);
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// AddressBookSync
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
/************************************************************************
|
|
* OSXAddressBookSync - sync with the OS X AddressBook, if we ought.
|
|
************************************************************************/
|
|
OSStatus OSXAddressBookSync(void)
|
|
{
|
|
OSStatus err = noErr;
|
|
short which;
|
|
|
|
// we must have OS 10.2 or great to do any of this ...
|
|
if (!HaveOS102())
|
|
return (noErr);
|
|
|
|
// Does the user wish to sync with the OS X Address Book?
|
|
if (!PrefIsSet(PREF_SYNC_OSXAB))
|
|
return (noErr);
|
|
|
|
// are we in Free mode? Then forget it.
|
|
if (IsFreeMode())
|
|
return (noErr);
|
|
|
|
// register for notifications
|
|
OSXAddressBookNotificationReg();
|
|
|
|
// we're set up and ready to go ...
|
|
gOSXABSyncInit = true;
|
|
|
|
// make sure the aliases are around ...
|
|
RegenerateAllAliases(false);
|
|
|
|
// create the address book file that will hold the new addresses
|
|
which = GetOSXAddressBookFile();
|
|
if (which != -1)
|
|
{
|
|
// sync the address book with the OS X Address Book.
|
|
err = DoOSXAddressBookSync(which);
|
|
if (err == noErr)
|
|
{
|
|
// save the file
|
|
SetAliasDirty(which);
|
|
SaveIndNickFile(which, false);
|
|
}
|
|
|
|
// lock the resulting file until we implement a full, two way sync
|
|
FSpSetFLock(&((*Aliases)[which].spec));
|
|
|
|
// tell Eudora about the new address book file
|
|
GenAliasList();
|
|
RegenerateAllAliases(true);
|
|
ABTickleHardEnoughToMakeYouPuke();
|
|
}
|
|
else
|
|
err = fnfErr; // couldn't create Eudora nickname file to sync to
|
|
|
|
return (err);
|
|
}
|
|
|
|
/************************************************************************
|
|
* OSXAddressBookSyncCleanup - Cleanup after OSX AB Sync
|
|
************************************************************************/
|
|
void OSXAddressBookSyncCleanup(Boolean bRemoveNickFile)
|
|
{
|
|
if (gOSXABSyncInit)
|
|
{
|
|
OSXAddressBookNotificationDeReg();
|
|
if (bRemoveNickFile) RemoveOSXAddressBookFile();
|
|
gOSXABSyncInit = false;
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* OSXAddressBookScheduledSync - perform scheduled sync
|
|
************************************************************************/
|
|
void OSXAddressBookScheduledSync(void)
|
|
{
|
|
if (bNeedsSync)
|
|
{
|
|
bNeedsSync = false;
|
|
OSXAddressBookSync();
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* RemoveOSXAddressBookFile - remove the nickname file we created
|
|
************************************************************************/
|
|
void RemoveOSXAddressBookFile(void)
|
|
{
|
|
Str255 name;
|
|
short which;
|
|
|
|
GetRString(name, OSXAB_FILE);
|
|
for (which = 0; which < NAliases; which++)
|
|
if (StringSame((*Aliases)[which].spec.name, name))
|
|
break;
|
|
|
|
if (which < NAliases)
|
|
{
|
|
// is this nickname file locked?
|
|
if ((*Aliases)[which].ro)
|
|
{
|
|
FSpRstFLock(&((*Aliases)[which].spec));
|
|
FSpDelete(&((*Aliases)[which].spec));
|
|
GenAliasList();
|
|
RegenerateAllAliases(true);
|
|
ABTickleHardEnoughToMakeYouPuke();
|
|
}
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* GetOSXAddressBookFile - figure out where to put OS X AddressBook
|
|
* contacts. Create a new nickname file if needed.
|
|
************************************************************************/
|
|
short GetOSXAddressBookFile(void)
|
|
{
|
|
short which = -1;
|
|
AliasDesc ad;
|
|
FSSpec folderSpec;
|
|
Str255 scratch;
|
|
long dirID;
|
|
OSErr err = noErr;
|
|
|
|
Zero(ad);
|
|
|
|
// get the name of the address book file ...
|
|
GetRString(ad.spec.name, OSXAB_FILE);
|
|
|
|
// does an address book with this name already exist?
|
|
for (which = 0; which < NAliases; which++)
|
|
if (StringSame((*Aliases)[which].spec.name, ad.spec.name))
|
|
break;
|
|
|
|
if (which < NAliases)
|
|
{
|
|
// it already exists. Make sure it's unlocked and delete it.
|
|
FSpRstFLock(&((*Aliases)[which].spec));
|
|
FSpDelete(&((*Aliases)[which].spec));
|
|
RegenerateAllAliases(true);
|
|
}
|
|
|
|
// create the address book file.
|
|
|
|
// find the nicknames folder ...
|
|
if (!SubFolderSpec(NICK_FOLDER,&folderSpec))
|
|
SimpleMakeFSSpec(folderSpec.vRefNum,folderSpec.parID,ad.spec.name,&ad.spec);
|
|
else
|
|
{
|
|
SimpleMakeFSSpec(Root.vRef,Root.dirId,GetRString(scratch,NICK_FOLDER),&folderSpec);
|
|
FSpDirCreate(&folderSpec,smSystemScript,&dirID);
|
|
SubFolderSpec(NICK_FOLDER,&folderSpec);
|
|
SimpleMakeFSSpec(folderSpec.vRefNum,folderSpec.parID,ad.spec.name,&ad.spec);
|
|
}
|
|
|
|
// make a new nickname file.
|
|
err = MakeResFile(ad.spec.name,ad.spec.vRefNum,ad.spec.parID,CREATOR,'TEXT');
|
|
if (err == noErr)
|
|
{
|
|
if (PtrPlusHand((void *)(&ad),(char **)(Aliases),sizeof(ad))) err = MemError();
|
|
which = HandleCount (Aliases) - 1;
|
|
}
|
|
|
|
return (which);
|
|
}
|
|
|
|
/************************************************************************
|
|
* DoOSXAddressBookSync - do the deed
|
|
************************************************************************/
|
|
OSStatus DoOSXAddressBookSync(short which)
|
|
{
|
|
OSStatus err = noErr;
|
|
ABPersonRef curPerson;
|
|
ABGroupRef curGroup;
|
|
Handle addressH, notesH;
|
|
ABSyncRecord record;
|
|
CFIndex count;
|
|
ABAddressBookRef addressBook = ABGetSharedAddressBook(); // get the OS X Address Book
|
|
CFArrayRef people = addressBook? ABCopyArrayOfAllPeople(addressBook) : NULL; // get a list of all the people
|
|
CFArrayRef groups = addressBook? ABCopyArrayOfAllGroups(addressBook) : NULL; // get a list of all the groups
|
|
|
|
if (people)
|
|
{
|
|
// count the number of records
|
|
count = CFArrayGetCount(people);
|
|
if (count > 0)
|
|
{
|
|
// iterate over each record
|
|
for (CFIndex curRecord = 0; curRecord < count; curRecord++)
|
|
{
|
|
// locate the current person in the OS X address book
|
|
curPerson = (ABPersonRef)CFArrayGetValueAtIndex(people, curRecord);
|
|
|
|
// import contact information
|
|
record.ABSyncRecord_ImportContact(curPerson);
|
|
|
|
// convert the OS X AddressBook record into something Eudora understands
|
|
addressH = record.ABSyncRecord_GenerateAddressLine();
|
|
notesH = record.ABSyncRecord_GenerateNotesLine();
|
|
|
|
// add the OS X address book entry to the Eudora Address Book file we're creating
|
|
if (addressH && notesH)
|
|
MakeEudoraABEntry(which, record.ABSyncRecord_GenerateNickName(), addressH, notesH);
|
|
|
|
// cleanup
|
|
record.ABSyncRecord_Reset();
|
|
if (addressH) DisposeHandle(addressH);
|
|
if (notesH) DisposeHandle(notesH);
|
|
}
|
|
}
|
|
// else
|
|
// nothing to do.
|
|
|
|
CFRelease(people);
|
|
}
|
|
|
|
// count the number of groups
|
|
if (groups)
|
|
{
|
|
count = CFArrayGetCount(groups);
|
|
if (count > 0)
|
|
{
|
|
// iterate over each record
|
|
for (CFIndex curRecord = 0; curRecord < count; curRecord++)
|
|
{
|
|
// locate the current person in the OS X address book
|
|
curGroup = (ABGroupRef)CFArrayGetValueAtIndex(groups, curRecord);
|
|
|
|
// import group information
|
|
record.ABSyncRecord_ImportGroup(curGroup);
|
|
|
|
// convert the OS X AddressBook record into something Eudora understands
|
|
addressH = record.ABSyncRecord_GenerateAddressLine();
|
|
notesH = record.ABSyncRecord_GenerateNotesLine();
|
|
|
|
// add the OS X address book entry to the Eudora Address Book file we're creating
|
|
if (addressH && notesH)
|
|
MakeEudoraABEntry(which, record.ABSyncRecord_GenerateNickName(), addressH, notesH);
|
|
|
|
// cleanup
|
|
record.ABSyncRecord_Reset();
|
|
if (addressH) DisposeHandle(addressH);
|
|
if (notesH) DisposeHandle(notesH);
|
|
}
|
|
}
|
|
// else
|
|
// nothing to do.
|
|
|
|
CFRelease(groups);
|
|
}
|
|
|
|
return (noErr);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Notifications
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
/************************************************************************
|
|
* OSXAddressBookNotificationReg - register for OS X AB notifications
|
|
* requires 10.3 or later.
|
|
************************************************************************/
|
|
void OSXAddressBookNotificationReg(void)
|
|
{
|
|
if (!gOSXABSyncInit && HaveOS103())
|
|
{
|
|
gABCallback = (CFNotificationCallback)MachOFunctionPointerForCFMFunctionPointer(OSXABSyncNotificationCallback);
|
|
CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), NULL, gABCallback, CFSTR("ABDatabaseChangedNotification"), NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
|
|
}
|
|
}
|
|
|
|
/************************************************************************
|
|
* OSXABSyncNotificationCallback - notification callback
|
|
************************************************************************/
|
|
void OSXABSyncNotificationCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
|
|
{
|
|
// schedule a sync to be done next time it's convenient.
|
|
bNeedsSync = true;
|
|
}
|
|
|
|
/************************************************************************
|
|
* OSXAddressBookNotificationDeReg - deregister for OS X AB notifications
|
|
************************************************************************/
|
|
void OSXAddressBookNotificationDeReg(void)
|
|
{
|
|
if (gOSXABSyncInit && HaveOS103())
|
|
{
|
|
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDistributedCenter(), NULL, CFSTR("ABDatabaseChangedNotification"), NULL);
|
|
DisposePtr((Ptr)gABCallback);
|
|
}
|
|
}
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ABSyncRecord
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
/*************************************************************************************************
|
|
* ABSyncRecord - creator
|
|
*************************************************************************************************/
|
|
ABSyncRecord::ABSyncRecord(void)
|
|
{
|
|
this->ABSyncRecord_Initialize();
|
|
}
|
|
|
|
/*************************************************************************************************
|
|
* ~ABSyncRecord - destructor. Clean up after a sync record.
|
|
*************************************************************************************************/
|
|
ABSyncRecord::~ABSyncRecord(void)
|
|
{
|
|
this->ABSyncRecord_Reset();
|
|
}
|
|
|
|
/*************************************************************************************************
|
|
* ABSyncRecord_Initialize - initialize a sync record
|
|
*************************************************************************************************/
|
|
void ABSyncRecord::ABSyncRecord_Initialize(void)
|
|
{
|
|
fOtherEmailAddresses = NULL;
|
|
long count;
|
|
|
|
fFieldMappingTable = gFieldMappingTable;
|
|
|
|
for (count = 0; count < ABReservedTagsLimit; count++)
|
|
{
|
|
fOSXABFieldValues[count] = NULL;
|
|
fOSXABFieldMultiValues[count] = NULL;
|
|
fOSXABNickName = NULL;
|
|
}
|
|
}
|
|
|
|
/*************************************************************************************************
|
|
* ABSyncRecord_ReleaseABValues - release memory allocated during record conversion
|
|
*************************************************************************************************/
|
|
void ABSyncRecord::ABSyncRecord_Reset(void)
|
|
{
|
|
for (long count = 0; count < ABReservedTagsLimit; count++)
|
|
{
|
|
if (fOSXABFieldValues[count])
|
|
{
|
|
CFRelease(fOSXABFieldValues[count]);
|
|
fOSXABFieldValues[count] = NULL;
|
|
}
|
|
if (fOSXABFieldMultiValues[count])
|
|
{
|
|
CFRelease(fOSXABFieldMultiValues[count]);
|
|
fOSXABFieldMultiValues[count] = NULL;
|
|
}
|
|
fOSXABNickName = NULL;
|
|
fEudoraNickName[0] = 0;
|
|
}
|
|
|
|
if (fOtherEmailAddresses)
|
|
{
|
|
CFRelease(fOtherEmailAddresses);
|
|
fOtherEmailAddresses = NULL;
|
|
}
|
|
}
|
|
|
|
/*************************************************************************************************
|
|
* ABSyncRecord_ImportContact - Read in contact data from the OS X Address Book
|
|
*************************************************************************************************/
|
|
void ABSyncRecord::ABSyncRecord_ImportContact(ABPersonRef abRecord)
|
|
{
|
|
ABPropertyType curPropertyType;
|
|
int index, count;
|
|
int primaryEmailIndex;
|
|
CFStringRef recordType;
|
|
CFStringRef property;
|
|
CFStringRef label;
|
|
CFStringRef key;
|
|
CFStringRef valueCopy;
|
|
ABAddressBookRef addressBook = ABGetSharedAddressBook();
|
|
|
|
// initialize and set up
|
|
this->fIsGroup = false;
|
|
|
|
// import the values from each of the address book record fields
|
|
const ABSyncRecordField *curField = fFieldMappingTable;
|
|
while (curField->m_EudoraABFieldName != ABReservedTagsLimit)
|
|
{
|
|
recordType = CFStringCreateWithCString(NULL, kABPersonRecordType, NULL);
|
|
property = curField->m_pOSXABProperty ? CFStringCreateWithCString(NULL, curField->m_pOSXABProperty, NULL) : NULL;
|
|
label = curField->m_pOSXABLabel ? CFStringCreateWithCString(NULL, curField->m_pOSXABLabel, NULL) : NULL;
|
|
key = curField->m_pOSXABKey ? CFStringCreateWithCString(NULL, curField->m_pOSXABKey, NULL) : NULL;
|
|
|
|
// does this Eudora Address Book field have a corresponding OS X Address Book property?
|
|
if (curField->m_pOSXABProperty != NULL)
|
|
{
|
|
// figure out what type of property this is ...
|
|
curPropertyType = ABTypeOfProperty(addressBook, recordType, property);
|
|
|
|
// Is this a multi value property?
|
|
if ((curPropertyType & kABMultiValueMask) != NULL)
|
|
{
|
|
// find the value of this property ...
|
|
valueCopy = (CFStringRef)ABRecordCopyValue(abRecord, property);
|
|
if (valueCopy)
|
|
{
|
|
fOSXABFieldMultiValues[curField->m_EudoraABFieldName] = ABMultiValueCreateCopy((ABMultiValueRef)valueCopy);
|
|
if (fOSXABFieldMultiValues[curField->m_EudoraABFieldName] != NULL)
|
|
{
|
|
//
|
|
// special cases
|
|
//
|
|
|
|
// Is this the email field?
|
|
if (curField->m_pOSXABProperty == kABEmailProperty)
|
|
{
|
|
if (curField->m_EudoraABFieldName == abTagEmail)
|
|
{
|
|
// place the primary email address into the Email field
|
|
CFStringRef primaryEmailKey = ABMultiValueCopyPrimaryIdentifier(fOSXABFieldMultiValues[curField->m_EudoraABFieldName]);
|
|
if (primaryEmailKey != NULL)
|
|
{
|
|
primaryEmailIndex = ABMultiValueIndexForIdentifier(fOSXABFieldMultiValues[curField->m_EudoraABFieldName], primaryEmailKey);
|
|
if (primaryEmailIndex >= 0)
|
|
fOSXABFieldValues[abTagEmail] = (CFStringRef)ABMultiValueCopyValueAtIndex(fOSXABFieldMultiValues[curField->m_EudoraABFieldName], primaryEmailIndex);
|
|
CFRelease(primaryEmailKey);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// place all other email addresses into the OtherEmail accumulator
|
|
index = ABMultiValueCount(fOSXABFieldMultiValues[curField->m_EudoraABFieldName]);
|
|
while (--index)
|
|
{
|
|
if (index != primaryEmailIndex)
|
|
{
|
|
CFStringRef fieldValue = (CFStringRef)ABMultiValueCopyValueAtIndex(fOSXABFieldMultiValues[curField->m_EudoraABFieldName], index);
|
|
if (fieldValue != NULL)
|
|
{
|
|
if (fOtherEmailAddresses == NULL)
|
|
{
|
|
fOtherEmailAddresses = CFStringCreateMutableCopy(NULL, 1000, fieldValue);
|
|
}
|
|
else
|
|
{
|
|
CFStringAppendPascalString(fOtherEmailAddresses, "\p, ", NULL);;
|
|
CFStringAppend(fOtherEmailAddresses, fieldValue);
|
|
}
|
|
CFRelease(fieldValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// find the proper label within this property
|
|
count = ABMultiValueCount(fOSXABFieldMultiValues[curField->m_EudoraABFieldName]);
|
|
for (index = 0; (count > 0) && (index < count); index++)
|
|
{
|
|
CFStringRef curLabel = ABMultiValueCopyLabelAtIndex(fOSXABFieldMultiValues[curField->m_EudoraABFieldName], index);
|
|
if (curLabel != NULL)
|
|
{
|
|
if (CFStringCompare(curLabel, label, NULL) == kCFCompareEqualTo)
|
|
{
|
|
// do the proper thing to store the C-string representation of this value ...
|
|
if ((curPropertyType == kABDictionaryProperty) || (curPropertyType == kABMultiDictionaryProperty))
|
|
{
|
|
// this multi-value property is a Dictionary
|
|
CFDictionaryRef fieldDictionary = CFDictionaryCreateCopy(NULL, (CFDictionaryRef)ABMultiValueCopyValueAtIndex(fOSXABFieldMultiValues[curField->m_EudoraABFieldName], index));
|
|
CFStringRef dictFieldValue = (CFStringRef) CFDictionaryGetValue(fieldDictionary, key);
|
|
if (dictFieldValue)
|
|
{
|
|
fOSXABFieldValues[curField->m_EudoraABFieldName] = CFStringCreateCopy(NULL, dictFieldValue);
|
|
CFRelease(dictFieldValue);
|
|
}
|
|
}
|
|
else if ((curPropertyType == kABMultiStringProperty) != 0)
|
|
{
|
|
// this multi-value property is a multi-string
|
|
fOSXABFieldValues[curField->m_EudoraABFieldName] = (CFStringRef)ABMultiValueCopyValueAtIndex(fOSXABFieldMultiValues[curField->m_EudoraABFieldName],index);
|
|
}
|
|
// else
|
|
// this is some other unhandled type. Forgettaboutit.
|
|
CFRelease(curLabel);
|
|
break;
|
|
}
|
|
CFRelease(curLabel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CFRelease(valueCopy);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// find the value of this propetry ...
|
|
fOSXABFieldValues[curField->m_EudoraABFieldName] = (CFStringRef)ABRecordCopyValue(abRecord, property);
|
|
}
|
|
}
|
|
// else
|
|
// ignore this address book field.
|
|
|
|
// cleanup
|
|
if (recordType) CFRelease(recordType);
|
|
if (property) CFRelease(property);
|
|
if (label) CFRelease(label);
|
|
if (key) CFRelease(key);
|
|
|
|
// next field
|
|
curField++;
|
|
}
|
|
|
|
// we've read all the fields that map to Eudora's address book.
|
|
// Grab the OS X nickname as well.
|
|
property = CFStringCreateWithCString(NULL, kABNicknameProperty, NULL);
|
|
fOSXABNickName = (CFStringRef)ABRecordCopyValue(abRecord, property);
|
|
CFRelease(property);
|
|
}
|
|
|
|
/*************************************************************************************************
|
|
* ABSyncRecord_ImportGroup - Read in group data from the OS X Address Book
|
|
*************************************************************************************************/
|
|
void ABSyncRecord::ABSyncRecord_ImportGroup(ABGroupRef abRecord)
|
|
{
|
|
// initialize and set up
|
|
this->fIsGroup = true;
|
|
|
|
// read the name of this group
|
|
CFStringRef property = CFStringCreateWithCString(NULL,kABGroupNameProperty, NULL);
|
|
fOSXABFieldValues[abTagName] = (CFStringRef)ABRecordCopyValue(abRecord, property);
|
|
CFRelease(property);
|
|
|
|
// read in all members, including subgroups
|
|
ABSyncRecord_ImportGroupMembers(abRecord);
|
|
}
|
|
|
|
/*************************************************************************************************
|
|
* ABSyncRecord_ImportGroupMembers - Import a group.
|
|
*************************************************************************************************/
|
|
void ABSyncRecord::ABSyncRecord_ImportGroupMembers(ABGroupRef abRecord)
|
|
{
|
|
int count, emailIndex;
|
|
CFStringRef property;
|
|
ABPersonRef curMember;
|
|
CFStringRef addressFieldValues;
|
|
ABMultiValueRef addressFieldMultiValues;
|
|
CFStringRef emailKey;
|
|
CFStringRef address;
|
|
|
|
|
|
//
|
|
// Import group members
|
|
//
|
|
|
|
// next, grab the list of all members of this group
|
|
CFArrayRef members = ABGroupCopyArrayOfAllMembers(abRecord);
|
|
if (members)
|
|
{
|
|
count = CFArrayGetCount(members);
|
|
if (count > 0)
|
|
{
|
|
// iterate over each member
|
|
property = CFStringCreateWithCString(NULL,kABEmailProperty, NULL);
|
|
for (CFIndex curRecord = 0; curRecord < count; curRecord++)
|
|
{
|
|
curMember = (ABPersonRef)CFArrayGetValueAtIndex(members, curRecord);
|
|
|
|
// read their email address for this group
|
|
addressFieldValues = (CFStringRef)ABRecordCopyValue(curMember, property);
|
|
if (addressFieldValues)
|
|
{
|
|
addressFieldMultiValues = ABMultiValueCreateCopy((ABMultiValueRef)addressFieldValues);
|
|
if (addressFieldMultiValues)
|
|
{
|
|
emailKey = ABGroupCopyDistributionIdentifier(abRecord, curMember, property);
|
|
if (emailKey)
|
|
{
|
|
emailIndex = ABMultiValueIndexForIdentifier(addressFieldMultiValues, emailKey);
|
|
address = (CFStringRef)ABMultiValueCopyValueAtIndex(addressFieldMultiValues, emailIndex);
|
|
if (address)
|
|
{
|
|
// add this address to the group's list of addresses
|
|
if (fOtherEmailAddresses == NULL)
|
|
{
|
|
fOtherEmailAddresses = CFStringCreateMutableCopy(NULL, 1000, address);
|
|
}
|
|
else
|
|
{
|
|
CFStringAppendPascalString(fOtherEmailAddresses, "\p, ", NULL);;
|
|
CFStringAppend(fOtherEmailAddresses, address);
|
|
}
|
|
CFRelease(address);
|
|
}
|
|
CFRelease(emailKey);
|
|
}
|
|
CFRelease(addressFieldMultiValues);
|
|
}
|
|
CFRelease(addressFieldValues);
|
|
}
|
|
}
|
|
CFRelease(property);
|
|
}
|
|
CFRelease(members);
|
|
}
|
|
|
|
//
|
|
// Import sub groups
|
|
//
|
|
|
|
CFArrayRef subGroups = ABGroupCopyArrayOfAllSubgroups(abRecord);
|
|
if (subGroups)
|
|
{
|
|
count = CFArrayGetCount(subGroups);
|
|
if (count > 0)
|
|
{
|
|
for (CFIndex curRecord = 0; curRecord < count; curRecord++)
|
|
ABSyncRecord_ImportGroupMembers((ABGroupRef)CFArrayGetValueAtIndex(subGroups, curRecord));
|
|
}
|
|
CFRelease(subGroups);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************************************
|
|
* ABSyncRecord_GenerateAddressLine - generat a list of address to create a new Eudora nick from
|
|
*************************************************************************************************/
|
|
Handle ABSyncRecord::ABSyncRecord_GenerateAddressLine(void)
|
|
{
|
|
Handle aliasH = NULL;
|
|
OSErr err = noErr;
|
|
Accumulator line;
|
|
Boolean needsQuote = false;
|
|
const char *pAddresses;
|
|
Str255 emailAddress;
|
|
|
|
err = AccuInit(&line);
|
|
if (err == noErr)
|
|
{
|
|
// are we a group?
|
|
if (fIsGroup)
|
|
{
|
|
//
|
|
// add the list of email addresses ...
|
|
//
|
|
|
|
if (fOtherEmailAddresses)
|
|
{
|
|
pAddresses = CFStringGetCStringPtr(fOtherEmailAddresses, NULL);
|
|
if (pAddresses && *pAddresses)
|
|
AccuAddPtr(&line, (unsigned char *)pAddresses, SafeStrLen(pAddresses));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// add the primary email address ...
|
|
//
|
|
|
|
if (fOSXABFieldValues[abTagEmail] != NULL)
|
|
{
|
|
ConvertFieldValueToPString(fOSXABFieldValues[abTagEmail], emailAddress);
|
|
AccuAddPtr(&line, emailAddress+1, emailAddress[0]);
|
|
}
|
|
else
|
|
AccuAddChar(&line, kNoEmailAddressChar);
|
|
AccuAddChar(&line, kEndOfLine);
|
|
}
|
|
|
|
//
|
|
// return the alias line
|
|
//
|
|
|
|
AccuTrim(&line);
|
|
aliasH = line.data;
|
|
}
|
|
|
|
return (aliasH);
|
|
}
|
|
|
|
/*************************************************************************************************
|
|
* ABSyncRecord_GenerateNotesLine - generate a notes line to create a new Eudora nickname with
|
|
*************************************************************************************************/
|
|
Handle ABSyncRecord::ABSyncRecord_GenerateNotesLine(void)
|
|
{
|
|
Handle notesH = NULL;
|
|
OSErr err = noErr;
|
|
Boolean needsQuote = false;
|
|
Str255 value;
|
|
Str255 tag;
|
|
|
|
notesH = (char **)NuHandle(0);
|
|
err = MemError ();
|
|
if (err == noErr)
|
|
{
|
|
//
|
|
// add all the fields that belong in the notes ...
|
|
//
|
|
|
|
const ABSyncRecordField *curField = fFieldMappingTable;
|
|
while ((err == noErr) && (curField->m_EudoraABFieldName < ABReservedTagsLimit))
|
|
{
|
|
//
|
|
// special cases
|
|
//
|
|
if (curField->m_EudoraABFieldName == abTagOtherEmail)
|
|
{
|
|
if (!this->fIsGroup)
|
|
{
|
|
// add the list of other email addresses. Don't do this for groups.
|
|
if (fOtherEmailAddresses)
|
|
{
|
|
const char *pOtherEmailAddresses = CFStringGetCStringPtr(fOtherEmailAddresses, NULL);
|
|
int len = pOtherEmailAddresses ? strlen(pOtherEmailAddresses) : 0;
|
|
|
|
if (len)
|
|
err = AddAttributeValuePair((char **)notesH, GetRString(tag, ABReservedTagsStrn+abTagOtherEmail), (char *)pOtherEmailAddresses, len);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// one to one mapped fields
|
|
//
|
|
else if (fOSXABFieldValues[curField->m_EudoraABFieldName])
|
|
{
|
|
ConvertFieldValueToPString(fOSXABFieldValues[curField->m_EudoraABFieldName], value);
|
|
ConvertLineBreaks(value);
|
|
err = AddAttributeValuePair((char **)notesH, GetRString (tag, ABReservedTagsStrn + curField->m_EudoraABFieldName), (char *)(value+1), (long)value[0]);
|
|
}
|
|
curField++;
|
|
}
|
|
}
|
|
|
|
return (notesH);
|
|
}
|
|
|
|
/************************************************************************
|
|
* ABSyncRecord_GenerateNickName - build a nickname for a record in a way
|
|
* not quite unlike how Eudora does it.
|
|
************************************************************************/
|
|
unsigned char * ABSyncRecord::ABSyncRecord_GenerateNickName(void)
|
|
{
|
|
OSErr err = noErr;
|
|
Boolean needsQuote = false;
|
|
Str255 firstName, lastName;
|
|
|
|
// Init
|
|
Zero(fEudoraNickName);
|
|
|
|
// use the OSX AddressBook nickname if it exists
|
|
if (fOSXABNickName)
|
|
{
|
|
ConvertFieldValueToPString(fOSXABNickName, fEudoraNickName);
|
|
}
|
|
else
|
|
{
|
|
// otherwise generate our own
|
|
|
|
// get first and last name from the record
|
|
ConvertFieldValueToPString(fOSXABFieldValues[abTagFirst], firstName);
|
|
ConvertFieldValueToPString(fOSXABFieldValues[abTagLast], lastName);
|
|
|
|
// Derive the nickname by first looking joining the first and last names
|
|
if (*firstName || *lastName)
|
|
JoinFirstLast (fEudoraNickName, firstName, lastName);
|
|
|
|
// If that didn't work, try the full name field
|
|
if (!*(fEudoraNickName))
|
|
ConvertFieldValueToPString(fOSXABFieldValues[abTagName], fEudoraNickName);
|
|
|
|
// If that didn't work, try the company name field
|
|
if (!*(fEudoraNickName))
|
|
ConvertFieldValueToPString(fOSXABFieldValues[abTagCompany], fEudoraNickName);
|
|
}
|
|
|
|
// clean up the nickname
|
|
BeautifyFrom (fEudoraNickName);
|
|
SanitizeFN (fEudoraNickName, fEudoraNickName, NICK_BAD_CHAR, NICK_REP_CHAR, false);
|
|
*fEudoraNickName = MIN (*fEudoraNickName, sizeof (Str31) - 1);
|
|
|
|
return (fEudoraNickName);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Utilities
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////
|
|
|
|
/************************************************************************
|
|
* SafeStrLen - strlen that handles strlen(NULL)
|
|
************************************************************************/
|
|
short SafeStrLen(const char *s)
|
|
{
|
|
short i = 0;
|
|
if (s)
|
|
{
|
|
while (*s)
|
|
{
|
|
i++; s++;
|
|
}
|
|
}
|
|
return(i);
|
|
}
|
|
|
|
/**********************************************************************
|
|
* ConvertLineBreaks - convert line breaks. The Eudora Address Book
|
|
* gets all choked up on what the OS X Address Book gives it.
|
|
**********************************************************************/
|
|
void ConvertLineBreaks(Str255 in)
|
|
{
|
|
PReplace(in, "\p\n","\p\003");
|
|
PReplace(in, "\p\r","\p\003");
|
|
}
|
|
|
|
/************************************************************************
|
|
* MachOFunctionPointerForCFMFunctionPointer - This function allocates a
|
|
* block of CFM glue code which contains the instructions to call CFM
|
|
* routines. Thanks, Marshall! :)
|
|
************************************************************************/
|
|
UInt32 templ[6] = {0x3D800000, 0x618C0000, 0x800C0000, 0x804C0004, 0x7C0903A6, 0x4E800420};
|
|
void *MachOFunctionPointerForCFMFunctionPointer( void *cfmfp )
|
|
{
|
|
UInt32 *mfp = (UInt32*) NewPtr( sizeof(templ) ); // Must later dispose of allocated memory
|
|
if (mfp)
|
|
{
|
|
mfp[0] = templ[0] | ((UInt32)cfmfp >> 16);
|
|
mfp[1] = templ[1] | ((UInt32)cfmfp & 0xFFFF);
|
|
mfp[2] = templ[2];
|
|
mfp[3] = templ[3];
|
|
mfp[4] = templ[4];
|
|
mfp[5] = templ[5];
|
|
MakeDataExecutable( mfp, sizeof(templ) );
|
|
}
|
|
return( mfp );
|
|
}
|
|
|
|
/************************************************************************
|
|
* MakeEudoraABEntry - make an address book entry in Eudora
|
|
************************************************************************/
|
|
OSErr MakeEudoraABEntry(short which, Str255 nickname, Handle addresses, Handle notes)
|
|
{
|
|
OSErr err = noErr;
|
|
Handle mungedAddresses = nil;
|
|
|
|
if (addresses)
|
|
err=SuckAddresses((unsigned char ***)&mungedAddresses, (unsigned char **)addresses,true,false,false,nil);
|
|
|
|
// Add this nickname.
|
|
err = NewNickLow(mungedAddresses, notes, which, nickname, false, nrReplace, false);
|
|
if (mungedAddresses) DisposeHandle(mungedAddresses);
|
|
|
|
return (err);
|
|
}
|
|
|
|
/************************************************************************
|
|
* ConvertFieldValueToPString - turn a field value into a PString,
|
|
* decoding Unicode while we're at it.
|
|
************************************************************************/
|
|
OSErr ConvertFieldValueToPString(CFStringRef fieldValue, Str255 pValue)
|
|
{
|
|
static UnicodeToTextInfo info;
|
|
static TextEncoding encoding;
|
|
static Boolean bInit = false;
|
|
OSErr err = noErr;
|
|
const UniChar *pucv;
|
|
const char *puv;
|
|
|
|
// Init
|
|
pValue[0] = 0;
|
|
if (!bInit)
|
|
{
|
|
encoding = DefaultEncoding(kTextEncodingMacRoman);
|
|
err = CreateUnicodeToTextInfoByEncoding (encoding, &info);
|
|
if (err == noErr)
|
|
bInit = true;
|
|
}
|
|
|
|
// convert
|
|
if (err == noErr)
|
|
{
|
|
if (fieldValue)
|
|
{
|
|
puv = CFStringGetCStringPtr(fieldValue, NULL);
|
|
if (puv)
|
|
{
|
|
pValue[0] = SafeStrLen(puv);
|
|
BMD(puv, pValue+1, pValue[0]);
|
|
}
|
|
else
|
|
{
|
|
pucv = CFStringGetCharactersPtr(fieldValue);
|
|
if (pucv)
|
|
err = ConvertFromUnicodeToPString(info, CFStringGetLength(fieldValue) * 2, pucv, pValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (err);
|
|
} |