/* * xml - A plugin for xml objects for the opensync framework * Copyright (C) 2004-2005 Armin Bauer * * 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) any later version. * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "opensync/opensync.h" #include "opensync/opensync_time_internals.h" #include "xml-support.h" #include static char *osxml_prepare_time(const char *content, xmlNode *node) { osync_trace(TRACE_ENTRY, "%s(%s, %p)", __func__, content, node); int tzoffset = 0; char *time = NULL; struct tm *ttm = NULL; if (!osync_time_isutc(content)) { time = osync_time_tzlocal2utc(node, (char *) node->name); if (!time) { ttm = osync_time_vtime2tm(content); tzoffset = osync_time_timezone_diff(ttm); time = osync_time_vtime2utc(content, tzoffset); g_free(ttm); } } if (!time) time = g_strdup(content); osync_trace(TRACE_EXIT, "%s: %s", __func__, time); return time; } static osync_bool osxml_compare_time(xmlNode *leftnode, xmlNode *rightnode) { osync_trace(TRACE_ENTRY, "%s(%s(%p), %s(%p))", __func__, leftnode->name, leftnode, rightnode->name, rightnode); int ret = 0; char *left = NULL, *right = NULL; char *leftcontent = osxml_find_node(leftnode, "Content"); char *rightcontent = osxml_find_node(rightnode, "Content"); osync_trace(TRACE_SENSITIVE, "time compare - left: %s right: %s", leftcontent, rightcontent); if (osync_time_isutc(leftcontent) != osync_time_isutc(rightcontent)) { left = osxml_prepare_time(leftcontent, leftnode); right = osxml_prepare_time(rightcontent, rightnode); g_free(leftcontent); g_free(rightcontent); osync_trace(TRACE_SENSITIVE, "AFTER convert - left: %s right: %s", left, right); } else { left = leftcontent; right = rightcontent; } ret = strcmp(left, right); g_free(left); g_free(right); if (ret) { osync_trace(TRACE_EXIT, "%s: FALSE", __func__); return FALSE; } osync_trace(TRACE_EXIT, "%s: TRUE", __func__); return TRUE; } static osync_bool osxml_compare_node(xmlNode *leftnode, xmlNode *rightnode) { osync_trace(TRACE_ENTRY, "%s(%p:%s, %p:%s)", __func__, leftnode, leftnode->name, rightnode, rightnode->name); if (strcmp((char*)leftnode->name, (char*)rightnode->name)) { osync_trace(TRACE_EXIT, "%s: FALSE: Different Name", __func__); return FALSE; } leftnode = leftnode->children; rightnode = rightnode->children; xmlNode *rightstartnode = rightnode; if (!leftnode && !rightnode) { osync_trace(TRACE_EXIT, "%s: TRUE. Both 0", __func__); return TRUE; } if (!leftnode || !rightnode) { osync_trace(TRACE_EXIT, "%s: FALSE. One 0", __func__); return FALSE; } do { if (!strcmp("UnknownParam", (char*)leftnode->name)) continue; if (!strcmp("Order", (char*)leftnode->name)) continue; rightnode = rightstartnode; char *leftcontent = (char*)xmlNodeGetContent(leftnode); do { if (!strcmp("UnknownParam", (char*)rightnode->name)) continue; osync_trace(TRACE_INTERNAL, "leftnode %s, rightnode %s", leftnode->name, rightnode->name); /* Compare only nodes with same name. Skip if * the names are different */ if (xmlStrcmp(leftnode->name, rightnode->name)) continue; char *rightcontent = (char*)xmlNodeGetContent(rightnode); osync_trace(TRACE_SENSITIVE, "leftcontent %s, rightcontent %s\n", leftcontent, rightcontent); if (leftcontent == rightcontent) { g_free(rightcontent); goto next; } /* We compare the striped content to work around bugs in * applications like evo2 which always strip the content * and would therefore cause conflicts. This change should not break * anything since it does not touch the actual content */ char *strip_right = g_strstrip(g_strdup(rightcontent)); char *strip_left = g_strstrip(g_strdup(leftcontent)); if (!strcmp(strip_left, strip_right)) { g_free(strip_right); g_free(strip_left); g_free(rightcontent); goto next; } g_free(strip_right); g_free(strip_left); if (!leftcontent || !rightcontent) { osync_trace(TRACE_EXIT, "%s: One is empty", __func__); return FALSE; } /* Workaround for palm-sync. palm-sync is not able to set a correct Completed date-timestamp so ignore value (objtype: todo) */ if (!strcmp("Completed", (char*)rightnode->name) && !strcmp("Completed",(char*)leftnode->name)) { if ((leftcontent && rightcontent) || (!leftcontent && !rightcontent)) { osync_trace(TRACE_INTERNAL, "PALM-SYNC workaround active!"); g_free(rightcontent); goto next; } } g_free(rightcontent); if ((!strcmp("DateStarted", (char*)rightnode->name) && !strcmp("DateStarted", (char*)leftnode->name)) || (!strcmp("DateEnd", (char*)rightnode->name) && !strcmp("DateEnd", (char*)leftnode->name))) { if (osxml_compare_time(leftnode, rightnode)) goto next; } /* compare child nodes again .... */ if (rightnode->type == XML_ELEMENT_NODE && osxml_compare_node(rightnode, leftnode)) goto next; } while ((rightnode = rightnode->next)); osync_trace(TRACE_EXIT, "%s: Could not match one", __func__); g_free(leftcontent); return FALSE; next:; g_free(leftcontent); } while ((leftnode = leftnode->next)); osync_trace(TRACE_EXIT, "%s: TRUE", __func__); return TRUE; } OSyncConvCmpResult osxml_compare(xmlDoc *leftinpdoc, xmlDoc *rightinpdoc, OSyncXMLScore *scores, int default_score, int treshold) { osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, leftinpdoc, rightinpdoc, scores); int z = 0, i = 0, n = 0; int res_score = 0; xmlDoc *leftdoc = xmlCopyDoc(leftinpdoc, TRUE); xmlDoc *rightdoc = xmlCopyDoc(rightinpdoc, TRUE); osync_trace(TRACE_INTERNAL, "Comparing given score list"); while (scores && scores[z].path) { OSyncXMLScore *score = &scores[z]; z++; xmlXPathObject *leftxobj = osxml_get_nodeset(leftdoc, score->path); xmlXPathObject *rightxobj = osxml_get_nodeset(rightdoc, score->path); xmlNodeSet *lnodes = leftxobj->nodesetval; xmlNodeSet *rnodes = rightxobj->nodesetval; int lsize = (lnodes) ? lnodes->nodeNr : 0; int rsize = (rnodes) ? rnodes->nodeNr : 0; osync_trace(TRACE_INTERNAL, "parsing next path %s", score->path); if (!score->value) { for (i = 0; i < lsize; i++) { xmlUnlinkNode(lnodes->nodeTab[i]); xmlFreeNode(lnodes->nodeTab[i]); lnodes->nodeTab[i] = NULL; } for (n = 0; n < rsize; n++) { xmlUnlinkNode(rnodes->nodeTab[n]); xmlFreeNode(rnodes->nodeTab[n]); rnodes->nodeTab[n] = NULL; } } else { for (i = 0; i < lsize; i++) { if (!lnodes->nodeTab[i]) continue; for (n = 0; n < rsize; n++) { if (!rnodes->nodeTab[n]) continue; osync_trace(TRACE_INTERNAL, "cmp %i:%s (leftcontent), %i:%s (rightcontent)", i, lnodes->nodeTab[i]->name, n, rnodes->nodeTab[n]->name); osync_trace(TRACE_SENSITIVE, "cmp %i:%s (%s), %i:%s (%s)\n", i, lnodes->nodeTab[i]->name, osxml_find_node(lnodes->nodeTab[i], "Content"), n, rnodes->nodeTab[n]->name, osxml_find_node(rnodes->nodeTab[n], "Content")); /* if ((!strcmp("DateStarted", lnodes->nodeTab[i]->name) && !strcmp("DateStarted", rnodes->nodeTab[n]->name)) || (!strcmp("DateEnd", lnodes->nodeTab[i]->name) && !strcmp("DateEnd", rnodes->nodeTab[n]->name))) { */ if (osxml_compare_node(lnodes->nodeTab[i], rnodes->nodeTab[n])) { osync_trace(TRACE_INTERNAL, "Adding %i for %s", score->value, score->path); res_score += score->value; xmlUnlinkNode(lnodes->nodeTab[i]); xmlFreeNode(lnodes->nodeTab[i]); lnodes->nodeTab[i] = NULL; xmlUnlinkNode(rnodes->nodeTab[n]); xmlFreeNode(rnodes->nodeTab[n]); rnodes->nodeTab[n] = NULL; goto next; } } osync_trace(TRACE_INTERNAL, "Subtracting %i for %s", score->value, score->path); res_score -= score->value; next:; } for(i = 0; i < rsize; i++) { if (!rnodes->nodeTab[i]) continue; res_score -= score->value; } } xmlXPathFreeObject(leftxobj); xmlXPathFreeObject(rightxobj); } xmlXPathObject *leftxobj = osxml_get_nodeset(leftdoc, "/*/*"); xmlXPathObject *rightxobj = osxml_get_nodeset(rightdoc, "/*/*"); xmlNodeSet *lnodes = leftxobj->nodesetval; xmlNodeSet *rnodes = rightxobj->nodesetval; // Check if nodeTab actually exists (for example vnote stuff would crash otherwise...) if (lnodes->nodeTab && rnodes->nodeTab) { // WORKAROUND - FIXME // if nodeTab[0] is an Event or Todo we need a new node structure (/*/*/*) if ((!strcmp((char*)lnodes->nodeTab[0]->name, "Event") && \ !strcmp((char*)rnodes->nodeTab[0]->name, "Event")) || \ (!strcmp((char*)lnodes->nodeTab[0]->name, "Todo") && \ !strcmp((char*)rnodes->nodeTab[0]->name, "Todo"))) { xmlXPathFreeObject(leftxobj); xmlXPathFreeObject(rightxobj); leftxobj = osxml_get_nodeset(leftdoc, "/*/*/*"); rightxobj = osxml_get_nodeset(rightdoc, "/*/*/*"); lnodes = leftxobj->nodesetval; rnodes = rightxobj->nodesetval; } } int lsize = (lnodes) ? lnodes->nodeNr : 0; int rsize = (rnodes) ? rnodes->nodeNr : 0; osync_trace(TRACE_INTERNAL, "Comparing remaining list"); osync_bool same = TRUE; for(i = 0; i < lsize; i++) { for (n = 0; n < rsize; n++) { if (!rnodes->nodeTab[n]) continue; osync_trace(TRACE_INTERNAL, "cmp %i:%s (leftcontent), %i:%s (rightcontent)", i, lnodes->nodeTab[i]->name, n, rnodes->nodeTab[n]->name); osync_trace(TRACE_SENSITIVE, "cmp %i:%s (%s), %i:%s (%s)\n", i, lnodes->nodeTab[i]->name, osxml_find_node(lnodes->nodeTab[i], "Content"), n, rnodes->nodeTab[n]->name, osxml_find_node(rnodes->nodeTab[n], "Content")); if ((!strcmp("DateStarted", (char *) lnodes->nodeTab[i]->name) && !strcmp("DateStarted", (char *) rnodes->nodeTab[n]->name)) || (!strcmp("DateEnd", (char *) lnodes->nodeTab[i]->name) && !strcmp("DateEnd", (char *) rnodes->nodeTab[n]->name))) { if (osxml_compare_time(lnodes->nodeTab[i], rnodes->nodeTab[n])) { xmlUnlinkNode(lnodes->nodeTab[i]); xmlFreeNode(lnodes->nodeTab[i]); lnodes->nodeTab[i] = NULL; xmlUnlinkNode(rnodes->nodeTab[n]); xmlFreeNode(rnodes->nodeTab[n]); rnodes->nodeTab[n] = NULL; osync_trace(TRACE_INTERNAL, "Adding %i", default_score); res_score += default_score; goto next2; } } if (osxml_compare_node(lnodes->nodeTab[i], rnodes->nodeTab[n])) { xmlUnlinkNode(lnodes->nodeTab[i]); xmlFreeNode(lnodes->nodeTab[i]); lnodes->nodeTab[i] = NULL; xmlUnlinkNode(rnodes->nodeTab[n]); xmlFreeNode(rnodes->nodeTab[n]); rnodes->nodeTab[n] = NULL; osync_trace(TRACE_INTERNAL, "Adding %i", default_score); res_score += default_score; goto next2; } } osync_trace(TRACE_INTERNAL, "Subtracting %i", default_score); res_score -= default_score; // XXX Find a better way to workaroudn the problem of ignoring without unlinking nodes. if (!strcmp("Timezone", (char *) lnodes->nodeTab[i]->name)) osync_trace(TRACE_INTERNAL, "Workaround for Timezone field. We ignore it but don't unlink it from XML"); else same = FALSE; //goto out; next2:; } for(i = 0; i < lsize; i++) { if (!lnodes->nodeTab[i]) continue; osync_trace(TRACE_INTERNAL, "left remaining: %s", lnodes->nodeTab[i]->name); // XXX Find a better way to workaroudn the problem of ignoring without unlinking nodes. if (!strcmp("Timezone", (char *) lnodes->nodeTab[i]->name)) osync_trace(TRACE_INTERNAL, "Workaround for Timezone field. We ignore it but don't unlink it from XML"); else same = FALSE; goto out; } for(i = 0; i < rsize; i++) { if (!rnodes->nodeTab[i]) continue; osync_trace(TRACE_INTERNAL, "right remaining: %s", rnodes->nodeTab[i]->name); // XXX Find a better way to workaroudn the problem of ignoring without unlinking nodes. if (!strcmp("Timezone", (char *) rnodes->nodeTab[i]->name)) osync_trace(TRACE_INTERNAL, "Workaround for Timezone field. We ignore it but don't unlink it from XML"); else same = FALSE; goto out; } out: xmlXPathFreeObject(leftxobj); xmlXPathFreeObject(rightxobj); xmlFreeDoc(leftdoc); xmlFreeDoc(rightdoc); osync_trace(TRACE_INTERNAL, "Result is: %i, Treshold is: %i", res_score, treshold); if (same) { osync_trace(TRACE_EXIT, "%s: SAME", __func__); return CONV_DATA_SAME; } if (res_score >= treshold) { osync_trace(TRACE_EXIT, "%s: SIMILAR", __func__); return CONV_DATA_SIMILAR; } osync_trace(TRACE_EXIT, "%s: MISMATCH", __func__); return CONV_DATA_MISMATCH; }