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 GetLegacyMIMEType (const std::string &type, bool force) {
112 if (boost::iequals (type, "text/x-vcard") || boost::iequals (type, "text/x-vcard:2.1")) {
113 return "text/x-vcard";
114 } else if (boost::iequals (type, "text/vcard") ||boost::iequals (type, "text/vcard:3.0")) {
115 return force ? "text/vcard" : "text/x-vcard";
116 } else if (boost::iequals (type, "text/x-vcalendar") ||boost::iequals (type, "text/x-vcalendar:1.0")
117 ||boost::iequals (type, "text/x-calendar") || boost::iequals (type, "text/x-calendar:1.0")) {
118 return "text/x-vcalendar";
119 } else if (boost::iequals (type, "text/calendar") ||boost::iequals (type, "text/calendar:2.0")) {
120 return force ? "text/vcalendar" : "text/x-calendar";
121 } else if (boost::iequals (type, "text/plain") ||boost::iequals (type, "text/plain:1.0")) {
129 std::string Status2String(SyncMLStatus status)
135 local = status >= static_cast<int>(sysync::LOCAL_STATUS_CODE);
137 status <= static_cast<int>(sysync::LOCAL_STATUS_CODE_END)) {
138 code = status - static_cast<int>(sysync::LOCAL_STATUS_CODE);
148 case STATUS_NO_CONTENT:
149 error = "no content/end of data";
151 case STATUS_DATA_MERGED:
152 error = "data merged";
154 case STATUS_FORBIDDEN:
155 error = "access denied";
157 case STATUS_NOT_FOUND:
158 error = "object not found";
160 case STATUS_COMMAND_NOT_ALLOWED:
161 error = "operation not allowed";
163 case STATUS_ALREADY_EXISTS:
164 error = "object exists already";
167 error = "fatal error";
169 case STATUS_DATASTORE_FAILURE:
170 error = "database failure";
173 error = "out of space";
176 case STATUS_UNEXPECTED_SLOW_SYNC:
177 error = "unexpected slow sync";
180 case STATUS_PARTIAL_FAILURE:
181 error = "some changes could not be transferred";
184 case STATUS_PASSWORD_TIMEOUT:
185 error = "password request timed out";
188 case sysync::LOCERR_BADPROTO:
189 error = "bad or unknown protocol";
191 case sysync::LOCERR_SMLFATAL:
192 error = "fatal problem with SML";
194 case sysync::LOCERR_COMMOPEN:
195 error = "cannot open communication";
197 case sysync::LOCERR_SENDDATA:
198 error = "cannot send data";
200 case sysync::LOCERR_RECVDATA:
201 error = "cannot receive data";
203 case sysync::LOCERR_BADCONTENT:
204 error = "bad content in response";
206 case sysync::LOCERR_PROCESSMSG:
207 error = "SML (or SAN) error processing incoming message";
209 case sysync::LOCERR_COMMCLOSE:
210 error = "cannot close communication";
212 case sysync::LOCERR_AUTHFAIL:
213 error = "transport layer authorisation failed";
215 case sysync::LOCERR_CFGPARSE:
216 error = "error parsing config file";
218 case sysync::LOCERR_CFGREAD:
219 error = "error reading config file";
221 case sysync::LOCERR_NOCFG:
222 error = "no config found";
224 case sysync::LOCERR_NOCFGFILE:
225 error = "config file could not be found";
227 case sysync::LOCERR_EXPIRED:
230 case sysync::LOCERR_WRONGUSAGE:
233 case sysync::LOCERR_BADHANDLE:
234 error = "bad handle";
236 case sysync::LOCERR_USERABORT:
237 error = "aborted on behalf of user";
239 case sysync::LOCERR_BADREG:
240 error = "bad registration";
242 case sysync::LOCERR_LIMITED:
243 error = "limited trial version";
245 case sysync::LOCERR_TIMEOUT:
246 error = "connection timeout";
248 case sysync::LOCERR_CERT_EXPIRED:
249 error = "connection SSL certificate expired";
251 case sysync::LOCERR_CERT_INVALID:
252 error = "connection SSL certificate invalid";
254 case sysync::LOCERR_INCOMPLETE:
255 error = "incomplete sync session";
257 case sysync::LOCERR_RETRYMSG:
258 error = "retry sending message";
260 case sysync::LOCERR_OUTOFMEM:
261 error = "out of memory";
263 case sysync::LOCERR_NOCONN:
264 error = "no means to open a connection";
266 case sysync::LOCERR_CONN:
267 error = "connection cannot be established";
269 case sysync::LOCERR_ALREADY:
270 error = "element is already installed";
272 case sysync::LOCERR_TOONEW:
273 error = "this build is too new for this license";
275 case sysync::LOCERR_NOTIMP:
276 error = "function not implemented";
278 case sysync::LOCERR_WRONGPROD:
279 error = "this license code is valid, but not for this product";
281 case sysync::LOCERR_USERSUSPEND:
282 error = "explicitly suspended on behalf of user";
284 case sysync::LOCERR_TOOOLD:
285 error = "this build is too old for this SDK/plugin";
287 case sysync::LOCERR_UNKSUBSYSTEM:
288 error = "unknown subsystem";
290 case sysync::LOCERR_SESSIONRST:
291 error = "next message will be a session restart";
293 case sysync::LOCERR_LOCDBNOTRDY:
294 error = "local datastore is not ready";
296 case sysync::LOCERR_RESTART:
297 error = "session should be restarted from scratch";
299 case sysync::LOCERR_PIPECOMM:
300 error = "internal pipe communication problem";
302 case sysync::LOCERR_BUFTOOSMALL:
303 error = "buffer too small for requested value";
305 case sysync::LOCERR_TRUNCATED:
306 error = "value truncated to fit into field or buffer";
308 case sysync::LOCERR_BADPARAM:
309 error = "bad parameter";
311 case sysync::LOCERR_OUTOFRANGE:
312 error = "out of range";
314 case sysync::LOCERR_TRANSPFAIL:
315 error = "external transport failure";
317 case sysync::LOCERR_CLASSNOTREG:
318 error = "class not registered";
320 case sysync::LOCERR_IIDNOTREG:
321 error = "interface not registered";
323 case sysync::LOCERR_BADURL:
326 case sysync::LOCERR_SRVNOTFOUND:
327 error = "server not found";
334 string statusstr = StringPrintf("%s, status %d",
335 local ? "local" : "remote",
339 description = statusstr;
341 description = StringPrintf("%s (%s)",
350 const char * const locNames[] = { "local", "remote", NULL };
351 const char * const stateNames[] = { "added", "updated", "removed", "any", NULL };
352 const char * const resultNames[] = { "total", "reject", "match",
353 "conflict_server_won",
354 "conflict_client_won",
355 "conflict_duplicated",
360 int toIndex(const char * const names[],
361 const std::string &name) {
364 names[i] && name != names[i];
369 std::string toString(const char * const names[],
382 std::string SyncSourceReport::LocationToString(ItemLocation location) { return toString(locNames, location); }
383 SyncSourceReport::ItemLocation SyncSourceReport::StringToLocation(const std::string &location) { return static_cast<ItemLocation>(toIndex(locNames, location)); }
384 std::string SyncSourceReport::StateToString(ItemState state) { return toString(stateNames, state); }
385 SyncSourceReport::ItemState SyncSourceReport::StringToState(const std::string &state) { return static_cast<ItemState>(toIndex(stateNames, state)); }
386 std::string SyncSourceReport::ResultToString(ItemResult result) { return toString(resultNames, result); }
387 SyncSourceReport::ItemResult SyncSourceReport::StringToResult(const std::string &result) { return static_cast<ItemResult>(toIndex(resultNames, result)); }
389 std::string SyncSourceReport::StatTupleToString(ItemLocation location, ItemState state, ItemResult result)
391 return std::string("") +
392 LocationToString(location) + "-" +
393 StateToString(state) + "-" +
394 ResultToString(result);
396 void SyncSourceReport::StringToStatTuple(const std::string &str, ItemLocation &location, ItemState &state, ItemResult &result)
398 std::vector< std::string > tokens;
399 boost::split(tokens, str, boost::is_any_of("-"));
400 location = tokens.size() > 0 ? StringToLocation(tokens[0]) : ITEM_LOCATION_MAX;
401 state = tokens.size() > 1 ? StringToState(tokens[1]) : ITEM_STATE_MAX;
402 result = tokens.size() > 2 ? StringToResult(tokens[2]) : ITEM_RESULT_MAX;
405 bool SyncSourceReport::wasChanged(ItemLocation location)
407 for (int i = ITEM_ADDED; i < ITEM_ANY; i++) {
408 if (getItemStat(location, (ItemState)i, ITEM_TOTAL) > 0) {
416 std::ostream &operator << (std::ostream &out, const SyncReport &report)
418 report.prettyPrint(out, 0);
423 string fill(char sep, size_t width) {
425 res.resize(width - 1, sep);
428 string center(char sep, const string &str, size_t width) {
429 if (str.size() + 1 >= width) {
433 res.resize(width - 1, sep);
434 res.replace((width - 1 - str.size()) / 2, str.size(), str);
438 string right(char sep, const string &str, size_t width) {
439 if (str.size() + 1 >= width) {
443 res.resize(width - 1, sep);
444 res.replace(width - 2 - str.size(), str.size(), str);
448 string left(char sep, const string &str, size_t width) {
449 if (str.size() + 1 >= width) {
453 res.resize(width - 1, sep);
454 res.replace(1, str.size(), str);
459 // insert string at column if it fits, otherwise flush right
460 string align(char sep, const string &str, size_t width, size_t column) {
461 if (column + str.size() + 1 >= width) {
462 return right(sep, str, width);
465 res.resize(width - 1, sep);
466 res.replace(column, str.size(), str);
472 void SyncReport::prettyPrint(std::ostream &out, int flags) const
474 // table looks like this:
475 // +-------------------+-------------------------------+-------------------------------|-CON-+
476 // | | LOCAL | REMOTE | FLI |
477 // | Source | NEW | MOD | DEL | ERR | TOTAL | NEW | MOD | DEL | ERR | TOTAL | CTS |
478 // +-------------------+-----+-----+-----+-----+-------+-----+-----+-----+-----+-------+-----+
480 // Most of the columns can be turned on or off dynamically.
481 // Their width is calculated once (including right separators and spaces):
482 // | name_width |count_width| | |conflict_width|
483 // |client_width | server_width |
486 // name column is sized dynamically based on column header and actual names
487 size_t name_width = strlen("Source");
488 BOOST_FOREACH(const SyncReport::value_type &entry, *this) {
489 const std::string &name = entry.first;
490 if (name_width < name.size()) {
491 name_width = name.size();
494 name_width += 1; // separator
495 if (name_width < 20) {
496 // enough room for spaces
502 if (flags & WITH_TOTAL) {
505 if (!(flags & WITHOUT_REJECTS)) {
508 int client_width = (flags & WITHOUT_CLIENT) ? 0 :
509 num_counts * count_width;
510 int server_width = (flags & WITHOUT_SERVER) ? 0 :
511 num_counts * count_width;
512 int conflict_width = (flags & WITHOUT_CONFLICTS) ? 0 : 6;
513 int text_width = name_width + client_width + server_width + conflict_width;
515 if (text_width < 70) {
516 // enlarge name column to make room for long lines of text
517 name_width += 70 - text_width;
521 out << "+" << fill('-', name_width);
522 if (!(flags & WITHOUT_CLIENT)) {
523 out << '|' << center('-', "", client_width);
525 if (!(flags & WITHOUT_SERVER)) {
526 out << '|' << center('-', "", server_width);
528 if (!(flags & WITHOUT_CONFLICTS)) {
529 out << '|' << center('-', "CON", conflict_width);
533 if (!(flags & WITHOUT_REJECTS) || !(flags & WITHOUT_CONFLICTS)) {
534 out << "|" << fill(' ', name_width);
535 if (!(flags & WITHOUT_CLIENT)) {
536 out << '|' << center(' ', "LOCAL", client_width);
538 if (!(flags & WITHOUT_SERVER)) {
539 out << '|' << center(' ', "REMOTE", server_width);
541 if (!(flags & WITHOUT_CONFLICTS)) {
542 out << '|' << center(' ', "FLI", conflict_width);
547 out << '|' << right(' ', "Source", name_width);
548 if (!(flags & WITHOUT_CLIENT)) {
549 out << '|' << center(' ', "NEW", count_width);
550 out << '|' << center(' ', "MOD", count_width);
551 out << '|' << center(' ', "DEL", count_width);
552 if (!(flags & WITHOUT_REJECTS)) {
553 out << '|' << center(' ', "ERR", count_width);
555 if (flags & WITH_TOTAL) {
556 out << '|' << center(' ', "TOTAL", count_width);
559 if (!(flags & WITHOUT_SERVER)) {
560 out << '|' << center(' ', "NEW", count_width);
561 out << '|' << center(' ', "MOD", count_width);
562 out << '|' << center(' ', "DEL", count_width);
563 if (!(flags & WITHOUT_REJECTS)) {
564 out << '|' << center(' ', "ERR", count_width);
566 if (flags & WITH_TOTAL) {
567 out << '|' << center(' ', "TOTAL", count_width);
570 if (!(flags & WITHOUT_CONFLICTS)) {
571 out << '|' << center(' ', "CTS", conflict_width);
575 stringstream sepstream;
576 sepstream << '+' << fill('-', name_width);
577 if (!(flags & WITHOUT_CLIENT)) {
578 sepstream << '+' << fill('-', count_width);
579 sepstream << '+' << fill('-', count_width);
580 sepstream << '+' << fill('-', count_width);
581 if (!(flags & WITHOUT_REJECTS)) {
582 sepstream << '+' << fill('-', count_width);
584 if (flags & WITH_TOTAL) {
585 sepstream << '+' << fill('-', count_width);
588 if (!(flags & WITHOUT_SERVER)) {
589 sepstream << '+' << fill('-', count_width);
590 sepstream << '+' << fill('-', count_width);
591 sepstream << '+' << fill('-', count_width);
592 if (!(flags & WITHOUT_REJECTS)) {
593 sepstream << '+' << fill('-', count_width);
595 if (flags & WITH_TOTAL) {
596 sepstream << '+' << fill('-', count_width);
599 if (!(flags & WITHOUT_CONFLICTS)) {
600 sepstream << '+' << fill('-', conflict_width);
603 string sep = sepstream.str();
606 BOOST_FOREACH(const SyncReport::value_type &entry, *this) {
607 const std::string &name = entry.first;
608 const SyncSourceReport &source = entry.second;
609 out << '|' << right(' ', name, name_width);
610 ssize_t name_column = name_width - 2 - name.size();
611 if (name_column < 0) {
614 for (SyncSourceReport::ItemLocation location =
615 ((flags & WITHOUT_CLIENT) ? SyncSourceReport::ITEM_REMOTE : SyncSourceReport::ITEM_LOCAL);
616 location <= ((flags & WITHOUT_SERVER) ? SyncSourceReport::ITEM_LOCAL : SyncSourceReport::ITEM_REMOTE);
617 location = SyncSourceReport::ItemLocation(int(location) + 1)) {
618 for (SyncSourceReport::ItemState state = SyncSourceReport::ITEM_ADDED;
619 state <= SyncSourceReport::ITEM_REMOVED;
620 state = SyncSourceReport::ItemState(int(state) + 1)) {
622 count << source.getItemStat(location, state, SyncSourceReport::ITEM_TOTAL);
623 out << '|' << center(' ', count.str(), count_width);
625 if (!(flags & WITHOUT_REJECTS)) {
627 count << source.getItemStat(location,
628 SyncSourceReport::ITEM_ANY,
629 SyncSourceReport::ITEM_REJECT);
630 out << '|' << center(' ', count.str(), count_width);
632 if (flags & WITH_TOTAL) {
634 count << source.getItemStat(location,
635 SyncSourceReport::ITEM_ANY,
636 SyncSourceReport::ITEM_TOTAL);
637 out << '|' << center(' ', count.str(), count_width);
641 int total_conflicts = 0;
642 if (!(flags & WITHOUT_CONFLICTS)) {
644 source.getItemStat(SyncSourceReport::ITEM_REMOTE,
645 SyncSourceReport::ITEM_ANY,
646 SyncSourceReport::ITEM_CONFLICT_SERVER_WON) +
647 source.getItemStat(SyncSourceReport::ITEM_REMOTE,
648 SyncSourceReport::ITEM_ANY,
649 SyncSourceReport::ITEM_CONFLICT_CLIENT_WON) +
650 source.getItemStat(SyncSourceReport::ITEM_REMOTE,
651 SyncSourceReport::ITEM_ANY,
652 SyncSourceReport::ITEM_CONFLICT_DUPLICATED);
653 stringstream conflicts;
654 conflicts << total_conflicts;
655 out << '|' << center(' ', conflicts.str(), conflict_width);
659 std::stringstream line;
661 if (source.getFinalSyncMode() != SYNC_NONE ||
662 source.getItemStat(SyncSourceReport::ITEM_LOCAL,
663 SyncSourceReport::ITEM_ANY,
664 SyncSourceReport::ITEM_SENT_BYTES) ||
665 source.getItemStat(SyncSourceReport::ITEM_LOCAL,
666 SyncSourceReport::ITEM_ANY,
667 SyncSourceReport::ITEM_RECEIVED_BYTES)) {
669 PrettyPrintSyncMode(source.getFinalSyncMode()) << ", " <<
670 source.getItemStat(SyncSourceReport::ITEM_LOCAL,
671 SyncSourceReport::ITEM_ANY,
672 SyncSourceReport::ITEM_SENT_BYTES) / 1024 <<
673 " KB sent by client, " <<
674 source.getItemStat(SyncSourceReport::ITEM_LOCAL,
675 SyncSourceReport::ITEM_ANY,
676 SyncSourceReport::ITEM_RECEIVED_BYTES) / 1024 <<
678 out << '|' << align(' ', line.str(), text_width, name_column) << "|\n";
681 if (total_conflicts > 0) {
682 for (SyncSourceReport::ItemResult result = SyncSourceReport::ITEM_CONFLICT_SERVER_WON;
683 result <= SyncSourceReport::ITEM_CONFLICT_DUPLICATED;
684 result = SyncSourceReport::ItemResult(int(result) + 1)) {
686 if ((count = source.getItemStat(SyncSourceReport::ITEM_REMOTE,
687 SyncSourceReport::ITEM_ANY,
688 result)) != 0 || true) {
689 std::stringstream line;
690 line << count << " " <<
691 (result == SyncSourceReport::ITEM_CONFLICT_SERVER_WON ? "client item(s) discarded" :
692 result == SyncSourceReport::ITEM_CONFLICT_CLIENT_WON ? "server item(s) discarded" :
693 "item(s) duplicated");
695 out << '|' << align(' ', line.str(), text_width, name_column) << "|\n";
700 int total_matched = source.getItemStat(SyncSourceReport::ITEM_REMOTE,
701 SyncSourceReport::ITEM_ANY,
702 SyncSourceReport::ITEM_MATCH);
705 line << total_matched << " item(s) matched";
706 out << '|' << align(' ', line.str(), text_width, name_column)
710 if (source.m_backupBefore.isAvailable() ||
711 source.m_backupAfter.isAvailable()) {
712 std::stringstream backup;
713 backup << "item(s) in database backup: ";
714 if (source.m_backupBefore.isAvailable()) {
715 backup << source.m_backupBefore.getNumItems() << " before sync, ";
717 backup << "no backup before sync, ";
719 if (source.m_backupAfter.isAvailable()) {
720 backup << source.m_backupAfter.getNumItems() << " after it";
722 backup << "no backup after it";
724 out << '|' << align(' ', backup.str(), text_width, name_column) << "|\n";
726 if (source.getStatus()) {
727 out << '|' << align(' ',
728 Status2String(source.getStatus()),
729 text_width, name_column) << "|\n";
735 out << '|' << center(' ', formatSyncTimes(), text_width) << "|\n";
738 out << '|' << center(' ',
739 getStatus() != STATUS_HTTP_OK ?
740 Status2String(getStatus()) :
741 "synchronization completed successfully",
745 if (getStatus() || getStart()) {
748 if (!getError().empty()) {
749 out << "First ERROR encountered: " << getError() << endl;
753 std::string SyncReport::formatSyncTimes() const
755 std::stringstream out;
756 time_t duration = m_end - m_start;
763 strftime(buffer, sizeof(buffer), "%c", localtime(&m_start));
766 out << ", unknown duration (crashed?!)";
768 out << ", duration " << duration / 60 << ":"
769 << std::setw(2) << std::setfill('0') << duration % 60
776 std::string SyncReport::slowSyncExplanation(const std::string &peer,
777 const std::set<std::string> &sources)
779 if (sources.empty()) {
783 string sourceparam = boost::join(sources, " ");
784 std::string explanation =
785 StringPrintf("Doing a slow synchronization may lead to duplicated items or\n"
786 "lost data when the server merges items incorrectly. Choosing\n"
787 "a different synchronization mode may be the better alternative.\n"
788 "Restart synchronization of affected source(s) with one of the\n"
789 "following sync modes to recover from this problem:\n"
790 " slow, refresh-from-server, refresh-from-client\n\n"
791 "Analyzing the current state:\n"
792 " syncevolution --status %s %s\n\n"
793 "Running with one of the three modes:\n"
794 " syncevolution --sync [slow|refresh-from-server|refresh-from-client] %s %s\n",
795 peer.c_str(), sourceparam.c_str(),
796 peer.c_str(), sourceparam.c_str());
800 std::string SyncReport::slowSyncExplanation(const std::string &peer) const
802 std::set<std::string> sources;
803 BOOST_FOREACH(const SyncReport::value_type &entry, *this) {
804 const std::string &name = entry.first;
805 const SyncSourceReport &source = entry.second;
806 if (source.getStatus() == STATUS_UNEXPECTED_SLOW_SYNC) {
807 string virtualsource = source.getVirtualSource();
808 sources.insert(virtualsource.empty() ?
813 return slowSyncExplanation(peer, sources);
816 ConfigNode &operator << (ConfigNode &node, const SyncReport &report)
818 node.setProperty("start", static_cast<long>(report.getStart()));
819 node.setProperty("end", static_cast<long>(report.getEnd()));
820 node.setProperty("status", static_cast<int>(report.getStatus()));
821 string error = report.getError();
822 if (!error.empty()) {
823 node.setProperty("error", error);
825 node.removeProperty("error");
828 BOOST_FOREACH(const SyncReport::value_type &entry, report) {
829 const std::string &name = entry.first;
830 const SyncSourceReport &source = entry.second;
832 string prefix = name;
833 boost::replace_all(prefix, "_", "__");
834 boost::replace_all(prefix, "-", "_+");
835 prefix = "source-" + prefix;
838 key = prefix + "-mode";
839 node.setProperty(key, PrettyPrintSyncMode(source.getFinalSyncMode()));
840 key = prefix + "-first";
841 node.setProperty(key, source.isFirstSync());
842 key = prefix + "-resume";
843 node.setProperty(key, source.isResumeSync());
844 key = prefix + "-status";
845 node.setProperty(key, static_cast<long>(source.getStatus()));
846 string virtualsource = source.getVirtualSource();
847 if (!virtualsource.empty()) {
848 key = prefix + "-virtualsource";
849 node.setProperty(key, virtualsource);
851 key = prefix + "-backup-before";
852 node.setProperty(key, source.m_backupBefore.getNumItems());
853 key = prefix + "-backup-after";
854 node.setProperty(key, source.m_backupAfter.getNumItems());
856 for (int location = 0;
857 location < SyncSourceReport::ITEM_LOCATION_MAX;
860 state < SyncSourceReport::ITEM_STATE_MAX;
863 result < SyncSourceReport::ITEM_RESULT_MAX;
865 int intval = source.getItemStat(SyncSourceReport::ItemLocation(location),
866 SyncSourceReport::ItemState(state),
867 SyncSourceReport::ItemResult(result));
869 key = prefix + "-stat-" +
870 SyncSourceReport::StatTupleToString(SyncSourceReport::ItemLocation(location),
871 SyncSourceReport::ItemState(state),
872 SyncSourceReport::ItemResult(result));
873 node.setProperty(key, intval);
883 ConfigNode &operator >> (ConfigNode &node, SyncReport &report)
886 if (node.getProperty("start", ts)) {
889 if (node.getProperty("end", ts)) {
893 if (node.getProperty("status", status)) {
894 report.setStatus(static_cast<SyncMLStatus>(status));
897 if (node.getProperty("error", error)) {
898 report.setError(error);
901 ConfigNode::PropsType props;
902 node.readProperties(props);
903 BOOST_FOREACH(const ConfigNode::PropsType::value_type &prop, props) {
904 string key = prop.first;
905 if (boost::starts_with(key, "source-")) {
906 key.erase(0, strlen("source-"));
907 size_t off = key.find('-');
908 if (off != key.npos) {
909 string sourcename = key.substr(0, off);
910 boost::replace_all(sourcename, "_+", "-");
911 boost::replace_all(sourcename, "__", "_");
912 SyncSourceReport &source = report.getSyncSourceReport(sourcename);
913 key.erase(0, off + 1);
914 if (boost::starts_with(key, "stat-")) {
915 key.erase(0, strlen("stat-"));
916 SyncSourceReport::ItemLocation location;
917 SyncSourceReport::ItemState state;
918 SyncSourceReport::ItemResult result;
919 SyncSourceReport::StringToStatTuple(key, location, state, result);
920 stringstream in(prop.second);
923 source.setItemStat(location, state, result, intval);
924 } else if (key == "mode") {
925 source.recordFinalSyncMode(StringToSyncMode(prop.second));
926 } else if (key == "first") {
928 if (node.getProperty(prop.first, value)) {
929 source.recordFirstSync(value);
931 } else if (key == "resume") {
933 if (node.getProperty(prop.first, value)) {
934 source.recordResumeSync(value);
936 } else if (key == "status") {
938 if (node.getProperty(prop.first, value)) {
939 source.recordStatus(static_cast<SyncMLStatus>(value));
941 } else if (key == "virtualsource") {
942 source.recordVirtualSource(node.readProperty(prop.first));
943 } else if (key == "backup-before") {
945 if (node.getProperty(prop.first, value)) {
946 source.m_backupBefore.setNumItems(value);
948 } else if (key == "backup-after") {
950 if (node.getProperty(prop.first, value)) {
951 source.m_backupAfter.setNumItems(value);