2 * Copyright (C) 2007-2009 Patrick Ohly <patrick.ohly@gmx.de>
3 * Copyright (C) 2009 Intel Corporation
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) version 3.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 #include "FileSyncSource.h"
27 // SyncEvolution includes a copy of Boost header files.
28 // They are safe to use without creating additional
29 // build dependencies. boost::filesystem requires a
30 // library and therefore is avoided here. Some
31 // utility functions from SyncEvolution are used
32 // instead, plus standard C/Posix functions.
33 #include <boost/algorithm/string/case_conv.hpp>
38 #include <sys/types.h>
41 #include <syncevo/util.h>
46 #include <syncevo/SyncContext.h>
47 #include <syncevo/declarations.h>
50 FileSyncSource::FileSyncSource(const SyncSourceParams ¶ms,
51 const string &dataformat) :
52 TrackingSyncSource(params),
55 if (dataformat.empty()) {
56 throwError("a data format must be specified");
58 size_t sep = dataformat.find(':');
59 if (sep == dataformat.npos) {
60 throwError(string("data format not specified as <mime type>:<mime version>: " + dataformat));
62 m_mimeType.assign(dataformat, 0, sep);
63 m_mimeVersion = dataformat.substr(sep + 1);
64 m_supportedTypes = dataformat;
67 const char *FileSyncSource::getMimeType() const
69 return m_mimeType.c_str();
72 const char *FileSyncSource::getMimeVersion() const
74 return m_mimeVersion.c_str();
77 void FileSyncSource::open()
79 const string &database = getDatabaseID();
80 const string prefix("file://");
82 bool createDir = false;
84 // file:// is optional. It indicates that the
85 // directory is to be created.
86 if (boost::starts_with(database, prefix)) {
87 basedir = database.substr(prefix.size());
93 // check and, if allowed and necessary, create it
94 if (!isDir(basedir)) {
95 if (errno == ENOENT && createDir) {
96 mkdir_p(basedir.c_str());
98 throwError(basedir, errno);
106 bool FileSyncSource::isEmpty()
112 dir = opendir(m_basedir.c_str());
114 SyncContext::throwError(m_basedir, errno);
117 struct dirent *entry = readdir(dir);
119 if (strcmp(entry->d_name, ".") &&
120 strcmp(entry->d_name, "..")) {
124 entry = readdir(dir);
127 SyncContext::throwError(m_basedir, errno);
140 void FileSyncSource::close()
145 FileSyncSource::Databases FileSyncSource::getDatabases()
149 result.push_back(Database("select database via directory path",
154 void FileSyncSource::listAllItems(RevisionMap_t &revisions)
156 ReadDir dirContent(m_basedir);
158 BOOST_FOREACH(const string &entry, dirContent) {
159 string filename = createFilename(entry);
160 string revision = getATimeString(filename);
161 long entrynum = atoll(entry.c_str());
162 if (entrynum >= m_entryCounter) {
163 m_entryCounter = entrynum + 1;
165 revisions[entry] = revision;
169 void FileSyncSource::readItem(const string &uid, std::string &item, bool raw)
171 string filename = createFilename(uid);
173 if (!ReadFile(filename, item)) {
174 throwError(filename + ": reading failed", errno);
178 TrackingSyncSource::InsertItemResult FileSyncSource::insertItem(const string &uid, const std::string &item, bool raw)
184 // Inserting a new and updating an existing item often uses
185 // very similar code. In this case only the code for determining
186 // the filename differs.
188 // In other sync sources the database might also have limitations
189 // for the content of different items, for example, only one
190 // VCALENDAR:EVENT with a certain UID. If the server does not
191 // recognize that and sends a new item which collides with an
192 // existing one, then the existing one should be updated.
195 // valid local ID: update that file
196 filename = createFilename(uid);
198 // no local ID: create new file
201 buff << m_entryCounter;
202 filename = createFilename(buff.str());
204 // only create and truncate if file does not
205 // exist yet, otherwise retry with next counter
207 if (stat(filename.c_str(), &dummy)) {
208 if (errno == ENOENT) {
212 throwError(filename, errno);
221 out.open(filename.c_str());
222 out.write(item.c_str(), item.size());
225 throwError(filename + ": writing failed", errno);
228 return InsertItemResult(newuid,
229 getATimeString(filename),
230 false /* true if adding item was turned into update */);
234 void FileSyncSource::removeItem(const string &uid)
236 string filename = createFilename(uid);
238 if (unlink(filename.c_str()) &&
240 throwError(filename, errno);
244 string FileSyncSource::getATimeString(const string &filename)
247 if (stat(filename.c_str(), &buf)) {
248 throwError(filename, errno);
250 time_t mtime = buf.st_mtime;
252 ostringstream revision;
255 return revision.str();
258 string FileSyncSource::createFilename(const string &entry)
260 string filename = m_basedir + "/" + entry;
266 #endif /* ENABLE_FILE */
268 #ifdef ENABLE_MODULES
269 # include "FileSyncSourceRegister.cpp"