/* eurephia_xml.c -- Generic helper functions for XML parsing * * GPLv2 only - Copyright (C) 2008 - 2012 * David Sommerseth * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 * of the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ /** * @file eurephia_xml.c * @author David Sommerseth * @date 2008-12-15 * * @brief Generic XML parser functions * * */ #ifdef HAVE_LIBXML2 #include #include #include #include #include #include #include #include /** * String replace in a xmlChar based string * * @param str xmlChar input string * @param s search for this character * @param r replace the character with this one */ void xmlReplaceChars(xmlChar *str, char s, char r) { if( str != NULL ) { xmlChar *ptr = str; while( *ptr != '\0' ) { if( *ptr == s ) { *ptr = r; } ptr++; } } } /** * Retrieves a given XML node attribute/property * * @param attr xmlAttr pointer from an xmlNode pointer. * @param key The attribute name to search for * * @return The value of the found attribute. If not found, NULL is returned. */ char *xmlGetAttrValue(xmlAttr *attr, const char *key) { xmlAttr *aptr; xmlChar *x_key = NULL; x_key = xmlCharStrdup(key); assert( x_key != NULL ); for( aptr = attr; aptr != NULL; aptr = aptr->next ) { if( xmlStrcmp(aptr->name, x_key) == 0 ) { // FIXME: Should find a better way to return UTF-8 data free_nullsafe(NULL, x_key); return (char *)(aptr->children != NULL ? aptr->children->content : NULL); } } free_nullsafe(NULL, x_key); return NULL; } /** * Loops through a xmlNode chain to look for a given tag. The search is not recursive. * * @param node xmlNode pointer where to look * @param key the name of the XML tag to find * * @return xmlNode pointer to the found xmlNode. NULL is returned if not found. */ xmlNode *xmlFindNode(xmlNode *node, const char *key) { xmlNode *nptr = NULL; xmlChar *x_key = NULL; if( (node == NULL) || (node->children == NULL) ) { return NULL; } x_key = xmlCharStrdup(key); assert( x_key != NULL ); for( nptr = node->children; nptr != NULL; nptr = nptr->next ) { if( xmlStrcmp(nptr->name, x_key) == 0 ) { free_nullsafe(NULL, x_key); return nptr; } } free_nullsafe(NULL, x_key); return NULL; } /** * Simple function for creating a new eurephia XML document. On failure, this function will cause * an assertion error. * * @param ctx eurephiaCTX * @param format Format version of the eurephia document (int value) * @param eurephiaRoot The name of the root tag of the resulting XML document * @param doc xmlDoc pointer to the new document * @param root_n xmlNode pointer to the root element of the document * * @return returns always 1. */ int eurephiaXML_CreateDoc(eurephiaCTX *ctx, int format, const char *eurephiaRoot, xmlDoc **doc, xmlNode **root_n) { char tmp[34]; // Create a new XML document *doc = xmlNewDoc((xmlChar *)"1.0"); assert(*doc != NULL); // Set the XML root to be *root_n = xmlNewNode(NULL, (xmlChar *)"eurephia"); assert(*root_n != NULL); // Add eurephia XML document format version id snprintf(tmp, 33, "%i%c", format, '\0'); xmlNewProp(*root_n, (xmlChar *)"format", (xmlChar *)tmp); xmlDocSetRootElement(*doc, *root_n); // Add the eurephia XML root (always inside the tags) *root_n = xmlNewChild(*root_n, NULL, (xmlChar*)eurephiaRoot, NULL); return 1; } /** * Get the root node of an eurephia XML document. This function also validates the basic structure * of the document and makes sure the format version of the document is valid. * * @param ctx eurephiaCTX * @param doc xmlDoc pointer to the XML document * @param nodeset The expected root node to be found. * @param req_format The minimum format version to be accepted * * @return Returns pointer to the given xmlNode tag. On failure, NULL is returned. */ xmlNode *eurephiaXML_getRoot(eurephiaCTX *ctx, xmlDoc *doc, const char *nodeset, int req_format) { xmlNode *root = NULL; char *xmlformat_str = NULL; int xmlformat = 0; root = xmlDocGetRootElement(doc); if( (root == NULL) || (xmlStrcmp(root->name, (xmlChar *)"eurephia") != 0) ) { eurephia_log(ctx, LOG_FATAL, 0, "Could not find eurephia XML root element. " "Not a valid eurephia XML document."); return NULL; } xmlformat_str = xmlGetAttrValue(root->properties, "format"); xmlformat = atoi_nullsafe(xmlformat_str); if( xmlformat < req_format ) { eurephia_log(ctx, LOG_ERROR, 0, "eurephia XML document format is not supported. " "The XML document uses '%s', while we need minimum '%i'", xmlformat_str, req_format); return NULL; } return (nodeset != NULL ? xmlFindNode(root, nodeset) : root->children); } /** * Creates a simple result message, formatted as an XML document. * * @param ctx eurephiaCTX * @param type Can be exmlRESULT or exmlERROR. The former is used for informational messages. * @param info_n xmlNode with more details about the result * @param fmt stdarg format string * * @return Returns a valid eurephia ResultMsg XML document as a properly formatted result message. * On failure, NULL is returned * * Skeleton for a eurephia ResultMsg XML document * @code * * * {String containing a descriptive message} * [
{xmlNode including children with more detailed information}
] *
*
* @endcode * The status attribute is set to "Result" on success, and "Error" in error situations */ xmlDoc *eurephiaXML_ResultMsg(eurephiaCTX *ctx, exmlResultType type, xmlNode *info_n, const char *fmt, ... ) { va_list ap; xmlChar msg[2050], *xmlfmt = NULL; xmlDoc *msgdoc = NULL; xmlNode *msg_n = NULL; memset(&msg, 0, 2050); xmlfmt = xmlCharStrdup(fmt); assert( xmlfmt != NULL ); va_start(ap, fmt); xmlStrVPrintf(msg, 2048, xmlfmt, ap); va_end(ap); free_nullsafe(ctx, xmlfmt); eurephiaXML_CreateDoc(ctx, 1, "Result", &msgdoc, &msg_n); assert( (msgdoc != NULL) && (msg_n != NULL) ); switch( type ) { case exmlRESULT: xmlNewProp(msg_n, (xmlChar *) "status", (xmlChar *) "Result"); break; case exmlERROR: xmlNewProp(msg_n, (xmlChar *) "status", (xmlChar *) "Error"); break; default: eurephia_log(ctx, LOG_ERROR, 0, "Wrong XML result message type (%i)", type); return NULL; } xmlNewChild(msg_n, NULL, (xmlChar *) "Message", msg); if( info_n != NULL ) { // Create a new child tag (Details) and copy the info nodes into this tag xmlAddChild(xmlNewChild(msg_n, NULL, (xmlChar *) "Details", NULL), xmlCopyNode(info_n, 1)); } return msgdoc; } /** * Checks if the given XML document is an eurephia ResultMsg XML document * * @param ctx eurephiaCTX * @param resxml XML document to validate * * @return Returns 1 if the input XML document is a ResultMsg document. Otherwise 0 */ unsigned int eurephiaXML_IsResultMsg(eurephiaCTX *ctx, xmlDoc *resxml) { xmlNode *node = NULL; assert( ctx != NULL ); if( resxml == NULL ) { return 0; } node = eurephiaXML_getRoot(ctx, resxml, "Result", 1); return (node != NULL ? 1 : 0); } /** * Parses an eurephia Result XML document * * @param ctx eurephiaCTX * @param resxml The result XML document, as produced by eurephiaXML_ResultMsg() * * @return Returns a pointer to an eurephiaRESULT structure containing the results. * On failure NULL is returned. This structure can be freed with free_nullsafe(). * * @remark If the result XML document is freed, the information in eurephiaRESULT will be invalidated * Immediately. However, the eurephiaRESULT pointer must still be freed. * @see eurephiaXML_ResultMsg() */ eurephiaRESULT *eurephiaXML_ParseResultMsg(eurephiaCTX *ctx, xmlDoc *resxml) { eurephiaRESULT *res = NULL; xmlNode *res_n = NULL; char *str = NULL; assert( ctx != NULL ); if( resxml == NULL ) { return NULL; } res_n = eurephiaXML_getRoot(ctx, resxml, "Result", 1); if( res_n == NULL) { eurephia_log(ctx, LOG_ERROR, 0, "Could not find a valid tag"); return NULL; } res = (eurephiaRESULT *) malloc_nullsafe(ctx, sizeof(eurephiaRESULT) + 2); assert( res != NULL ); str = xmlGetAttrValue(res_n->properties, "status"); if( strcmp(str, "Error") == 0 ) { res->resultType = exmlERROR; } else if( strcmp(str, "Result") == 0 ) { res->resultType = exmlRESULT; } else { free_nullsafe(ctx, res); eurephia_log(ctx, LOG_ERROR, 0, "Invalid result status"); return NULL; } res->message = xmlGetNodeContent(res_n, "Message"); res->details = xmlFindNode(res_n, "Details"); return res; } /** * Return the text content of a given xmlNode * * @param n xmlNode to extract the value from. * * @return returns a char pointer with the text contents of an xmlNode. */ inline char *xmlExtractContent(xmlNode *n) { // FIXME: Should find better way how to return UTF-8 data return (char *) (((n != NULL) && (n->children != NULL)) ? n->children->content : NULL); } /** * Get the text contents of a given xmlNode * * @param node An xmlNode pointer where to look for the contents * @param key Name of the tag to retrieve the content of. * * @return Returns a string with the text content, if the node is found. Otherwise, NULL is returned. */ inline char *xmlGetNodeContent(xmlNode *node, const char *key) { return xmlExtractContent(xmlFindNode(node, key)); } #endif