/* * libopensync - A synchronization framework * Copyright (C) 2006 NetNix Finland Ltd * * 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 * * Author: Daniel Friedrich * */ #include "opensync.h" #include "opensync_internals.h" #include "opensync-merger.h" #include "opensync-merger_internals.h" /** * @defgroup OSyncXMLFieldPrivateAPI OpenSync XMLField Internals * @ingroup OSyncPrivate * @brief The private part of the OSyncXMLField * */ /*@{*/ /** * @brief Creates a new xmlfield object which will be added to the end of xmlfields of the xmlformat object. * The returned object will be freed with the xmlformat object. * @param xmlformat The pointer to a xmlformat object * @param node The node must be already inserted at the end of childs of the xmlDoc root element * @param error The error which will hold the info in case of an error * @return The pointer to the newly allocated xmlfield object or NULL in case of error */ OSyncXMLField *_osync_xmlfield_new(OSyncXMLFormat *xmlformat, xmlNodePtr node, OSyncError **error) { OSyncXMLField *xmlfield = osync_try_malloc0(sizeof(OSyncXMLField), error); if(!xmlfield) { osync_trace(TRACE_ERROR, "%s: %s" , __func__, osync_error_print(error)); return NULL; } xmlfield->next = NULL; xmlfield->node = node; xmlfield->prev = xmlformat->last_child; node->_private = xmlfield; if(!xmlformat->first_child) xmlformat->first_child = xmlfield; if(xmlformat->last_child) xmlformat->last_child->next = xmlfield; xmlformat->last_child = xmlfield; xmlformat->child_count++; return xmlfield; } /** * @brief Frees a already unlinked xmlfield object * @param xmlfield The pointer to a xmlfield object */ void _osync_xmlfield_free(OSyncXMLField *xmlfield) { osync_assert(xmlfield); xmlFreeNode(xmlfield->node); g_free(xmlfield); } /** * @brief Unlink a xmlfield object * @param xmlfield The pointer to a xmlfield object */ void _osync_xmlfield_unlink(OSyncXMLField *xmlfield) { osync_assert(xmlfield); xmlUnlinkNode(xmlfield->node); if(!xmlfield->prev) ((OSyncXMLFormat *)xmlfield->node->doc->_private)->first_child = xmlfield->next; if(xmlfield->prev) xmlfield->prev->next = xmlfield->next; if(xmlfield->next) xmlfield->next->prev = xmlfield->prev; xmlfield->next = NULL; xmlfield->prev = NULL; ((OSyncXMLFormat *)xmlfield->node->doc->_private)->child_count--; } /** * @brief Compare the names of two xmlfields * @param xmlfield1 The pointer to a xmlfield object * @param xmlfield2 The pointer to a xmlfield object */ int _osync_xmlfield_compare_stdlib(const void *xmlfield1, const void *xmlfield2) { return strcmp(osync_xmlfield_get_name(*(OSyncXMLField **)xmlfield1), osync_xmlfield_get_name(*(OSyncXMLField **)xmlfield2)); } /*@}*/ /** * @defgroup OSyncXMLFieldAPI OpenSync XMLField * @ingroup OSyncPublic * @brief The public part of the OSyncXMLField * */ /*@{*/ /** * @brief Creates a new xmlfield object which will be added to the end of xmlfields of the xmlformat object. * The returned object will be freed with the xmlformat object. * @param xmlformat The pointer to a xmlformat object * @param name The name of the xmlfield * @param error The error which will hold the info in case of an error * @return The pointer to the newly allocated xmlformat object or NULL in case of error */ OSyncXMLField *osync_xmlfield_new(OSyncXMLFormat *xmlformat, const char *name, OSyncError **error) { osync_trace(TRACE_ENTRY, "%s(%p, %s, %p)", __func__, xmlformat, name, error); osync_assert(xmlformat); osync_assert(name); xmlNodePtr node = xmlNewTextChild(xmlDocGetRootElement(xmlformat->doc), NULL, BAD_CAST name, NULL); OSyncXMLField *xmlfield = _osync_xmlfield_new(xmlformat, node, error); if(!xmlfield) { xmlUnlinkNode(node); xmlFreeNode(node); osync_trace(TRACE_EXIT_ERROR, "%s: %s" , __func__, osync_error_print(error)); return NULL; } // XMLFormat entry got added - not sure if it is still sorted xmlformat->sorted = FALSE; osync_trace(TRACE_EXIT, "%s: %p", __func__, xmlfield); return xmlfield; } /** * @brief Unlink a xmlfield from its context and frees it * @param xmlfield The pointer to a xmlfield object */ void osync_xmlfield_delete(OSyncXMLField *xmlfield) { osync_assert(xmlfield); _osync_xmlfield_unlink(xmlfield); _osync_xmlfield_free(xmlfield); } /** * @brief Links a xmlfield object from a xmlformat object before a other xmlfield object of a other xmlformat object * @param xmlfield The pointer to a xmlfield object * @param to_link The pointer to a xmlfield object */ void osync_xmlfield_adopt_xmlfield_before_field(OSyncXMLField *xmlfield, OSyncXMLField *to_link) { osync_assert(xmlfield); osync_assert(to_link); _osync_xmlfield_unlink(to_link); xmlDOMWrapAdoptNode(NULL, to_link->node->doc, to_link->node, xmlfield->node->doc, xmlfield->node, 0); xmlAddPrevSibling(xmlfield->node, to_link->node); to_link->next = xmlfield; to_link->prev = xmlfield->prev; if(xmlfield->prev) xmlfield->prev->next = to_link; else ((OSyncXMLFormat *)xmlfield->node->doc->_private)->first_child = to_link; xmlfield->prev = to_link; ((OSyncXMLFormat *)xmlfield->node->doc->_private)->child_count++; } /** * @brief Links a xmlfield object from a xmlformat object after a other xmlfield object of a other xmlformat object * @param xmlfield The pointer to a xmlfield object * @param to_link The pointer to a xmlfield object */ void osync_xmlfield_adopt_xmlfield_after_field(OSyncXMLField *xmlfield, OSyncXMLField *to_link) { osync_assert(xmlfield); osync_assert(to_link); _osync_xmlfield_unlink(to_link); xmlDOMWrapAdoptNode(NULL, to_link->node->doc, to_link->node, xmlfield->node->doc, xmlfield->node, 0); xmlAddNextSibling(xmlfield->node, to_link->node); to_link->next = xmlfield->next; to_link->prev = xmlfield; if(xmlfield->next) xmlfield->next->prev = to_link; else ((OSyncXMLFormat *)xmlfield->node->doc->_private)->last_child = to_link; xmlfield->next = to_link; ((OSyncXMLFormat *)xmlfield->node->doc->_private)->child_count++; } /** * @brief Get the name of a xmlfield * @param xmlfield The pointer to a xmlfield object * @return The name of the xmlfield */ const char *osync_xmlfield_get_name(OSyncXMLField *xmlfield) { osync_assert(xmlfield); return (const char *)xmlfield->node->name; } /** * @brief Get the next xmlfield * @param xmlfield The pointer to a xmlfield object * @return The pointer to the next xmlfield or NULL if there is no more xmlfield */ OSyncXMLField *osync_xmlfield_get_next(OSyncXMLField *xmlfield) { osync_assert(xmlfield); return xmlfield->next; } /** * @brief Get the value of a attribute of a xmlfield * @param xmlfield The pointer to a xmlfield object * @param attr The name of the attribute * @return The value of the attribute of the xmlfield or NULL in case of error */ const char *osync_xmlfield_get_attr(OSyncXMLField *xmlfield, const char *attr) { osync_assert(xmlfield); osync_assert(attr); xmlAttrPtr prop; prop = xmlHasProp(xmlfield->node, BAD_CAST attr); if(prop == NULL) return NULL; return (const char *)osxml_attr_get_content(prop); // return (const char *)prop->children->content; // return (const char *)xmlGetProp(xmlfield->node, BAD_CAST attr); } /** * @brief Set the attribute name and the attribute value of a xmlfield * @param xmlfield The pointer to a xmlfield object * @param attr The name of the attribute * @param value The value of the attribute */ void osync_xmlfield_set_attr(OSyncXMLField *xmlfield, const char *attr, const char *value) { osync_assert(xmlfield); osync_assert(attr); osync_assert(value); xmlSetProp(xmlfield->node, BAD_CAST attr, BAD_CAST value); } /** * @brief Get the count of attributes of a xmlfield * @param xmlfield The pointer to a xmlfield object * @return The count of attributes of the xmlfield */ int osync_xmlfield_get_attr_count(OSyncXMLField *xmlfield) { osync_assert(xmlfield); int count; xmlAttrPtr attr = xmlfield->node->properties; for(count=0; attr != NULL; count++) attr = attr->next; return count; } /** * @brief Get the name of the nth attribute of a xmlfield * @param xmlfield The pointer to a xmlfield object * @param nth The number of the attribute * @return The name of the nth attribute of the xmlfield or NULL in case of error */ const char *osync_xmlfield_get_nth_attr_name(OSyncXMLField *xmlfield, int nth) { osync_assert(xmlfield); int count; xmlAttrPtr attr = xmlfield->node->properties; for(count=0; attr != NULL; count++) { if(count == nth) return (const char *)attr->name; attr = attr->next; } return NULL; } /** * @brief Get the value of the nth attribute of a xmlfield * @param xmlfield The pointer to a xmlfield object * @param nth The number of the attribute * @return The value of the nth attribute of the xmlfield or NULL in case of error */ const char *osync_xmlfield_get_nth_attr_value(OSyncXMLField *xmlfield, int nth) { osync_assert(xmlfield); int count; xmlAttrPtr attr = xmlfield->node->properties; for(count=0; attr != NULL; count++) { if(count == nth) return (const char *)osxml_attr_get_content(attr); // return (const char *)attr->children->content; // return (const char *)xmlNodeGetContent((xmlNodePtr)attr); attr = attr->next; } return NULL; } /** * @brief Get the value of a key/value pair of a xmlfield * @param xmlfield The pointer to a xmlfield object * @param key The key of the key/value pair * @return The value of the key/value pair of the xmlfield or NULL in case of error */ const char *osync_xmlfield_get_key_value(OSyncXMLField *xmlfield, const char *key) { osync_assert(xmlfield); osync_assert(key); xmlNodePtr cur = xmlfield->node->children; for(; cur != NULL; cur = cur->next) { if(!xmlStrcmp(cur->name, BAD_CAST key)) return (const char *)osxml_node_get_content(cur); // return (const char *)cur->children->content; // return (const char *)xmlNodeGetContent(cur); } return NULL; } /** * @brief Set the key/value pair of a xmlfield * @param xmlfield The pointer to a xmlfield object * @param key The key of the key/value pair * @param value The value of the key/value pair */ void osync_xmlfield_set_key_value(OSyncXMLField *xmlfield, const char *key, const char *value) { osync_assert(xmlfield); osync_assert(key); //If value is empty (this happen of for exmaple not full filled address field) just skip the argument. //TODO: it should be parsed a empty string instead of null so we can revert this patch //osync_assert(value); if (!value) return; xmlNodePtr cur = xmlfield->node->children; for(; cur != NULL; cur = cur->next) { if(!xmlStrcmp(cur->name, BAD_CAST key)) { xmlNodeSetContent(xmlfield->node, BAD_CAST value); break; } } if(cur == NULL) xmlNewTextChild(xmlfield->node, NULL, BAD_CAST key, BAD_CAST value); } /** * @brief Add the key/value pair to a xmlfield * @param xmlfield The pointer to a xmlfield object * @param key The key of the key/value pair * @param value The value of the key/value pair */ void osync_xmlfield_add_key_value(OSyncXMLField *xmlfield, const char *key, const char *value) { osync_assert(xmlfield); osync_assert(key); osync_assert(value); xmlNewTextChild(xmlfield->node, NULL, BAD_CAST key, BAD_CAST value); } /** * @brief Get the count of keys of a xmlfield * @param xmlfield The pointer to a xmlfield object * @return The count of keys of the xmlfield */ int osync_xmlfield_get_key_count(OSyncXMLField *xmlfield) { osync_assert(xmlfield); int count; xmlNodePtr child = xmlfield->node->children; for(count=0; child != NULL; count++) { child = child->next; } return count; } /** * @brief Get the name of the nth key of a xmlfield * @param xmlfield The pointer to a xmlfield object * @param nth The number of the key * @return The name of the nth key of the xmlfield or NULL in case of error */ const char *osync_xmlfield_get_nth_key_name(OSyncXMLField *xmlfield, int nth) { osync_assert(xmlfield); int count; xmlNodePtr child = xmlfield->node->children; for(count=0; child != NULL; count++) { if(count == nth) return (const char *)child->name; child = child->next; } return NULL; } /** * @brief Get the value of the nth key of a xmlfield * @param xmlfield The pointer to a xmlfield object * @param nth The number of the key * @return The value of the nth key of the xmlfield or NULL in case of error */ const char *osync_xmlfield_get_nth_key_value(OSyncXMLField *xmlfield, int nth) { osync_assert(xmlfield); int count; xmlNodePtr child = xmlfield->node->children; for(count=0; child != NULL; count++) { if(count == nth) return (const char *)osxml_node_get_content(child); // return (const char *)child->children->content; // return (const char *)xmlNodeGetContent(child); child = child->next; } return NULL; } /** * @brief Set the value of the nth key of a xmlfield * @param xmlfield The pointer to a xmlfield object * @param nth The number of the key * @param value The value of the key/value pair * @return The value of the nth key of the xmlfield or NULL in case of error */ void osync_xmlfield_set_nth_key_value(OSyncXMLField *xmlfield, int nth, const char *value) { osync_assert(xmlfield); osync_assert(value); int count; xmlNodePtr cur = xmlfield->node->children; for(count = 0; cur != NULL ; count++) { if(count == nth) xmlNodeSetContent(cur, BAD_CAST value); cur = cur->next; } } /** * @brief Compares two xmlfield objects with each other * @param xmlfield1 The pointer to a xmlformat object * @param xmlfield2 The pointer to a xmlformat object * @return TRUE if both xmlfield objects are the same otherwise FALSE */ osync_bool osync_xmlfield_compare(OSyncXMLField *xmlfield1, OSyncXMLField *xmlfield2) { osync_trace(TRACE_ENTRY, "%s(%p, %p)", __func__, xmlfield1, xmlfield2); osync_assert(xmlfield1); osync_assert(xmlfield2); int i; osync_bool same; if(xmlStrcmp(xmlfield1->node->name, xmlfield2->node->name)) { osync_trace(TRACE_EXIT, "%s: %i", __func__, FALSE); return FALSE; } same = TRUE; xmlNodePtr key1 = xmlfield1->node->children; xmlNodePtr key2 = xmlfield2->node->children; while(same) { if(key1 == NULL && key2 == NULL) { break; } if(key1 == NULL || key2 == NULL) { same = FALSE; break; } GSList *keylist1; GSList *keylist2; keylist1 = NULL; keylist2 = NULL; const char *curkeyname = (const char *)key1->name; do { keylist1 = g_slist_prepend(keylist1, key1); key1 = key1->next; if(key1 == NULL) break; i = strcmp((const char *)key1->name, curkeyname); } while(i == 0); do { keylist2 = g_slist_prepend(keylist2, key2); key2 = key2->next; if(key2 == NULL) break; i = strcmp((const char *)key2->name, curkeyname); } while(i == 0); do{ /* both lists must have the same length */ if(g_slist_length(keylist1) != g_slist_length(keylist2)) { same = FALSE; break; } GSList *cur_list1; GSList *cur_list2; do { cur_list1 = keylist1; cur_list2 = keylist2; do { if(!xmlStrcmp(osxml_node_get_content(cur_list1->data), osxml_node_get_content(cur_list2->data))) break; cur_list2 = g_slist_next(cur_list2); if(cur_list2 == NULL) { same = FALSE; break; } }while(1); if(same) { keylist1 = g_slist_delete_link(keylist1, cur_list1); keylist2 = g_slist_delete_link(keylist2, cur_list2); }else break; }while(keylist1 != NULL); }while(0); if(keylist1) g_slist_free(keylist1); if(keylist2) g_slist_free(keylist2); } /* now we check if the attributes are equal */ do { if(!same) break; int i1 =osync_xmlfield_get_attr_count(xmlfield1); int i2 =osync_xmlfield_get_attr_count(xmlfield2); if(i1 != i2) { same = FALSE; break; } for(i=0; i < i1; i++) { const char *attrvalue = osync_xmlfield_get_attr(xmlfield2, osync_xmlfield_get_nth_attr_name(xmlfield1, i)); if( attrvalue == NULL || strcmp(attrvalue, osync_xmlfield_get_nth_attr_value(xmlfield1, i))) { same = FALSE; break; } } } while(0); osync_trace(TRACE_EXIT, "%s: %i", __func__, same); return same; } /** * @brief Compares two xmlfield objects with each other of similarity * @param xmlfield1 The pointer to a xmlformat object * @param xmlfield2 The pointer to a xmlformat object * @param keys OSyncXMLPoints::keys * @return TRUE if both xmlfield objects are the similar otherwise FALSE */ osync_bool osync_xmlfield_compare_similar(OSyncXMLField *xmlfield1, OSyncXMLField *xmlfield2, char* keys[]) { osync_trace(TRACE_ENTRY, "%s(%p, %p, %p)", __func__, xmlfield1, xmlfield2, keys); osync_assert(xmlfield1); osync_assert(xmlfield2); osync_bool res = TRUE; if(strcmp((const char *) xmlfield1->node->name, (const char *) xmlfield2->node->name) != 0) res = FALSE; xmlNodePtr node1, node2; node1 = xmlfield1->node->children; node2 = xmlfield2->node->children; int i; for(i=0; keys[i]; i++) { GSList *list1; GSList *list2; list1 = NULL; list2 = NULL; while(node1 != NULL) { if(strcmp(keys[i], (const char *)node1->name) == 0) list1 = g_slist_prepend(list1, node1); node1 = node1->next; }; while(node2 != NULL) { if(strcmp(keys[i], (const char *)node2->name) == 0) list2 = g_slist_prepend(list2, node2); node2 = node2->next; }; GSList *cur_list1; GSList *cur_list2; while(list1 != NULL) { cur_list1 = list1; cur_list2 = list2; if(cur_list2 == NULL) { res = FALSE; break; } while(!xmlStrcmp(osxml_node_get_content((xmlNodePtr)cur_list1->data), osxml_node_get_content((xmlNodePtr)cur_list2->data))) { // while(strcmp((const char *)xmlNodeGetContent(cur_list1->data), // (const char *)xmlNodeGetContent(cur_list2->data)) != 0) { cur_list2 = g_slist_next(cur_list2); if(cur_list2 == NULL) { res = FALSE; break; } } if(res) { list1 = g_slist_delete_link(list1, cur_list1); list2 = g_slist_delete_link(list2, cur_list2); }else break; } if(list2 != NULL) res = FALSE; if(!res) { if(list1 != NULL) g_slist_free(list1); if(list2 != NULL) g_slist_free(list2); } } osync_trace(TRACE_EXIT, "%s: %i", __func__, res); return res; } /*@}*/