paulo@0: /* paulo@0: * $Id: xml.c,v 1.10 2004/04/13 07:25:18 hipnod Exp $ paulo@0: * paulo@0: * Copyright (C) 2001-2003 giFT project (gift.sourceforge.net) paulo@0: * paulo@0: * This program is free software; you can redistribute it and/or modify it paulo@0: * under the terms of the GNU General Public License as published by the paulo@0: * Free Software Foundation; either version 2, or (at your option) any paulo@0: * later version. paulo@0: * paulo@0: * This program is distributed in the hope that it will be useful, but paulo@0: * WITHOUT ANY WARRANTY; without even the implied warranty of paulo@0: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU paulo@0: * General Public License for more details. paulo@0: */ paulo@0: paulo@0: #include "gt_gnutella.h" paulo@0: paulo@0: #include paulo@0: paulo@0: #ifdef USE_LIBXML2 paulo@0: #include /* xmlParseMemory() */ paulo@0: #include /* xmlSetGenericErrorFunc() */ paulo@0: #endif /* USE_LIBXML2 */ paulo@0: paulo@0: #include "xml.h" paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: #ifndef USE_LIBXML2 paulo@0: BOOL gt_xml_parse (const char *xml, Dataset **ret) paulo@0: { paulo@0: return FALSE; paulo@0: } paulo@0: paulo@0: BOOL gt_xml_parse_indexed (const char *xml, size_t bin_len, Share **shares, paulo@0: size_t shares_len) paulo@0: { paulo@0: return FALSE; paulo@0: } paulo@0: #endif /* !USE_LIBXML2 */ paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: /* the rest of this file is conditional on using libxml */ paulo@0: #ifdef USE_LIBXML2 paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: #define MAX_XML_BUFSIZE 65536 paulo@0: paulo@0: static char *xml_buf; /* for decompressing xml */ paulo@0: static size_t xml_buf_size; paulo@0: static z_stream zxml; paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: static void print_nodes (xmlNodePtr node, Dataset **ret) paulo@0: { paulo@0: while (node != NULL) paulo@0: { paulo@0: /* paulo@0: * If this node has no children, it is a leaf node, paulo@0: * so set the metadata from it. paulo@0: */ paulo@0: if (node->xmlChildrenNode) paulo@0: print_nodes (node->xmlChildrenNode, ret); paulo@0: else paulo@0: GT->DBGFN (GT, "name=%s", node->name); paulo@0: paulo@0: node = node->next; paulo@0: } paulo@0: } paulo@0: paulo@0: BOOL gt_xml_parse (const char *xml, Dataset **ret) paulo@0: { paulo@0: xmlDocPtr doc; paulo@0: paulo@0: /* disable for now because it doesn't work anyway: need to share paulo@0: * code with parse_indexed */ paulo@0: if (!XML_DEBUG) paulo@0: return FALSE; paulo@0: paulo@0: /* only parse documents starting with '<' */ paulo@0: if (!xml || xml[0] != '<') paulo@0: return FALSE; paulo@0: paulo@0: if (!(doc = xmlParseMemory (xml, strlen (xml)))) paulo@0: return FALSE; paulo@0: paulo@0: print_nodes (doc->xmlChildrenNode, ret); paulo@0: paulo@0: xmlFreeDoc (doc); paulo@0: paulo@0: return TRUE; paulo@0: } paulo@0: paulo@0: static void add_child (Dataset **children, const char *key, const char *value) paulo@0: { paulo@0: char *dup = NULL; paulo@0: paulo@0: if (!key || !value) paulo@0: return; paulo@0: paulo@0: /* paulo@0: * Hack to map some of the attributes from XML documents found paulo@0: * on Gnutella to ones peddled by giFT. paulo@0: */ paulo@0: if (!strcasecmp (key, "bitrate")) paulo@0: { paulo@0: dup = stringf_dup ("%s000", value); paulo@0: value = dup; paulo@0: } paulo@0: else if (!strcasecmp (key, "seconds")) paulo@0: { paulo@0: key = "duration"; paulo@0: } paulo@0: paulo@0: dataset_insertstr (children, key, value); paulo@0: free (dup); paulo@0: } paulo@0: paulo@0: static Dataset *collect_attributes (xmlNode *node) paulo@0: { paulo@0: const xmlAttr *attr; paulo@0: Dataset *children = NULL; paulo@0: BOOL do_log = XML_DEBUG; paulo@0: paulo@0: for (attr = node->properties; attr != NULL; attr = attr->next) paulo@0: { paulo@0: xmlChar *str; paulo@0: paulo@0: /* is there an easier way to get attribute content? */ paulo@0: str = xmlGetProp (node, attr->name); paulo@0: paulo@0: if (do_log) paulo@0: { paulo@0: GT->dbg (GT, "name=%s content=%s", paulo@0: (const char *)attr->name, (const char *)str); paulo@0: } paulo@0: paulo@0: /* add the key->value pair to the dataset */ paulo@0: add_child (&children, (const char *)attr->name, paulo@0: (const char *)str); paulo@0: paulo@0: /* xmlGetProp() allocates memory */ paulo@0: free (str); paulo@0: } paulo@0: paulo@0: return children; paulo@0: } paulo@0: paulo@0: static void set_meta_foreach (ds_data_t *key, ds_data_t *value, Share *share) paulo@0: { paulo@0: char *meta_key = key->data; paulo@0: char *meta_val = value->data; paulo@0: paulo@0: share_set_meta (share, meta_key, meta_val); paulo@0: } paulo@0: paulo@0: static void set_share_meta (Share **shares, size_t shares_len, paulo@0: Dataset *children) paulo@0: { paulo@0: char *index_str; paulo@0: size_t index; paulo@0: paulo@0: /* paulo@0: * Lookup the "index" attribute, and use that to determine paulo@0: * which Share the XML applies to. paulo@0: */ paulo@0: if (!(index_str = dataset_lookupstr (children, "index"))) paulo@0: return; paulo@0: paulo@0: index = gift_strtoul (index_str); paulo@0: paulo@0: if (index >= shares_len) paulo@0: return; paulo@0: paulo@0: if (!shares[index]) paulo@0: return; paulo@0: paulo@0: /* skip the index attribute */ paulo@0: dataset_removestr (children, "index"); paulo@0: paulo@0: dataset_foreach (children, DS_FOREACH(set_meta_foreach), shares[index]); paulo@0: } paulo@0: paulo@0: static void set_metadata_from_indexed_xml (Share **shares, size_t shares_len, paulo@0: xmlDoc *doc) paulo@0: { paulo@0: xmlNode *node; paulo@0: paulo@0: if (!(node = xmlDocGetRootElement (doc))) paulo@0: return; paulo@0: paulo@0: for (node = node->xmlChildrenNode; node != NULL; node = node->next) paulo@0: { paulo@0: Dataset *children; paulo@0: paulo@0: children = collect_attributes (node); paulo@0: paulo@0: set_share_meta (shares, shares_len, children); paulo@0: dataset_clear (children); paulo@0: } paulo@0: } paulo@0: paulo@0: static int try_inflate_xml (const char *xml, size_t bin_len) paulo@0: { paulo@0: int ret; paulo@0: paulo@0: /* set zlib allocation data */ paulo@0: zxml.zalloc = Z_NULL; paulo@0: zxml.zfree = Z_NULL; paulo@0: zxml.opaque = Z_NULL; paulo@0: paulo@0: /* set the input parameters */ paulo@0: zxml.next_in = (char *)xml; paulo@0: zxml.avail_in = bin_len; paulo@0: paulo@0: /* set the output parameters */ paulo@0: zxml.next_out = xml_buf; paulo@0: zxml.avail_out = xml_buf_size - 1; paulo@0: paulo@0: if ((ret = inflateInit (&zxml)) != Z_OK) paulo@0: return ret; paulo@0: paulo@0: ret = inflate (&zxml, Z_FINISH); paulo@0: inflateEnd (&zxml); paulo@0: paulo@0: return ret; paulo@0: } paulo@0: paulo@0: static const char *inflate_xml (const char *xml, size_t bin_len) paulo@0: { paulo@0: size_t xml_len; paulo@0: int ret; paulo@0: paulo@0: ret = try_inflate_xml (xml, bin_len); paulo@0: paulo@0: if (ret == Z_BUF_ERROR && xml_buf_size < MAX_XML_BUFSIZE) paulo@0: { paulo@0: size_t newsize = xml_buf_size * 2; paulo@0: char *newbuf; paulo@0: paulo@0: if (!(newbuf = realloc (xml_buf, newsize))) paulo@0: return NULL; paulo@0: paulo@0: xml_buf = newbuf; paulo@0: xml_buf_size = newsize; paulo@0: paulo@0: /* retry with bigger buffer */ paulo@0: return inflate_xml (xml, bin_len); paulo@0: } paulo@0: paulo@0: if (ret != Z_STREAM_END) paulo@0: return NULL; paulo@0: paulo@0: /* null terminate (the now hopefully plaintext) XML */ paulo@0: xml_len = (xml_buf_size - 1) - zxml.avail_out; paulo@0: xml_buf[xml_len] = 0; paulo@0: paulo@0: if (XML_DEBUG) paulo@0: GT->dbg (GT, "inflated xml: %s", xml_buf); paulo@0: paulo@0: return xml_buf; paulo@0: } paulo@0: paulo@0: BOOL gt_xml_parse_indexed (const char *xml, size_t bin_len, Share **shares, paulo@0: size_t shares_len) paulo@0: { paulo@0: xmlDoc *doc; paulo@0: size_t xml_len; paulo@0: const char *next; paulo@0: const char *ptr; paulo@0: paulo@0: if (!xml || bin_len <= 4) paulo@0: return FALSE; paulo@0: paulo@0: /* paulo@0: * Look for the encoding type, currently possible paulo@0: * encoding values are: "{}" meaning plain text, "{plaintext}", paulo@0: * and "{deflate}". paulo@0: */ paulo@0: paulo@0: if (!strncmp (xml, "{}", 2)) paulo@0: { paulo@0: xml += 2; paulo@0: } paulo@0: else if (bin_len >= sizeof("{plaintext}") - 1 && paulo@0: !strncasecmp (xml, "{plaintext}", sizeof("{plaintext}") - 1)) paulo@0: { paulo@0: xml += sizeof("{plaintext}") - 1; paulo@0: } paulo@0: else if (bin_len >= sizeof("{deflate}") - 1 && paulo@0: !strncasecmp (xml, "{deflate}", sizeof("{deflate}") - 1)) paulo@0: { paulo@0: /* the len passed here should be bin_len - 1, but some servents (MRPH) paulo@0: * don't terminate the XML */ paulo@0: xml = inflate_xml (xml + sizeof("{deflate}") - 1, bin_len); paulo@0: paulo@0: if (XML_DEBUG) paulo@0: assert (xml != NULL); /* assume valid input */ paulo@0: paulo@0: if (!xml) paulo@0: return FALSE; paulo@0: } paulo@0: paulo@0: xml_len = strlen (xml); paulo@0: paulo@0: /* paulo@0: * The XML block is a sequence of XML documents, separated by the document prefix. Parse each one separately. paulo@0: */ paulo@0: for (ptr = xml; ptr != NULL; ptr = next) paulo@0: { paulo@0: size_t chunk_len; paulo@0: paulo@0: if (ptr[0] != '<') paulo@0: return FALSE; paulo@0: paulo@0: next = strstr (ptr + 1, "DBGFN (GT, "xml parse error: %s", buf); paulo@0: } paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: #endif /* USE_LIBXML2 */ paulo@0: paulo@0: /*****************************************************************************/ paulo@0: paulo@0: void gt_xml_init (void) paulo@0: { paulo@0: #ifdef USE_LIBXML2 paulo@0: /* so libxml doesn't print messages on stderr */ paulo@0: xmlSetGenericErrorFunc (NULL, error_handler_func); paulo@0: paulo@0: xml_buf = malloc (32); paulo@0: assert (xml_buf != NULL); paulo@0: xml_buf_size = 32; paulo@0: paulo@0: memset (&zxml, 0, sizeof (zxml)); paulo@0: #endif /* USE_LIBXML2 */ paulo@0: } paulo@0: paulo@0: void gt_xml_cleanup (void) paulo@0: { paulo@0: #ifdef USE_LIBXML2 paulo@0: free (xml_buf); paulo@0: xml_buf = NULL; paulo@0: xml_buf_size = 0; paulo@0: #endif /* USE_LIBXML2 */ paulo@0: }