305 lines
11 KiB
C++
305 lines
11 KiB
C++
/*
|
|
* Copyright (C) 2007-2009 Patrick Ohly <patrick.ohly@gmx.de>
|
|
* Copyright (C) 2011 Intel Corporation
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) version 3.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
* 02110-1301 USA
|
|
*/
|
|
|
|
#ifndef INCL_ACTIVESYNCSOURCE
|
|
#define INCL_ACTIVESYNCSOURCE
|
|
|
|
#include <config.h>
|
|
#include <syncevo/SyncSource.h>
|
|
|
|
#ifdef ENABLE_ACTIVESYNC
|
|
|
|
#include <syncevo/PrefixConfigNode.h>
|
|
#include <syncevo/SafeConfigNode.h>
|
|
#include <syncevo/SmartPtr.h>
|
|
#include <syncevo/GLibSupport.h>
|
|
|
|
#include <boost/bind.hpp>
|
|
|
|
#include <string>
|
|
#include <map>
|
|
|
|
#include "libeassync.h"
|
|
#include <eas-item-info.h>
|
|
#include <eas-folder.h>
|
|
|
|
#include <syncevo/declarations.h>
|
|
SE_BEGIN_CXX
|
|
|
|
|
|
/**
|
|
* Synchronizes contacts, events, tasks and journals with an
|
|
* ActiveSync server. Sub-classes provide the necessary specialization
|
|
* for different data formats.
|
|
*
|
|
* Data is exchanged between ActiveSyncSource, ActiveSync library, and
|
|
* Synthesis engine as vCard 3.0 and iCalendar 2.0 format. The
|
|
* standard contact and calendar profile is used, with ACTIVESYNC set
|
|
* as sub-rule. This influences how the Synthesis engine converts to
|
|
* and from its internal format. See KDE in
|
|
* src/syncevo/profiles/datatypes/01vcard-profile.xml for an extensive
|
|
* example how that works.
|
|
*
|
|
* Each ActiveSync calendar item is a VCALENDAR which contains all
|
|
* VEVENTs with the same UID. The SyncEvolution/Synthesis engine works
|
|
* with individual VEVENTs per item. This implies that someone has to
|
|
* map between the two concepts. This is done in the derived
|
|
* ActiveSyncCalendarSource, similar to the existing MapSyncSource.
|
|
*
|
|
* Reusing that class was considered, but discarded because
|
|
* MapSyncSource assumes that the class that it wraps uses id/revision
|
|
* string pairs for change tracking, which is not the case for
|
|
* ActiveSync.
|
|
*
|
|
* A sync session is done like this:
|
|
* - The Synthesis sync anchor directly maps to the
|
|
* ActiveSync sync key.
|
|
* - In SyncSourceSession::beginSync() (inherited from TestingSyncSource),
|
|
* the ActiveSync library is asked for changes (for a non-empty key)
|
|
* or all items (empty key).
|
|
* - The returned item IDs are stored in SyncSourceChanges.
|
|
* Only the server's IDs are used. They map 1:1 with the "luid" in
|
|
* the SyncSource API and the Synthesis engine.
|
|
* Because a full list of all existing items is expected,
|
|
* ActiveSyncSource maintains a list of all known items
|
|
* in the params.m_nodes.getTrackingNode() that it gets
|
|
* for that purpose.
|
|
* - The returned item content is cached in a local content cache
|
|
* and returns items from that when asked to via
|
|
* SyncSourceSerialize.
|
|
* - As items are added/remove/updated, the content cache, the list
|
|
* of IDs, and the sync key are updated. The expectation is
|
|
* that any changes made by other ActiveSync server clients
|
|
* will be reported when asking for changes based on that updated
|
|
* key without including changes made by our own client, even
|
|
* when these changes happen concurrently. This is true for
|
|
* Exchange 2010 + ActiveSync 12.1.
|
|
* TODO: write a test program to verify that assumption.
|
|
* Google + ActiveSync 12.0 do not support this.
|
|
* TODO: deal with server-side changes reported to us at the
|
|
* time when we make changes ourselves.
|
|
* - When multiple events are in one item, it can happen that
|
|
* the event series has to be retrieved individually from the
|
|
* server. Example:
|
|
* - nothing changed on server => nothing sent at start of sync,
|
|
* cache empty
|
|
* - ActiveSyncCalendarSource must delete a detached recurrence
|
|
* inside a series
|
|
* - retrieve series
|
|
* - remove recurrence
|
|
* - sent back the updated series
|
|
* - At the end of the sync, the updated ID list is stored and
|
|
* the updated sync key is returned to the Synthesis engine.
|
|
* - If anything goes wrong, a fatal error is returned to the
|
|
* Synthesis engine, which then invalidates the sync key and
|
|
* thus forces a slow sync in the next session.
|
|
* TODO: investigate more intelligent ways of recovering.
|
|
* The problem will be that trying again with the original
|
|
* sync key will return changes made by the client itself
|
|
* as part of the incomplete sync session.
|
|
*
|
|
* The command line item manipulation operations
|
|
* (--import/export/update/print-items/delete-items) always start a
|
|
* session without a sync key and thus (with the current API) have to
|
|
* download all items before doing anything.
|
|
* TODO: optimize that
|
|
*/
|
|
class ActiveSyncSource :
|
|
public TestingSyncSource, // == SyncSourceSession, SyncSourceChanges, SyncSourceDelete, SyncSourceSerialize
|
|
// TODO: implement SyncSourceLogging to get nicer debug and command line output
|
|
// virtual public SyncSourceLogging,
|
|
virtual public SyncSourceAdmin,
|
|
virtual public SyncSourceBlob
|
|
{
|
|
public:
|
|
ActiveSyncSource(const SyncSourceParams ¶ms) :
|
|
TestingSyncSource(params),
|
|
// Ensure that arbitrary keys can be stored (SafeConfigNode) and
|
|
// that we use a common prefix, so that we can use the key/value store
|
|
// also for other keys if the need ever arises).
|
|
m_itemNode(new PrefixConfigNode("item-",
|
|
boost::shared_ptr<ConfigNode>(new SafeConfigNode(params.m_nodes.getTrackingNode())))),
|
|
m_context(params.m_context)
|
|
{
|
|
if (!m_context) {
|
|
m_context.reset(new SyncConfig());
|
|
}
|
|
}
|
|
|
|
/** sync config used by this instance, never NULL */
|
|
SyncConfig &getSyncConfig() { return *m_context; }
|
|
|
|
/* partial implementation of SyncSource */
|
|
virtual void enableServerMode();
|
|
virtual bool serverModeEnabled() const;
|
|
virtual Databases getDatabases();
|
|
virtual void open();
|
|
virtual void close();
|
|
virtual std::string getPeerMimeType() const { return getMimeType(); }
|
|
|
|
/* implementation of SyncSourceSession */
|
|
virtual void beginSync(const std::string &lastToken, const std::string &resumeToken);
|
|
virtual std::string endSync(bool success);
|
|
|
|
/* implementation of SyncSourceDelete */
|
|
virtual void deleteItem(const string &luid);
|
|
|
|
/* partial implementation of SyncSourceSerialize */
|
|
virtual std::string getMimeType() const = 0;
|
|
virtual std::string getMimeVersion() const = 0;
|
|
virtual InsertItemResult insertItem(const std::string &luid, const std::string &item);
|
|
virtual void readItem(const std::string &luid, std::string &item);
|
|
|
|
/** to be provided by derived class */
|
|
virtual EasItemType getEasType() const = 0;
|
|
|
|
protected:
|
|
|
|
virtual void getSynthesisInfo(SynthesisInfo &info,
|
|
XMLConfigFragments &fragments);
|
|
EasSyncHandler *getHandler() { return m_handler.get(); }
|
|
std::string getFolder() { return m_folder; }
|
|
std::string getStartSyncKey() { return m_startSyncKey; }
|
|
void setStartSyncKey(const std::string &startSyncKey) { m_startSyncKey = startSyncKey; }
|
|
std::string getCurrentSyncKey() { return m_currentSyncKey; }
|
|
void setCurrentSyncKey(const std::string ¤tSyncKey) { m_currentSyncKey = currentSyncKey; }
|
|
void findCollections(const std::string &account, bool force_update);
|
|
std::string lookupFolder(const std::string &folder);
|
|
|
|
boost::shared_ptr<ConfigNode> m_itemNode;
|
|
|
|
private:
|
|
/** "target-config@<context>" instance which holds our username == ActiveSync account ID */
|
|
boost::shared_ptr<SyncConfig> m_context;
|
|
|
|
/** account ID for libeas, must be set in "username" config property */
|
|
std::string m_account;
|
|
|
|
/** folder ID for libeas, optionally set in "database" config property */
|
|
std::string m_folder;
|
|
|
|
/** smart pointer holding reference to EasSyncHandler during session */
|
|
eptr<EasSyncHandler, GObject> m_handler;
|
|
|
|
/** original sync key, set when session starts */
|
|
std::string m_startSyncKey;
|
|
|
|
/** current sync key, set when session starts and updated as changes are made */
|
|
std::string m_currentSyncKey;
|
|
|
|
/**
|
|
* server-side IDs of all items, updated as changes are reported and/or are made;
|
|
* NULL if not using change tracking
|
|
*/
|
|
boost::shared_ptr<ConfigNode> m_ids;
|
|
|
|
/**
|
|
* cache of all items, filled at begin of session and updated as
|
|
* changes are made (if doing change tracking)
|
|
*/
|
|
std::map<std::string, std::string> m_items;
|
|
|
|
/**
|
|
* list of folders
|
|
*/
|
|
typedef struct Collection {
|
|
std::string collectionId;
|
|
std::string name;
|
|
std::string parentId;
|
|
std::string pathName;
|
|
unsigned type;
|
|
bool pathFound;
|
|
ActiveSyncSource *source;
|
|
|
|
Collection() :
|
|
type(0),
|
|
pathFound(false),
|
|
source(NULL)
|
|
{}
|
|
|
|
int getFolderType();
|
|
bool collectionIsDefault();
|
|
std::string fullPath();
|
|
|
|
} collection;
|
|
std::map<std::string, Collection> m_collections; // Indexed by collectionID
|
|
typedef std::map<std::string, std::string> FolderPaths;
|
|
FolderPaths m_folderPaths; // Maps pathName to collectionId
|
|
};
|
|
|
|
class ActiveSyncContactSource : public ActiveSyncSource
|
|
{
|
|
public:
|
|
ActiveSyncContactSource(const SyncSourceParams ¶ms) :
|
|
ActiveSyncSource(params)
|
|
{}
|
|
|
|
protected:
|
|
/* partial implementation of SyncSourceSerialize */
|
|
virtual std::string getMimeType() const { return "text/vcard"; }
|
|
virtual std::string getMimeVersion() const { return "3.0"; }
|
|
|
|
EasItemType getEasType() const { return EAS_ITEM_CONTACT; }
|
|
};
|
|
|
|
/**
|
|
* used for all iCalendar 2.0 items (events, todos, journals)
|
|
*/
|
|
class ActiveSyncCalFormatSource : public ActiveSyncSource
|
|
{
|
|
EasItemType m_type;
|
|
|
|
public:
|
|
ActiveSyncCalFormatSource(const SyncSourceParams ¶ms, EasItemType type) :
|
|
ActiveSyncSource(params),
|
|
m_type(type)
|
|
{}
|
|
|
|
protected:
|
|
/* partial implementation of SyncSourceSerialize */
|
|
virtual std::string getMimeType() const { return "text/calendar"; }
|
|
virtual std::string getMimeVersion() const { return "2.0"; }
|
|
|
|
EasItemType getEasType() const { return m_type; }
|
|
};
|
|
|
|
void EASItemUnref(EasItemInfo *info);
|
|
|
|
/** non-copyable list of EasItemInfo pointers, owned by list */
|
|
typedef GListCXX<EasItemInfo, GSList, EASItemUnref> EASItemsCXX;
|
|
|
|
void GStringUnref(char *str);
|
|
|
|
/** non-copyable list of strings, owned by list */
|
|
typedef GListCXX<char, GSList, GStringUnref> EASIdsCXX;
|
|
|
|
/** non-copyable smart pointer to an EasItemInfo, unrefs when going out of scope */
|
|
typedef eptr<EasItemInfo, GObject> EASItemPtr;
|
|
|
|
void EASFolderUnref(EasFolder *f);
|
|
|
|
/** non-copyable list of EasFolder pointers, owned by list */
|
|
typedef GListCXX<EasFolder, GSList, EASFolderUnref> EASFoldersCXX;
|
|
|
|
SE_END_CXX
|
|
|
|
#endif // ENABLE_ACTIVESYNC
|
|
#endif // INCL_ACTIVESYNCSOURCE
|