2 * Copyright (C) 2008-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
21 #ifndef INCL_SYNCEVOLUTION_UTIL
22 # define INCL_SYNCEVOLUTION_UTIL
24 #include <syncevo/SyncML.h>
26 #include <boost/algorithm/string/case_conv.hpp>
27 #include <boost/algorithm/string/predicate.hpp>
28 #include <boost/function.hpp>
29 #include <boost/utility/value_init.hpp>
40 #include <syncevo/Timespec.h> // definitions used to be included in util.h,
41 // include it to avoid changing code using the time things
42 #include <syncevo/Logging.h>
44 #include <syncevo/declarations.h>
49 /** case-insensitive less than for assoziative containers */
50 template <class T> class Nocase : public std::binary_function<T, T, bool> {
52 bool operator()(const T &x, const T &y) const { return boost::ilexicographical_compare(x, y); }
55 /** case-insensitive equals */
56 template <class T> class Iequals : public std::binary_function<T, T, bool> {
58 bool operator()(const T &x, const T &y) const { return boost::iequals(x, y); }
61 /** shorthand, primarily useful for BOOST_FOREACH macro */
62 typedef std::pair<std::string, std::string> StringPair;
63 typedef std::map<std::string, std::string> StringMap;
66 * remove multiple slashes in a row and dots directly after a slash if not followed by filename,
69 std::string normalizePath(const std::string &path);
72 * Returns last component of path. Trailing slash is ignored.
73 * Empty if path is empty.
75 std::string getBasename(const std::string &path);
78 * Returns path without the last component. Empty if nothing left.
80 std::string getDirname(const std::string &path);
83 * Splits path into directory and file part. Trailing slashes
86 void splitPath(const std::string &path, std::string &dir, std::string &file);
89 * convert relative path to canonicalized absolute path
90 * @param path will be turned into absolute path if possible, otherwise left unchanged
91 * @return true if conversion is successful, false otherwise(errno will be set)
93 bool relToAbs(std::string &path);
95 /** ensure that m_path is writable, otherwise throw error */
96 void mkdir_p(const std::string &path);
98 inline bool rm_r_all(const std::string &path, bool isDir) { return true; }
101 * remove a complete directory hierarchy; invoking on non-existant directory is okay
102 * @param path relative or absolute path to be removed
103 * @param filter an optional callback which determines whether an entry really is
104 * to be deleted (return true in that case); called with full path
105 * to entry and true if known to be a directory
107 void rm_r(const std::string &path, boost::function<bool (const std::string &,
108 bool)> filter = rm_r_all);
111 * copy complete directory hierarchy
113 * If the source is a directory, then the target
114 * also has to be a directory name. It will be
115 * created if necessary.
117 * Alternatively, both names may refer to files.
118 * In that case the directory which is going to
119 * contain the target file must exist.
121 * @param from source directory or file
122 * @param to target directory or file (must have same type as from)
124 void cp_r(const std::string &from, const std::string &to);
126 /** true if the path refers to a directory */
127 bool isDir(const std::string &path);
130 * try to read a file into the given string, throw exception if fails
132 * @param filename absolute or relative file name
133 * @retval content filled with file content
134 * @return true if file could be read
136 bool ReadFile(const std::string &filename, std::string &content);
137 bool ReadFile(std::istream &in, std::string &content);
140 EXECUTE_NO_STDERR = 1<<0, /**< suppress stderr of command */
141 EXECUTE_NO_STDOUT = 1<<1 /**< suppress stdout of command */
145 * system() replacement
147 * If called without output redirection active (see LogRedirect),
148 * then it will simply call system(). If output redirection is
149 * active, the command is executed in a forked process without
150 * blocking the parent process and the parent reads the output,
151 * passing it through LogRedirect for processing.
153 * This is necessary to capture all output reliably: LogRedirect
154 * ensures that we don't deadlock, but to achieve that, it drops
155 * data when the child prints too much of it.
157 * @param cmd command including parameters, without output redirection
158 * @param flags see ExecuteFlags
159 * @return same as in system(): use WEXITSTATUS() et.al. to decode it
161 int Execute(const std::string &cmd, ExecuteFlags flags) throw();
164 * Simple string hash function, derived from Dan Bernstein's algorithm.
166 unsigned long Hash(const char *str);
167 unsigned long Hash(const std::string &str);
170 * SHA-256 implementation, returning hash as lowercase hex string (like sha256sum).
171 * Might not be available, in which case it raises an exception.
173 std::string SHA_256(const std::string &in);
176 * escape/unescape code
178 * Escaping is done URL-like, with a configurable escape
179 * character. The exact set of characters to replace (besides the
180 * special escape character) is configurable, too.
182 * The code used to be in SafeConfigNode, but is of general value.
188 SET, /**< explicit list of characters to be escaped */
189 INI_VALUE, /**< right hand side of .ini assignment:
190 escape all spaces at start and end (but not in the middle) and the equal sign */
191 INI_WORD, /**< same as before, but keep it one word:
192 escape all spaces and the equal sign = */
193 STRICT /**< general purpose:
194 escape all characters besides alphanumeric and -_ */
200 std::set<char> m_forbidden;
204 * default constructor, using % as escape character, escaping all spaces (including
205 * leading and trailing ones), and all characters besides alphanumeric and -_
207 StringEscape(char escapeChar = '%', Mode mode = STRICT) :
208 m_escapeChar(escapeChar),
213 * @param escapeChar character used to introduce escape sequence
214 * @param forbidden explicit list of characters which are to be escaped
216 StringEscape(char escapeChar, const char *forbidden);
218 /** special character which introduces two-char hex encoded original character */
219 char getEscapeChar() const { return m_escapeChar; }
220 void setEscapeChar(char escapeChar) { m_escapeChar = escapeChar; }
222 Mode getMode() const { return m_mode; }
223 void setMode(Mode mode) { m_mode = mode; }
226 * escape string according to current settings
228 std::string escape(const std::string &str) const;
230 /** escape string with the given settings */
231 static std::string escape(const std::string &str, char escapeChar, Mode mode);
234 * unescape string, with escape character as currently set
236 std::string unescape(const std::string &str) const { return unescape(str, m_escapeChar); }
239 * unescape string, with escape character as given
241 static std::string unescape(const std::string &str, char escapeChar);
245 * This is a simplified implementation of a class representing and calculating
246 * UUIDs v4 inspired from RFC 4122. We do not use cryptographic pseudo-random
247 * numbers, instead we rely on rand/srand.
249 * We initialize the random generation with the system time given by time(), but
252 * Instantiating this class will generate a new unique UUID, available afterwards
253 * in the base string class.
255 class UUID : public std::string {
261 * Safety check for string pointer.
262 * Returns pointer if valid, otherwise the default string.
264 inline const char *NullPtrCheck(const char *ptr, const char *def = "(null)")
266 return ptr ? ptr : def;
270 * A C++ wrapper around readir() which provides the names of all
271 * directory entries, excluding . and ..
276 ReadDir(const std::string &path, bool throwError = true);
278 typedef std::vector<std::string>::const_iterator const_iterator;
279 typedef std::vector<std::string>::iterator iterator;
280 iterator begin() { return m_entries.begin(); }
281 iterator end() { return m_entries.end(); }
282 const_iterator begin() const { return m_entries.begin(); }
283 const_iterator end() const { return m_entries.end(); }
286 * check whether directory contains entry, returns full path
287 * @param caseInsensitive ignore case, pick first entry which matches randomly
289 std::string find(const std::string &entry, bool caseSensitive);
293 std::vector<std::string> m_entries;
297 * Using this macro ensures that tests, even if defined in
298 * object files which are not normally linked into the test
299 * binary, are included in the test suite under the group
305 #ifdef ENABLE_UNIT_TESTS
307 class Foo : public CppUnit::TestFixture {
308 CPPUNIT_TEST_SUITE(foo);
309 CPPUNIT_TEST(testBar);
310 CPPUNIT_TEST_SUITE_END();
315 # SYNCEVOLUTION_TEST_SUITE_REGISTRATION(classname)
319 #define SYNCEVOLUTION_TEST_SUITE_REGISTRATION( ATestFixtureType ) \
320 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( ATestFixtureType, "SyncEvolution" ); \
321 extern "C" { int funambolAutoRegisterRegistry ## ATestFixtureType = 12345; }
323 std::string StringPrintf(const char *format, ...)
325 __attribute__((format(printf, 1, 2)))
328 std::string StringPrintfV(const char *format, va_list ap);
331 * strncpy() which inserts adds 0 byte
333 char *Strncpy(char *dest, const char *src, size_t n);
336 * sleep() with sub-second resolution. Might be interrupted by signals
337 * before the time has elapsed.
339 void Sleep(double seconds);
342 * Acts like the underlying type. In addition ensures that plain types
343 * are not left uninitialized.
345 template<class T> class Init {
347 Init(const T &val) : m_value(val) {}
348 Init() : m_value(boost::value_initialized<T>()) {}
349 Init(const Init &other) : m_value(other.m_value) {}
350 Init & operator = (const T &val) { m_value = val; return *this; }
351 operator const T & () const { return m_value; }
352 operator T & () { return m_value; }
359 * Version of InitState for scalar values (can't derive from them):
360 * acts like the underlying type. In addition ensures that plain types
361 * are not left uninitialized and tracks whether a value was every
362 * assigned explicitly.
364 template<class T> class InitState {
366 typedef T value_type;
368 InitState(const T &val, bool wasSet) : m_value(val), m_wasSet(wasSet) {}
369 InitState() : m_value(boost::value_initialized<T>()), m_wasSet(false) {}
370 InitState(const InitState &other) : m_value(other.m_value), m_wasSet(other.m_wasSet) {}
371 InitState & operator = (const T &val) { m_value = val; m_wasSet = true; return *this; }
372 operator const T & () const { return m_value; }
373 operator T & () { return m_value; }
374 const T & get() const { return m_value; }
375 T & get() { return m_value; }
376 bool wasSet() const { return m_wasSet; }
382 /** version of InitState for classes */
383 template<class T> class InitStateClass : public T {
385 typedef T value_type;
387 InitStateClass(const T &val, bool wasSet) : T(val), m_wasSet(wasSet) {}
388 InitStateClass() : m_wasSet(false) {}
389 InitStateClass(const char *val) : T(val), m_wasSet(false) {}
390 InitStateClass(const InitStateClass &other) : T(other), m_wasSet(other.m_wasSet) {}
391 InitStateClass & operator = (const T &val) { T::operator = (val); m_wasSet = true; return *this; }
392 const T & get() const { return *this; }
393 T & get() { return *this; }
394 bool wasSet() const { return m_wasSet; }
400 * a nop destructor which doesn't do anything, for boost::shared_ptr
404 template <class T> void operator () (T *) {}
408 * Acts like a boolean, but in addition, can also tell whether the
409 * value was explicitly set. Defaults to false for both.
411 typedef InitState<bool> Bool;
414 * Acts like a string, but in addition, can also tell whether the
415 * value was explicitly set.
417 typedef InitStateClass<std::string> InitStateString;
420 * Version of InitState where the value can true, false, or a string.
421 * Recognizes 0/1/false/true/no/yes case-insensitively as special
422 * booleans, everything else is considered a string.
424 class InitStateTri : public InitStateString
427 InitStateTri(const std::string &val, bool wasSet) : InitStateString(val, wasSet) {}
429 InitStateTri(const char *val) : InitStateString(val, false) {}
430 InitStateTri(const InitStateTri &other) : InitStateString(other) {}
431 InitStateTri(const InitStateString &other) : InitStateString(other) {}
439 // quick check for true/false, use get() for string case
440 Value getValue() const;
443 enum HandleExceptionFlags {
444 HANDLE_EXCEPTION_FLAGS_NONE = 0,
447 * a 404 status error is possible and must not be logged as ERROR
449 HANDLE_EXCEPTION_404_IS_OKAY = 1 << 0,
450 HANDLE_EXCEPTION_FATAL = 1 << 1,
452 * don't log exception as ERROR
454 HANDLE_EXCEPTION_NO_ERROR = 1 << 2,
455 HANDLE_EXCEPTION_MAX = 1 << 3,
459 * an exception which records the source file and line
460 * where it was thrown
462 * @TODO add function name
464 class Exception : public std::runtime_error
467 Exception(const std::string &file,
469 const std::string &what) :
470 std::runtime_error(what),
474 ~Exception() throw() {}
475 const std::string m_file;
479 * Convenience function, to be called inside a catch(..) block.
481 * Rethrows the exception to determine what it is, then logs it
482 * at the chosen level (error by default).
484 * Turns certain known exceptions into the corresponding
485 * status code if status still was STATUS_OK when called.
486 * Returns updated status code.
488 * @param logger the class which does the logging
489 * @retval explanation set to explanation for problem, if non-NULL
490 * @param level level to be used for logging
492 static SyncMLStatus handle(SyncMLStatus *status = NULL, Logger *logger = NULL, std::string *explanation = NULL, Logger::Level = Logger::ERROR, HandleExceptionFlags flags = HANDLE_EXCEPTION_FLAGS_NONE);
493 static SyncMLStatus handle(Logger *logger, HandleExceptionFlags flags = HANDLE_EXCEPTION_FLAGS_NONE) { return handle(NULL, logger, NULL, Logger::ERROR, flags); }
494 static SyncMLStatus handle(std::string &explanation, HandleExceptionFlags flags = HANDLE_EXCEPTION_FLAGS_NONE) { return handle(NULL, NULL, &explanation, Logger::ERROR, flags); }
495 static void handle(HandleExceptionFlags flags) { handle(NULL, NULL, NULL, Logger::ERROR, flags); }
496 static void log() { handle(NULL, NULL, NULL, Logger::DEBUG); }
499 * Tries to identify exception class based on explanation string created by
500 * handle(). If successful, that exception is throw with the same
501 * attributes as in the original exception. Otherwise parse() returns.
503 static void tryRethrow(const std::string &explanation);
506 * Same as tryRethrow() for strings with a 'org.syncevolution.xxxx:' prefix,
507 * as passed as D-Bus error strings.
509 static void tryRethrowDBus(const std::string &error);
513 * StatusException by wrapping a SyncML status
515 class StatusException : public Exception
518 StatusException(const std::string &file,
520 const std::string &what,
522 : Exception(file, line, what), m_status(status)
525 SyncMLStatus syncMLStatus() const { return m_status; }
527 SyncMLStatus m_status;
530 class TransportException : public Exception
533 TransportException(const std::string &file,
535 const std::string &what) :
536 Exception(file, line, what) {}
537 ~TransportException() throw() {}
540 class TransportStatusException : public StatusException
543 TransportStatusException(const std::string &file,
545 const std::string &what,
546 SyncMLStatus status) :
547 StatusException(file, line, what, status) {}
548 ~TransportStatusException() throw() {}
552 * replace ${} with environment variables, with
553 * XDG_DATA_HOME, XDG_CACHE_HOME and XDG_CONFIG_HOME having their normal
556 std::string SubstEnvironment(const std::string &str);
558 /** getenv() with default value */
559 inline const char *getEnv(const char *var, const char *def)
561 const char *res = getenv(var);
562 return res ? res : def;
565 inline std::string getHome() { return getEnv("HOME", "."); }
568 * Parse a separator splitted set of strings src, the separator itself is
569 * escaped by a backslash. Spaces around the separator is also stripped.
571 std::vector<std::string> unescapeJoinedString (const std::string &src, char separator);
574 * mapping from int flag to explanation
578 const char *m_description;
582 * turn flags into comma separated list of explanations
584 * @param flags bit mask
585 * @param descr array with zero m_flag as end marker
586 * @param sep used to join m_description strings
588 std::string Flags2String(int flags, const Flag *descr, const std::string &sep = ", ");
591 * Returns the path to the data directory. This is generally
592 * /usr/share/syncevolution/ but can be overridden by setting the
593 * SYNCEVOLUTION_DATA_DIR environment variable.
595 * @retval dataDir the path to the data directory
597 std::string SyncEvolutionDataDir();
600 * Temporarily set env variable, restore old value on destruction.
601 * Useful for unit tests which depend on the environment.
603 class ScopedEnvChange
606 ScopedEnvChange(const std::string &var, const std::string &value);
609 std::string m_var, m_oldval;
613 std::string getCurrentTime();
615 /** throw a normal SyncEvolution Exception, including source information */
616 #define SE_THROW(_what) \
617 SE_THROW_EXCEPTION(Exception, _what)
619 /** throw a class which accepts file, line, what parameters */
620 #define SE_THROW_EXCEPTION(_class, _what) \
621 throw _class(__FILE__, __LINE__, _what)
623 /** throw a class which accepts file, line, what plus 1 additional parameter */
624 #define SE_THROW_EXCEPTION_1(_class, _what, _x1) \
625 throw _class(__FILE__, __LINE__, (_what), (_x1))
627 /** throw a class which accepts file, line, what plus 2 additional parameters */
628 #define SE_THROW_EXCEPTION_2(_class, _what, _x1, _x2) \
629 throw _class(__FILE__, __LINE__, (_what), (_x1), (_x2))
631 /** throw a class which accepts file, line, what plus 2 additional parameters */
632 #define SE_THROW_EXCEPTION_3(_class, _what, _x1, _x2, _x3) \
633 throw _class(__FILE__, __LINE__, (_what), (_x1), (_x2), (_x3))
635 /** throw a class which accepts file, line, what plus 2 additional parameters */
636 #define SE_THROW_EXCEPTION_4(_class, _what, _x1, _x2, _x3, _x4) \
637 throw _class(__FILE__, __LINE__, (_what), (_x1), (_x2), (_x3), (_x4))
639 /** throw a class which accepts file, line, what parameters and status parameters*/
640 #define SE_THROW_EXCEPTION_STATUS(_class, _what, _status) \
641 throw _class(__FILE__, __LINE__, _what, _status)
644 #endif // INCL_SYNCEVOLUTION_UTIL