summaryrefslogtreecommitdiff
path: root/src/core/TrackingSyncSource.h
blob: f8be99a59e936cba40ad1ff74c43137da5f80c9c (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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
 * Copyright (C) 2008 Patrick Ohly
 */

#ifndef INCL_TRACKINGSYNCSOURCE
#define INCL_TRACKINGSYNCSOURCE

#include "EvolutionSyncSource.h"
#include "ConfigNode.h"

#include <boost/shared_ptr.hpp>
#include <string>
#include <map>
using namespace std;

/**
 * This class implements change tracking. Data sources which want to use
 * this functionality have to provide the following functionality
 * by implementing the pure virtual functions below:
 * - open() the data
 * - enumerate all existing items
 * - provide UID and "revision string": 
 *   The UID must remain *constant* when the user edits an item (it
 *   may change when SyncEvolution changes an item), whereas the
 *   revision string must *change* each time the item is changed
 *   by anyone.
 *   Both can be arbitrary strings, but keeping them simple (printable
 *   ASCII, no white spaces, no equal sign) makes debugging simpler
 *   because they can be stored as they are as key/value pairs in the
 *   sync source's change tracking config node (the .other.ini files when
 *   using file-based configuration). More complex strings use escape
 *   sequences introduced with an exclamation mark for unsafe characters.
 * - import/export/update single items
 * - persistently store all changes in flush()
 * - clean up in close()
 *
 * A derived class may (but doesn't have to) override additional
 * functions to modify or replace the default implementations, e.g.:
 * - dumping the complete database (export())
 *
 * Potential implementations of the revision string are:
 * - a modification time stamp
 * - a hash value of a textual representation of the item
 *   (beware, such a hash might change as the textual representation
 *    changes even though the item is unchanged)
 */
class TrackingSyncSource : public EvolutionSyncSource
{
  public:
    /**
     * Creates a new tracking sync source.
     */
    TrackingSyncSource(const EvolutionSyncSourceParams &params);

    /**
     * returns a list of all know sources for the kind of items
     * supported by this sync source
     */
    virtual Databases getDatabases() = 0;

    /**
     * Actually opens the data source specified in the constructor,
     * will throw the normal exceptions if that fails. Should
     * not modify the state of the sync source: that can be deferred
     * until the server is also ready and beginSync() is called.
     */
    virtual void open() = 0;

    /**
     * exports all items one after the other, separated by blank line;
     * if that format is not suitable, then the derived class must
     * override this call
     */
    virtual void exportData(ostream &out);

    typedef map<string, string> RevisionMap_t;

    /**
     * fills the complete mapping from UID to revision string of all
     * currently existing items
     *
     * Usually both UID and revision string must be non-empty. The
     * only exception is a refresh-from-client: in that case the
     * revision string may be empty. The implementor of this call
     * cannot know whether empty strings are allowed, therefore it
     * should not throw errors when it cannot create a non-empty
     * string. The caller of this method will detect situations where
     * a non-empty string is necessary and none was provided.
     */
    virtual void listAllItems(RevisionMap_t &revisions) = 0;

    class InsertItemResult {
    public:
        /**
         * @param uid       the uid after the operation; during an update the uid must
         *                  not be changed, so return the original one here
         * @param revision  the revision string after the operation
         * @param merged    set this to true if an existing item was updated instead of adding it
         */
        InsertItemResult(const string &uid,
                         const string &revision,
                         bool merged) :
        m_uid(uid),
            m_revision(revision),
            m_merged(merged)
            {}

        const string m_uid;
        const string m_revision;
        const bool m_merged;
    };

    /**
     * Create or modify an item.
     *
     * The sync source should be flexible: if the UID is non-empty, it
     * shall modify the item referenced by the UID. If the UID is
     * empty, the normal operation is to add it. But if the item
     * already exists (e.g., a calendar event which was imported
     * by the user manually), then the existing item should be
     * updated also in the second case.
     *
     * Passing a UID of an item which does not exist is an error.
     * This error should be reported instead of covering it up by
     * (re)creating the item.
     *
     * Errors are signalled by throwing an exception. Returning empty
     * strings in the result is an error which triggers an "item could
     * not be stored" error.
     *
     * @param uid      identifies the item to be modified, empty for creating
     * @param item     contains the new content of the item and its MIME type
     * @return the result of inserting the item
     */
    virtual InsertItemResult insertItem(const string &uid, const SyncItem &item) = 0;

    /**
     * Extract information for the item identified by UID
     * and store it in a new SyncItem. The caller must
     * free that item. May throw exceptions.
     *
     * @param uid      identifies the item
     */
    virtual SyncItem *createItem(const string &uid) = 0;

    /**
     * removes and item
     */
    virtual void deleteItem(const string &uid) = 0;

    /**
     * optional: write all changes, throw error if that fails
     *
     * This is called while the sync is still active whereas
     * close() is called afterwards. Reporting problems
     * as early as possible may be useful at some point,
     * but currently doesn't make a relevant difference.
     */
    virtual void flush() {}
    
    /**
     * closes the data source so that it can be reopened
     *
     * Just as open() it should not affect the state of
     * the database unless some previous action requires
     * it.
     */
    virtual void close() = 0;

    /**
     * file suffix for database files
     */
    virtual string fileSuffix() const = 0;

    /**
     * Returns the preferred mime type of the items handled by the sync source.
     * Example: "text/x-vcard"
     */
    virtual const char *getMimeType() const = 0;

    /**
     * Returns the version of the mime type used by client.
     * Example: "2.1"
     */
    virtual const char *getMimeVersion() const = 0;

    /**
     * A string representing the source types (with versions) supported by the SyncSource.
     * The string must be formatted as a sequence of "type:version" separated by commas ','.
     * For example: "text/x-vcard:2.1,text/vcard:3.0".
     * The version can be left empty, for example: "text/x-s4j-sifc:".
     * Supported types will be sent as part of the DevInf.
     */
    virtual const char* getSupportedTypes() const = 0;

 protected:
    /** log a one-line info about an item */
    virtual void logItem(const string &uid, const string &info, bool debug = false) = 0;
    virtual void logItem(const SyncItem &item, const string &info, bool debug = false) = 0;

    virtual void setItemStatusThrow(const char *key, int status);

  private:
    /* implementations of EvolutionSyncSource callbacks */
    virtual void beginSyncThrow(bool needAll,
                                bool needPartial,
                                bool deleteLocal);
    virtual void endSyncThrow();
    virtual int addItemThrow(SyncItem& item);
    virtual int updateItemThrow(SyncItem& item);
    virtual int deleteItemThrow(SyncItem& item);

    boost::shared_ptr<ConfigNode> m_trackingNode;
};

#endif // INCL_TRACKINGSYNCSOURCE