/** * Add namespace declaration to element. * * There must not be any ':' in the prefix string. * The prefix must not have been already declared locally. * * @param element the element node * @param prefix the shorthand prefix for the namespace * @param uri the namespace URI */ void xnode_add_namespace(xnode_t *element, const char *prefix, const char *uri) { size_t uri_len; xnode_check(element); g_assert(XNODE_T_ELEMENT == element->type); g_assert(prefix != NULL); g_assert(uri != NULL); g_assert(NULL == strchr(prefix, ':')); if (NULL == element->u.e.ns) element->u.e.ns = nv_table_make(TRUE); /* Prefix must not be redeclared in the same element */ g_assert(NULL == nv_table_lookup(element->u.e.ns, prefix)); uri_len = strlen(uri); nv_table_insert(element->u.e.ns, prefix, uri, uri_len + 1); }
/** * Successful SOAP RPC reply callback. * * @param sr the SOAP RPC request * @param root XML tree of SOAP reply * @param arg the UPnP control request */ static void upnp_ctrl_soap_reply(const soap_rpc_t *sr, xnode_t *root, void *arg) { upnp_ctrl_t *ucd = arg; xnode_t *xn; nv_table_t *nvt; void *reply; size_t reply_len; host_addr_t local_addr; int code; upnp_ctrl_check(ucd); if (GNET_PROPERTY(upnp_debug) > 1) { g_debug("UPNP got SOAP reply for %s", ucd->action); if (GNET_PROPERTY(upnp_debug) > 2) xfmt_tree_dump(root, stderr); } ucd->sr = NULL; /* Done with SOAP request */ if (soap_rpc_local_addr(sr, &local_addr)) upnp_set_local_addr(local_addr); /* * Decompile the returned values. * * <u:actionResponse xmlns:u="urn:schemas-upnp-org:service:serviceType:v"> * <arg1>out value1</arg1> * <arg2>out value2</arg2> * : : : : * <argn>out valuen</argn> * </u:actionResponse> * * Values are inserted in name / value pairs: "arg1" -> "out value 1" and * given to the launch callback for extracting and decompiling the values. */ nvt = nv_table_make(TRUE); for (xn = xnode_first_child(root); xn; xn = xnode_next_sibling(xn)) { nv_pair_t *nv; xnode_t *xt; if (!xnode_is_element(xn)) { if (GNET_PROPERTY(upnp_debug)) { g_warning("UPNP \"%s\" skipping XML node %s", ucd->action, xnode_to_string(xn)); } continue; } xt = xnode_first_child(xn); if (NULL == xt || !xnode_is_text(xt)) { if (GNET_PROPERTY(upnp_debug)) { g_warning("UPNP \"%s\" bad child node %s in %s", ucd->action, xnode_to_string(xt), xnode_to_string2(xn)); } } else { /* * Name/value strings point in the tree, which is going to be * alive for the duration of the processing, so we can use the * strings without copying them. */ nv = nv_pair_make_static_str( xnode_element_name(xn), xnode_text(xt)); nv_table_insert_pair(nvt, nv); if (xnode_next_sibling(xt) != NULL) { if (GNET_PROPERTY(upnp_debug)) { g_warning("UPNP \"%s\" content of %s is not pure text", ucd->action, xnode_to_string(xt)); } } } } /* * Attempt to decompile the replied values, if any are expected. * * Allocated data is done via walloc(), and the returned structure is flat. * It will be freed after invoking the user callback. */ if (ucd->lcb != NULL) { reply = (*ucd->lcb)(nvt, &reply_len); code = NULL == reply ? UPNP_ERR_OK : UPNP_ERR_BAD_REPLY; } else { code = UPNP_ERR_OK; reply = NULL; reply_len = 0; } /* * Let UPnP control invoker know about the result of the query. */ (*ucd->cb)(code, reply, reply_len, ucd->cb_arg); /* * Done, final cleanup. */ WFREE_NULL(reply, reply_len); nv_table_free(nvt); upnp_ctrl_free(ucd); }
/** * Extended XML formatting of a tree. * * Namespaces, if any, are automatically assigned a prefix, whose format * is "ns%u", the counter being incremented from 0. * * Users can supply a vector mapping namespaces to prefixes, so that they * can force specific prefixes for a given well-known namespace. * * If there is a default namespace, all the tags belonging to that namespace * are emitted without any prefix. * * The output stream must be explicitly closed by the user upon return. * * Options can be supplied to tune the output: * * - XFMT_O_SKIP_BLANKS will skip pure white space nodes. * - XFMT_O_COLLAPSE_BLANKS will replace consecutive blanks with 1 space * - XFMT_O_NO_INDENT requests that no indentation of the tree be made. * - XFMT_O_PROLOGUE emits a leading <?xml?> prologue. * - XFMT_O_FORCE_10 force generation of XML 1.0 * - XFMT_O_SINGLE_LINE emits XML as one big line (implies XFMT_O_NO_INDENT). * * @param root the root of the tree to dump * @param os the output stream where tree is dumped * @param options formatting options, as documented above * @param pvec a vector of prefixes to be used for namespaces * @param pvcnt amount of entries in vector * @param default_ns default namespace to install at root element * * @return TRUE on success. */ bool xfmt_tree_extended(const xnode_t *root, ostream_t *os, uint32 options, const struct xfmt_prefix *pvec, size_t pvcnt, const char *default_ns) { struct xfmt_pass1 xp1; struct xfmt_pass2 xp2; struct xfmt_invert_ctx ictx; const char *dflt_ns; g_assert(root != NULL); g_assert(os != NULL); if (options & XFMT_O_COLLAPSE_BLANKS) { /* FIXME */ g_carp("XFMT_O_COLLAPSE_BLANKS not supported yet"); stacktrace_where_print(stderr); } if (options & XFMT_O_SINGLE_LINE) options |= XFMT_O_NO_INDENT; /* * First pass: look at namespaces and construct a table recording the * earliest tree depth at which a namespace is used. */ ZERO(&xp1); xp1.uri2node = htable_create(HASH_KEY_STRING, 0); xp1.uri2prefix = nv_table_make(FALSE); if (default_ns != NULL) xp1.attr_uris = hset_create(HASH_KEY_STRING, 0); htable_insert_const(xp1.uri2node, VXS_XML_URI, root); xnode_tree_enter_leave(deconstify_pointer(root), xfmt_handle_pass1_enter, xfmt_handle_pass1_leave, &xp1); g_assert(0 == xp1.depth); /* Sound traversal */ /* * If there was a default namespace, make sure it is used in the tree. * Otherwise, discard it. */ if (default_ns != NULL) { if (NULL == htable_lookup(xp1.uri2node, default_ns)) { g_carp("XFMT default namespace '%s' is not needed", default_ns); dflt_ns = NULL; } else { dflt_ns = default_ns; } } else { dflt_ns = NULL; } /* * Prepare context for second pass. */ ZERO(&xp2); xp2.node2uri = htable_create(HASH_KEY_SELF, 0); xp2.os = os; xp2.options = options; xp2.default_ns = dflt_ns; xp2.attr_uris = xp1.attr_uris; xp2.uri2prefix = xp1.uri2prefix; xp2.uris = symtab_make(); xp2.prefixes = symtab_make(); xp2.depth = 0; xp2.pcount = 0; xp2.last_was_nl = TRUE; /* * Iterate over the hash table we've built to create a table indexed * by tree node and listing the namespaces to declare for that node. */ ictx.uri2node = xp1.uri2node; ictx.node2uri = xp2.node2uri; htable_foreach(xp1.uri2node, xfmt_invert_uri_kv, &ictx); htable_free_null(&xp1.uri2node); /* * Emit prologue if requested. */ if (options & XFMT_O_PROLOGUE) { if (options & XFMT_O_FORCE_10) { ostream_write(os, XFMT_DECL_10, CONST_STRLEN(XFMT_DECL_10)); } else { ostream_write(os, XFMT_DECL, CONST_STRLEN(XFMT_DECL)); } if (!(options & XFMT_O_SINGLE_LINE)) { ostream_putc(os, '\n'); } } xfmt_prefix_declare(&xp2, VXS_XML_URI, VXS_XML); /* * Prepare user-defined URI -> prefix mappings. */ if (pvcnt != 0) { size_t i; for (i = 0; i < pvcnt; i++) { const struct xfmt_prefix *p = &pvec[i]; xfmt_prefix_declare(&xp2, p->uri, p->prefix); } } /* * Second pass: generation. */ xnode_tree_enter_leave(deconstify_pointer(root), xfmt_handle_pass2_enter, xfmt_handle_pass2_leave, &xp2); g_assert(0 == xp2.depth); /* Sound traversal */ /* * Done, cleanup. */ nv_table_free_null(&xp2.uri2prefix); symtab_free_null(&xp2.prefixes); symtab_free_null(&xp2.uris); htable_free_null(&xp2.node2uri); hset_free_null(&xp2.attr_uris); return !ostream_has_ioerr(os); }