summaryrefslogtreecommitdiff
path: root/src/backends/addressbook/AddressBookSource.h
blob: 1fb11204e8945e26786522d2018f5e5258358f50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
 * Copyright (C) 2007-2008 Patrick Ohly
 */

#ifndef INCL_ADDRESSBOOKSOURCE
#define INCL_ADDRESSBOOKSOURCE

#include <config.h>
#include "TrackingSyncSource.h"

#ifdef ENABLE_ADDRESSBOOK

#include <AddressBook/ABAddressBookC.h>

/**
 * a smart pointer for CoreFoundation object references
 *
 * trying to store a NULL pointer raises an exception,
 * unreferencing valid objects is done automatically
 *
 * @param T         the pointer type
 * @param release   CFRelease() is only called when passing true
 */
template<class T, bool doRelease = 
#ifdef IPHONE
    // by default do not release anything because that has led
    // to crashes: this is the safe default in case of doubt
    false
#else
    true
#endif
    > class ref {
    /** do not allow copy construction */
    ref( const ref &other) {};

    /** do not allow copying */
    void operator = ( const ref &other ) {}

 protected:
    T m_pointer;
    
  public:
    /**
     * create a smart pointer that owns the given object;
     * passing a NULL pointer and a name for the object raises an error
     */
    ref(T pointer = NULL, const char *objectName = NULL) :
        m_pointer( pointer )
    {
        if (!pointer && objectName ) {
            throw std::runtime_error(std::string("Error allocating ") + objectName);
        }
    };
    ~ref()
    {
        set( NULL );
    }

    /**
     * store another object in this pointer, replacing any which was
     * referenced there before;
     * passing a NULL pointer and a name for the object raises an error
     */
    void set( T pointer, const char *objectName = NULL )
    {
        if (m_pointer && doRelease) {
            CFRelease(m_pointer);
        }
        if (!pointer && objectName) {
            throw std::runtime_error(std::string("Error allocating ") + objectName);
        }
        m_pointer = pointer;
    }

    ref<T> &operator = ( T pointer ) { set( pointer ); return *this; }
    T operator-> () { return m_pointer; }
    T operator * () { return m_pointer; }
    operator T () { return m_pointer; }
    operator bool () { return m_pointer != NULL; }

    T release() {
        T res = m_pointer;
        m_pointer = NULL;
        return res;
    }
};

#if 0
/* template typedefs would have been handy here, but are not specified in C++ (yet) */
#ifdef IPHONE
/** do not free some particular objects on the iPhone because that crashes */
template<class T> typedef ref<T, false> iphoneref;
#else
template<class T> typedef ref<T, true> iphoneref;
#endif

#else

#ifdef IPHONE
# define IPHONE_RELEASE false
#else
# define IPHONE_RELEASE true
#endif

#endif


/**
 * The AddressBookSource synchronizes the Mac OS X and iPhone system
 * address book using the "AddressBook" framework. Changes are tracked
 * by comparing the current time stamp of a contact against its time
 * stamp from the previous sync, stored in a separate key/value
 * database. Contacts are converted to/from vCard 2.1 using custom
 * code because a) the mapping can be chosen so that typical SyncML
 * servers understand it and b) the iPhone's AddressBook does not have
 * vcard import/export functions.
 *
 * On the iPhone the interface is similar, but not the same. These
 * differences are hidden behind "ifdef IPHONE" which depends (for
 * simplicity reasons) on the __arm__ define.
 *
 * Some of the differences and how they are handled are listed here.
 * - ABC instead of AB prefix, other renames: map Mac OS X name to iPhone
 *   name before including AddressBook.h, then use Mac OS X names
 * - CFRelease() and CFCopyDescription on ABMultiValueRef crash (bugs?!):
 *   use ref<T, IPHONE_RELEASE> for those instead the normal ref smart pointer,
 *   avoid CFCopyDescription()
 * - UID is integer, not CFStringRef: added wrapper function
 * - the address of kABC*Property identifies properties, not the CFStringRef
 *   at that address, caused toolchain problems when initializing data
 *   with these addresses: added one additional address indirection
 * - UIDs are assigned to added contacts only during saving, but are needed
 *   earlier: save after adding each contact (affects performance and aborted
 *   sync changes address book - perhaps better guess UID?)
 * - Mac OS X 10.4 still uses the kABHomePageProperty (a single string),
 *   the iPhone switched to the more recent kABCURLProperty/kABURLsProperty:
 *   conversion code is slightly different
 * - iPhone does not have a title (e.g. "sir") property, only the job title
 * - label constants are not part of the framework:
 *   defined in AddressSourceConstants
 */
class AddressBookSource : public TrackingSyncSource
{
 public:
    AddressBookSource(const EvolutionSyncSourceParams &params, bool asVCard30);
    virtual ~AddressBookSource() { close(); }

    void setVCard30(bool asVCard30) { m_asVCard30 = asVCard30; }
    bool getVCard30() { return m_asVCard30; }

    virtual Databases getDatabases();
    virtual void open();
    virtual void listAllItems(RevisionMap_t &revisions);
    virtual void exportData(ostream &out);
    virtual InsertItemResult insertItem(const string &uid, const SyncItem &item);
    virtual SyncItem *createItem(const string &uid) { return createItem(uid, m_asVCard30); }
    virtual SyncItem *createItem(const string &uid, bool asVCard30);
    virtual void deleteItem(const string &uid);
    virtual void flush() {}
    virtual void close();

    virtual string fileSuffix() const { return "vcf"; }
    virtual const char *getMimeType() const { return m_asVCard30 ? "text/vcard" : "text/x-vcard"; }
    virtual const char *getMimeVersion() const { return m_asVCard30 ? "3.0" : "2.1"; }
    virtual const char *getSupportedTypes() const { return m_asVCard30 ? "text/vcard:3.0" : "text/x-vcard:2.1"; }

 protected:
    virtual void logItem(const string &uid, const string &info, bool debug = false);
    virtual void logItem(const SyncItem &item, const string &info, bool debug = false);


  private:
    /** valid after open(): the address book that this source references */
    ABAddressBookRef m_addressbook;

    /** returns absolute modification time or (if that doesn't exist) the creation time */
    string getModTime(ABRecordRef record);

    /** unless selected otherwise send items as vCard 2.1 */
    bool m_asVCard30;
};

#endif // ENABLE_EBOOK

#endif // INCL_ADDRESSBOOKSOURCE