2 * Copyright (C) 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 #include <syncevo/SyncML.h>
22 #include <syncevo/ConfigNode.h>
23 #include <syncevo/util.h>
28 #include <boost/foreach.hpp>
29 #include <boost/algorithm/string/split.hpp>
30 #include <boost/algorithm/string/classification.hpp>
31 #include <boost/algorithm/string/predicate.hpp>
32 #include <boost/algorithm/string/replace.hpp>
33 #include <boost/algorithm/string/join.hpp>
35 #include <synthesis/syerror.h>
37 #include <syncevo/declarations.h>
40 std::string PrettyPrintSyncMode(SyncMode mode, bool userVisible)
44 return userVisible ? "disabled" : "SYNC_NONE";
47 return userVisible ? "two-way" : "SYNC_TWO_WAY";
49 return userVisible ? "slow" : "SYNC_SLOW";
50 case SYNC_ONE_WAY_FROM_CLIENT:
51 case SA_SYNC_ONE_WAY_FROM_CLIENT:
52 return userVisible ? "one-way-from-client" : "SYNC_ONE_WAY_FROM_CLIENT";
53 case SYNC_REFRESH_FROM_CLIENT:
54 case SA_SYNC_REFRESH_FROM_CLIENT:
55 return userVisible ? "refresh-from-client" : "SYNC_REFRESH_FROM_CLIENT";
56 case SYNC_ONE_WAY_FROM_SERVER:
57 case SA_SYNC_ONE_WAY_FROM_SERVER:
58 return userVisible ? "one-way-from-server" : "SYNC_ONE_WAY_FROM_SERVER";
59 case SYNC_REFRESH_FROM_SERVER:
60 case SA_SYNC_REFRESH_FROM_SERVER:
61 return userVisible ? "refresh-from-server" : "SYNC_REFRESH_FROM_SERVER";
62 case SYNC_RESTORE_FROM_BACKUP:
63 return userVisible ? "restore-from-backup" : "SYNC_RESTORE_FROM_BACKUP";
65 std::stringstream res;
67 res << (userVisible ? "sync-mode-" : "SYNC_") << int(mode);
72 SyncMode StringToSyncMode(const std::string &mode, bool serverAlerted)
74 if (boost::iequals(mode, "slow") || boost::iequals(mode, "SYNC_SLOW")) {
76 } else if (boost::iequals(mode, "two-way") || boost::iequals(mode, "SYNC_TWO_WAY")) {
77 return serverAlerted ?SA_SYNC_TWO_WAY: SYNC_TWO_WAY;
78 } else if (boost::iequals(mode, "refresh-from-server") || boost::iequals(mode, "SYNC_REFRESH_FROM_SERVER")) {
79 return serverAlerted? SA_SYNC_REFRESH_FROM_SERVER: SYNC_REFRESH_FROM_SERVER;
80 } else if (boost::iequals(mode, "refresh-from-client") || boost::iequals(mode, "SYNC_REFRESH_FROM_CLIENT")) {
81 return serverAlerted? SA_SYNC_REFRESH_FROM_CLIENT: SYNC_REFRESH_FROM_CLIENT;
82 } else if (boost::iequals(mode, "one-way-from-server") || boost::iequals(mode, "SYNC_ONE_WAY_FROM_SERVER")) {
83 return serverAlerted? SA_SYNC_ONE_WAY_FROM_SERVER: SYNC_ONE_WAY_FROM_SERVER;
84 } else if (boost::iequals(mode, "one-way-from-client") || boost::iequals(mode, "SYNC_ONE_WAY_FROM_CLIENT")) {
85 return serverAlerted? SA_SYNC_ONE_WAY_FROM_CLIENT: SYNC_ONE_WAY_FROM_CLIENT;
86 } else if (boost::iequals(mode, "disabled") || boost::iequals(mode, "SYNC_NONE")) {
94 ContentType StringToContentType(const std::string &type, bool force) {
95 if (boost::iequals (type, "text/x-vcard") || boost::iequals (type, "text/x-vcard:2.1")) {
97 } else if (boost::iequals (type, "text/vcard") ||boost::iequals (type, "text/vcard:3.0")) {
98 return force ? WSPCTC_VCARD : WSPCTC_XVCARD;
99 } else if (boost::iequals (type, "text/x-vcalendar") ||boost::iequals (type, "text/x-vcalendar:1.0")
100 ||boost::iequals (type, "text/x-calendar") || boost::iequals (type, "text/x-calendar:1.0")) {
101 return WSPCTC_XVCALENDAR;
102 } else if (boost::iequals (type, "text/calendar") ||boost::iequals (type, "text/calendar:2.0")) {
103 return force ? WSPCTC_ICALENDAR : WSPCTC_XVCALENDAR;
104 } else if (boost::iequals (type, "text/plain") ||boost::iequals (type, "text/plain:1.0")) {
105 return WSPCTC_TEXT_PLAIN;
107 return WSPCTC_UNKNOWN;
111 std::string Status2String(SyncMLStatus status)
117 local = status >= static_cast<int>(sysync::LOCAL_STATUS_CODE);
119 status <= static_cast<int>(sysync::LOCAL_STATUS_CODE_END)) {
120 code = status - static_cast<int>(sysync::LOCAL_STATUS_CODE);
130 case STATUS_NO_CONTENT:
131 error = "no content/end of data";
133 case STATUS_DATA_MERGED:
134 error = "data merged";
136 case STATUS_FORBIDDEN:
137 error = "access denied";
139 case STATUS_NOT_FOUND:
140 error = "object not found";
142 case STATUS_COMMAND_NOT_ALLOWED:
143 error = "operation not allowed";
145 case STATUS_ALREADY_EXISTS:
146 error = "object exists already";
149 error = "fatal error";
151 case STATUS_DATASTORE_FAILURE:
152 error = "database failure";
155 error = "out of space";
158 case STATUS_UNEXPECTED_SLOW_SYNC:
159 error = "unexpected slow sync";
162 case STATUS_PARTIAL_FAILURE:
163 error = "some changes could not be transferred";
166 case sysync::LOCERR_BADPROTO:
167 error = "bad or unknown protocol";
169 case sysync::LOCERR_SMLFATAL:
170 error = "fatal problem with SML";
172 case sysync::LOCERR_COMMOPEN:
173 error = "cannot open communication";
175 case sysync::LOCERR_SENDDATA:
176 error = "cannot send data";
178 case sysync::LOCERR_RECVDATA:
179 error = "cannot receive data";
181 case sysync::LOCERR_BADCONTENT:
182 error = "bad content in response";
184 case sysync::LOCERR_PROCESSMSG:
185 error = "SML (or SAN) error processing incoming message";
187 case sysync::LOCERR_COMMCLOSE:
188 error = "cannot close communication";
190 case sysync::LOCERR_AUTHFAIL:
191 error = "transport layer authorisation failed";
193 case sysync::LOCERR_CFGPARSE:
194 error = "error parsing config file";
196 case sysync::LOCERR_CFGREAD:
197 error = "error reading config file";
199 case sysync::LOCERR_NOCFG:
200 error = "no config found";
202 case sysync::LOCERR_NOCFGFILE:
203 error = "config file could not be found";
205 case sysync::LOCERR_EXPIRED:
208 case sysync::LOCERR_WRONGUSAGE:
211 case sysync::LOCERR_BADHANDLE:
212 error = "bad handle";
214 case sysync::LOCERR_USERABORT:
215 error = "aborted on behalf of user";
217 case sysync::LOCERR_BADREG:
218 error = "bad registration";
220 case sysync::LOCERR_LIMITED:
221 error = "limited trial version";
223 case sysync::LOCERR_TIMEOUT:
224 error = "connection timeout";
226 case sysync::LOCERR_CERT_EXPIRED:
227 error = "connection SSL certificate expired";
229 case sysync::LOCERR_CERT_INVALID:
230 error = "connection SSL certificate invalid";
232 case sysync::LOCERR_INCOMPLETE:
233 error = "incomplete sync session";
235 case sysync::LOCERR_RETRYMSG:
236 error = "retry sending message";
238 case sysync::LOCERR_OUTOFMEM:
239 error = "out of memory";
241 case sysync::LOCERR_NOCONN:
242 error = "no means to open a connection";
244 case sysync::LOCERR_CONN:
245 error = "connection cannot be established";
247 case sysync::LOCERR_ALREADY:
248 error = "element is already installed";
250 case sysync::LOCERR_TOONEW:
251 error = "this build is too new for this license";
253 case sysync::LOCERR_NOTIMP:
254 error = "function not implemented";
256 case sysync::LOCERR_WRONGPROD:
257 error = "this license code is valid, but not for this product";
259 case sysync::LOCERR_USERSUSPEND:
260 error = "explicitly suspended on behalf of user";
262 case sysync::LOCERR_TOOOLD:
263 error = "this build is too old for this SDK/plugin";
265 case sysync::LOCERR_UNKSUBSYSTEM:
266 error = "unknown subsystem";
268 case sysync::LOCERR_SESSIONRST:
269 error = "next message will be a session restart";
271 case sysync::LOCERR_LOCDBNOTRDY:
272 error = "local datastore is not ready";
274 case sysync::LOCERR_RESTART:
275 error = "session should be restarted from scratch";
277 case sysync::LOCERR_PIPECOMM:
278 error = "internal pipe communication problem";
280 case sysync::LOCERR_BUFTOOSMALL:
281 error = "buffer too small for requested value";
283 case sysync::LOCERR_TRUNCATED:
284 error = "value truncated to fit into field or buffer";
286 case sysync::LOCERR_BADPARAM:
287 error = "bad parameter";
289 case sysync::LOCERR_OUTOFRANGE:
290 error = "out of range";
292 case sysync::LOCERR_TRANSPFAIL:
293 error = "external transport failure";
295 case sysync::LOCERR_CLASSNOTREG:
296 error = "class not registered";
298 case sysync::LOCERR_IIDNOTREG:
299 error = "interface not registered";
301 case sysync::LOCERR_BADURL:
304 case sysync::LOCERR_SRVNOTFOUND:
305 error = "server not found";
312 string statusstr = StringPrintf("%s, status %d",
313 local ? "local" : "remote",
317 description = statusstr;
319 description = StringPrintf("%s (%s)",
328 const char * const locNames[] = { "local", "remote", NULL };
329 const char * const stateNames[] = { "added", "updated", "removed", "any", NULL };
330 const char * const resultNames[] = { "total", "reject", "match",
331 "conflict_server_won",
332 "conflict_client_won",
333 "conflict_duplicated",
338 int toIndex(const char * const names[],
339 const std::string &name) {
342 names[i] && name != names[i];
347 std::string toString(const char * const names[],
360 std::string SyncSourceReport::LocationToString(ItemLocation location) { return toString(locNames, location); }
361 SyncSourceReport::ItemLocation SyncSourceReport::StringToLocation(const std::string &location) { return static_cast<ItemLocation>(toIndex(locNames, location)); }
362 std::string SyncSourceReport::StateToString(ItemState state) { return toString(stateNames, state); }
363 SyncSourceReport::ItemState SyncSourceReport::StringToState(const std::string &state) { return static_cast<ItemState>(toIndex(stateNames, state)); }
364 std::string SyncSourceReport::ResultToString(ItemResult result) { return toString(resultNames, result); }
365 SyncSourceReport::ItemResult SyncSourceReport::StringToResult(const std::string &result) { return static_cast<ItemResult>(toIndex(resultNames, result)); }
367 std::string SyncSourceReport::StatTupleToString(ItemLocation location, ItemState state, ItemResult result)
369 return std::string("") +
370 LocationToString(location) + "-" +
371 StateToString(state) + "-" +
372 ResultToString(result);
374 void SyncSourceReport::StringToStatTuple(const std::string &str, ItemLocation &location, ItemState &state, ItemResult &result)
376 std::vector< std::string > tokens;
377 boost::split(tokens, str, boost::is_any_of("-"));
378 location = tokens.size() > 0 ? StringToLocation(tokens[0]) : ITEM_LOCATION_MAX;
379 state = tokens.size() > 1 ? StringToState(tokens[1]) : ITEM_STATE_MAX;
380 result = tokens.size() > 2 ? StringToResult(tokens[2]) : ITEM_RESULT_MAX;
383 bool SyncSourceReport::wasChanged(ItemLocation location)
385 for (int i = ITEM_ADDED; i < ITEM_ANY; i++) {
386 if (getItemStat(location, (ItemState)i, ITEM_TOTAL) > 0) {
394 std::ostream &operator << (std::ostream &out, const SyncReport &report)
396 report.prettyPrint(out, 0);
401 string fill(char sep, size_t width) {
403 res.resize(width - 1, sep);
406 string center(char sep, const string &str, size_t width) {
407 if (str.size() + 1 >= width) {
411 res.resize(width - 1, sep);
412 res.replace((width - 1 - str.size()) / 2, str.size(), str);
416 string right(char sep, const string &str, size_t width) {
417 if (str.size() + 1 >= width) {
421 res.resize(width - 1, sep);
422 res.replace(width - 2 - str.size(), str.size(), str);
426 string left(char sep, const string &str, size_t width) {
427 if (str.size() + 1 >= width) {
431 res.resize(width - 1, sep);
432 res.replace(1, str.size(), str);
437 // insert string at column if it fits, otherwise flush right
438 string align(char sep, const string &str, size_t width, size_t column) {
439 if (column + str.size() + 1 >= width) {
440 return right(sep, str, width);
443 res.resize(width - 1, sep);
444 res.replace(column, str.size(), str);
450 void SyncReport::prettyPrint(std::ostream &out, int flags) const
452 // table looks like this:
453 // +-------------------+-------------------------------+-------------------------------|-CON-+
454 // | | LOCAL | REMOTE | FLI |
455 // | Source | NEW | MOD | DEL | ERR | TOTAL | NEW | MOD | DEL | ERR | TOTAL | CTS |
456 // +-------------------+-----+-----+-----+-----+-------+-----+-----+-----+-----+-------+-----+
458 // Most of the columns can be turned on or off dynamically.
459 // Their width is calculated once (including right separators and spaces):
460 // | name_width |count_width| | |conflict_width|
461 // |client_width | server_width |
464 // name column is sized dynamically based on column header and actual names
465 size_t name_width = strlen("Source");
466 BOOST_FOREACH(const SyncReport::value_type &entry, *this) {
467 const std::string &name = entry.first;
468 if (name_width < name.size()) {
469 name_width = name.size();
472 name_width += 1; // separator
473 if (name_width < 20) {
474 // enough room for spaces
480 if (flags & WITH_TOTAL) {
483 if (!(flags & WITHOUT_REJECTS)) {
486 int client_width = (flags & WITHOUT_CLIENT) ? 0 :
487 num_counts * count_width;
488 int server_width = (flags & WITHOUT_SERVER) ? 0 :
489 num_counts * count_width;
490 int conflict_width = (flags & WITHOUT_CONFLICTS) ? 0 : 6;
491 int text_width = name_width + client_width + server_width + conflict_width;
493 if (text_width < 70) {
494 // enlarge name column to make room for long lines of text
495 name_width += 70 - text_width;
499 out << "+" << fill('-', name_width);
500 if (!(flags & WITHOUT_CLIENT)) {
501 out << '|' << center('-', "", client_width);
503 if (!(flags & WITHOUT_SERVER)) {
504 out << '|' << center('-', "", server_width);
506 if (!(flags & WITHOUT_CONFLICTS)) {
507 out << '|' << center('-', "CON", conflict_width);
511 if (!(flags & WITHOUT_REJECTS) || !(flags & WITHOUT_CONFLICTS)) {
512 out << "|" << fill(' ', name_width);
513 if (!(flags & WITHOUT_CLIENT)) {
514 out << '|' << center(' ', "LOCAL", client_width);
516 if (!(flags & WITHOUT_SERVER)) {
517 out << '|' << center(' ', "REMOTE", server_width);
519 if (!(flags & WITHOUT_CONFLICTS)) {
520 out << '|' << center(' ', "FLI", conflict_width);
525 out << '|' << right(' ', "Source", name_width);
526 if (!(flags & WITHOUT_CLIENT)) {
527 out << '|' << center(' ', "NEW", count_width);
528 out << '|' << center(' ', "MOD", count_width);
529 out << '|' << center(' ', "DEL", count_width);
530 if (!(flags & WITHOUT_REJECTS)) {
531 out << '|' << center(' ', "ERR", count_width);
533 if (flags & WITH_TOTAL) {
534 out << '|' << center(' ', "TOTAL", count_width);
537 if (!(flags & WITHOUT_SERVER)) {
538 out << '|' << center(' ', "NEW", count_width);
539 out << '|' << center(' ', "MOD", count_width);
540 out << '|' << center(' ', "DEL", count_width);
541 if (!(flags & WITHOUT_REJECTS)) {
542 out << '|' << center(' ', "ERR", count_width);
544 if (flags & WITH_TOTAL) {
545 out << '|' << center(' ', "TOTAL", count_width);
548 if (!(flags & WITHOUT_CONFLICTS)) {
549 out << '|' << center(' ', "CTS", conflict_width);
553 stringstream sepstream;
554 sepstream << '+' << fill('-', name_width);
555 if (!(flags & WITHOUT_CLIENT)) {
556 sepstream << '+' << fill('-', count_width);
557 sepstream << '+' << fill('-', count_width);
558 sepstream << '+' << fill('-', count_width);
559 if (!(flags & WITHOUT_REJECTS)) {
560 sepstream << '+' << fill('-', count_width);
562 if (flags & WITH_TOTAL) {
563 sepstream << '+' << fill('-', count_width);
566 if (!(flags & WITHOUT_SERVER)) {
567 sepstream << '+' << fill('-', count_width);
568 sepstream << '+' << fill('-', count_width);
569 sepstream << '+' << fill('-', count_width);
570 if (!(flags & WITHOUT_REJECTS)) {
571 sepstream << '+' << fill('-', count_width);
573 if (flags & WITH_TOTAL) {
574 sepstream << '+' << fill('-', count_width);
577 if (!(flags & WITHOUT_CONFLICTS)) {
578 sepstream << '+' << fill('-', conflict_width);
581 string sep = sepstream.str();
584 BOOST_FOREACH(const SyncReport::value_type &entry, *this) {
585 const std::string &name = entry.first;
586 const SyncSourceReport &source = entry.second;
587 out << '|' << right(' ', name, name_width);
588 ssize_t name_column = name_width - 2 - name.size();
589 if (name_column < 0) {
592 for (SyncSourceReport::ItemLocation location =
593 ((flags & WITHOUT_CLIENT) ? SyncSourceReport::ITEM_REMOTE : SyncSourceReport::ITEM_LOCAL);
594 location <= ((flags & WITHOUT_SERVER) ? SyncSourceReport::ITEM_LOCAL : SyncSourceReport::ITEM_REMOTE);
595 location = SyncSourceReport::ItemLocation(int(location) + 1)) {
596 for (SyncSourceReport::ItemState state = SyncSourceReport::ITEM_ADDED;
597 state <= SyncSourceReport::ITEM_REMOVED;
598 state = SyncSourceReport::ItemState(int(state) + 1)) {
600 count << source.getItemStat(location, state, SyncSourceReport::ITEM_TOTAL);
601 out << '|' << center(' ', count.str(), count_width);
603 if (!(flags & WITHOUT_REJECTS)) {
605 count << source.getItemStat(location,
606 SyncSourceReport::ITEM_ANY,
607 SyncSourceReport::ITEM_REJECT);
608 out << '|' << center(' ', count.str(), count_width);
610 if (flags & WITH_TOTAL) {
612 count << source.getItemStat(location,
613 SyncSourceReport::ITEM_ANY,
614 SyncSourceReport::ITEM_TOTAL);
615 out << '|' << center(' ', count.str(), count_width);
619 int total_conflicts = 0;
620 if (!(flags & WITHOUT_CONFLICTS)) {
622 source.getItemStat(SyncSourceReport::ITEM_REMOTE,
623 SyncSourceReport::ITEM_ANY,
624 SyncSourceReport::ITEM_CONFLICT_SERVER_WON) +
625 source.getItemStat(SyncSourceReport::ITEM_REMOTE,
626 SyncSourceReport::ITEM_ANY,
627 SyncSourceReport::ITEM_CONFLICT_CLIENT_WON) +
628 source.getItemStat(SyncSourceReport::ITEM_REMOTE,
629 SyncSourceReport::ITEM_ANY,
630 SyncSourceReport::ITEM_CONFLICT_DUPLICATED);
631 stringstream conflicts;
632 conflicts << total_conflicts;
633 out << '|' << center(' ', conflicts.str(), conflict_width);
637 std::stringstream line;
639 if (source.getFinalSyncMode() != SYNC_NONE ||
640 source.getItemStat(SyncSourceReport::ITEM_LOCAL,
641 SyncSourceReport::ITEM_ANY,
642 SyncSourceReport::ITEM_SENT_BYTES) ||
643 source.getItemStat(SyncSourceReport::ITEM_LOCAL,
644 SyncSourceReport::ITEM_ANY,
645 SyncSourceReport::ITEM_RECEIVED_BYTES)) {
647 PrettyPrintSyncMode(source.getFinalSyncMode()) << ", " <<
648 source.getItemStat(SyncSourceReport::ITEM_LOCAL,
649 SyncSourceReport::ITEM_ANY,
650 SyncSourceReport::ITEM_SENT_BYTES) / 1024 <<
651 " KB sent by client, " <<
652 source.getItemStat(SyncSourceReport::ITEM_LOCAL,
653 SyncSourceReport::ITEM_ANY,
654 SyncSourceReport::ITEM_RECEIVED_BYTES) / 1024 <<
656 out << '|' << align(' ', line.str(), text_width, name_column) << "|\n";
659 if (total_conflicts > 0) {
660 for (SyncSourceReport::ItemResult result = SyncSourceReport::ITEM_CONFLICT_SERVER_WON;
661 result <= SyncSourceReport::ITEM_CONFLICT_DUPLICATED;
662 result = SyncSourceReport::ItemResult(int(result) + 1)) {
664 if ((count = source.getItemStat(SyncSourceReport::ITEM_REMOTE,
665 SyncSourceReport::ITEM_ANY,
666 result)) != 0 || true) {
667 std::stringstream line;
668 line << count << " " <<
669 (result == SyncSourceReport::ITEM_CONFLICT_SERVER_WON ? "client item(s) discarded" :
670 result == SyncSourceReport::ITEM_CONFLICT_CLIENT_WON ? "server item(s) discarded" :
671 "item(s) duplicated");
673 out << '|' << align(' ', line.str(), text_width, name_column) << "|\n";
678 int total_matched = source.getItemStat(SyncSourceReport::ITEM_REMOTE,
679 SyncSourceReport::ITEM_ANY,
680 SyncSourceReport::ITEM_MATCH);
683 line << total_matched << " item(s) matched";
684 out << '|' << align(' ', line.str(), text_width, name_column)
688 if (source.m_backupBefore.isAvailable() ||
689 source.m_backupAfter.isAvailable()) {
690 std::stringstream backup;
691 backup << "item(s) in database backup: ";
692 if (source.m_backupBefore.isAvailable()) {
693 backup << source.m_backupBefore.getNumItems() << " before sync, ";
695 backup << "no backup before sync, ";
697 if (source.m_backupAfter.isAvailable()) {
698 backup << source.m_backupAfter.getNumItems() << " after it";
700 backup << "no backup after it";
702 out << '|' << align(' ', backup.str(), text_width, name_column) << "|\n";
704 if (source.getStatus()) {
705 out << '|' << align(' ',
706 Status2String(source.getStatus()),
707 text_width, name_column) << "|\n";
713 out << '|' << center(' ', formatSyncTimes(), text_width) << "|\n";
716 out << '|' << center(' ',
717 getStatus() != STATUS_HTTP_OK ?
718 Status2String(getStatus()) :
719 "synchronization completed successfully",
723 if (getStatus() || getStart()) {
726 if (!getError().empty()) {
727 out << "First ERROR encountered: " << getError() << endl;
731 std::string SyncReport::formatSyncTimes() const
733 std::stringstream out;
734 time_t duration = m_end - m_start;
741 strftime(buffer, sizeof(buffer), "%c", localtime(&m_start));
744 out << ", unknown duration (crashed?!)";
746 out << ", duration " << duration / 60 << ":"
747 << std::setw(2) << std::setfill('0') << duration % 60
754 std::string SyncReport::slowSyncExplanation(const std::string &peer,
755 const std::set<std::string> &sources)
757 if (sources.empty()) {
761 string sourceparam = boost::join(sources, " ");
762 std::string explanation =
763 StringPrintf("Doing a slow synchronization may lead to duplicated items or\n"
764 "lost data when the server merges items incorrectly. Choosing\n"
765 "a different synchronization mode may be the better alternative.\n"
766 "Restart synchronization of affected source(s) with one of the\n"
767 "following sync modes to recover from this problem:\n"
768 " slow, refresh-from-server, refresh-from-client\n\n"
769 "Analyzing the current state:\n"
770 " syncevolution --status %s %s\n\n"
771 "Running with one of the three modes:\n"
772 " syncevolution --sync [slow|refresh-from-server|refresh-from-client] %s %s\n",
773 peer.c_str(), sourceparam.c_str(),
774 peer.c_str(), sourceparam.c_str());
778 std::string SyncReport::slowSyncExplanation(const std::string &peer) const
780 std::set<std::string> sources;
781 BOOST_FOREACH(const SyncReport::value_type &entry, *this) {
782 const std::string &name = entry.first;
783 const SyncSourceReport &source = entry.second;
784 if (source.getStatus() == STATUS_UNEXPECTED_SLOW_SYNC) {
785 string virtualsource = source.getVirtualSource();
786 sources.insert(virtualsource.empty() ?
791 return slowSyncExplanation(peer, sources);
794 ConfigNode &operator << (ConfigNode &node, const SyncReport &report)
796 node.setProperty("start", static_cast<long>(report.getStart()));
797 node.setProperty("end", static_cast<long>(report.getEnd()));
798 node.setProperty("status", static_cast<int>(report.getStatus()));
799 string error = report.getError();
800 if (!error.empty()) {
801 node.setProperty("error", error);
803 node.removeProperty("error");
806 BOOST_FOREACH(const SyncReport::value_type &entry, report) {
807 const std::string &name = entry.first;
808 const SyncSourceReport &source = entry.second;
810 string prefix = name;
811 boost::replace_all(prefix, "_", "__");
812 boost::replace_all(prefix, "-", "_+");
813 prefix = "source-" + prefix;
816 key = prefix + "-mode";
817 node.setProperty(key, PrettyPrintSyncMode(source.getFinalSyncMode()));
818 key = prefix + "-first";
819 node.setProperty(key, source.isFirstSync());
820 key = prefix + "-resume";
821 node.setProperty(key, source.isResumeSync());
822 key = prefix + "-status";
823 node.setProperty(key, static_cast<long>(source.getStatus()));
824 string virtualsource = source.getVirtualSource();
825 if (!virtualsource.empty()) {
826 key = prefix + "-virtualsource";
827 node.setProperty(key, virtualsource);
829 key = prefix + "-backup-before";
830 node.setProperty(key, source.m_backupBefore.getNumItems());
831 key = prefix + "-backup-after";
832 node.setProperty(key, source.m_backupAfter.getNumItems());
834 for (int location = 0;
835 location < SyncSourceReport::ITEM_LOCATION_MAX;
838 state < SyncSourceReport::ITEM_STATE_MAX;
841 result < SyncSourceReport::ITEM_RESULT_MAX;
843 int intval = source.getItemStat(SyncSourceReport::ItemLocation(location),
844 SyncSourceReport::ItemState(state),
845 SyncSourceReport::ItemResult(result));
847 key = prefix + "-stat-" +
848 SyncSourceReport::StatTupleToString(SyncSourceReport::ItemLocation(location),
849 SyncSourceReport::ItemState(state),
850 SyncSourceReport::ItemResult(result));
851 node.setProperty(key, intval);
861 ConfigNode &operator >> (ConfigNode &node, SyncReport &report)
864 if (node.getProperty("start", ts)) {
867 if (node.getProperty("end", ts)) {
871 if (node.getProperty("status", status)) {
872 report.setStatus(static_cast<SyncMLStatus>(status));
875 if (node.getProperty("error", error)) {
876 report.setError(error);
879 ConfigNode::PropsType props;
880 node.readProperties(props);
881 BOOST_FOREACH(const ConfigNode::PropsType::value_type &prop, props) {
882 string key = prop.first;
883 if (boost::starts_with(key, "source-")) {
884 key.erase(0, strlen("source-"));
885 size_t off = key.find('-');
886 if (off != key.npos) {
887 string sourcename = key.substr(0, off);
888 boost::replace_all(sourcename, "_+", "-");
889 boost::replace_all(sourcename, "__", "_");
890 SyncSourceReport &source = report.getSyncSourceReport(sourcename);
891 key.erase(0, off + 1);
892 if (boost::starts_with(key, "stat-")) {
893 key.erase(0, strlen("stat-"));
894 SyncSourceReport::ItemLocation location;
895 SyncSourceReport::ItemState state;
896 SyncSourceReport::ItemResult result;
897 SyncSourceReport::StringToStatTuple(key, location, state, result);
898 stringstream in(prop.second);
901 source.setItemStat(location, state, result, intval);
902 } else if (key == "mode") {
903 source.recordFinalSyncMode(StringToSyncMode(prop.second));
904 } else if (key == "first") {
906 if (node.getProperty(prop.first, value)) {
907 source.recordFirstSync(value);
909 } else if (key == "resume") {
911 if (node.getProperty(prop.first, value)) {
912 source.recordResumeSync(value);
914 } else if (key == "status") {
916 if (node.getProperty(prop.first, value)) {
917 source.recordStatus(static_cast<SyncMLStatus>(value));
919 } else if (key == "virtualsource") {
920 source.recordVirtualSource(node.readProperty(prop.first));
921 } else if (key == "backup-before") {
923 if (node.getProperty(prop.first, value)) {
924 source.m_backupBefore.setNumItems(value);
926 } else if (key == "backup-after") {
928 if (node.getProperty(prop.first, value)) {
929 source.m_backupAfter.setNumItems(value);