/** * 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); }
/** * Pass 2 handler on each tree node entry. */ static bool xfmt_handle_pass2_enter(const void *node, void *data) { const xnode_t *xn = node; struct xfmt_pass2 *xp2 = data; xp2->depth++; if (xnode_is_element(xn)) { GSList *ns = xfmt_ns_declarations(xp2, xn); const char *nsuri = xnode_element_ns(xn); if (!xp2->had_text && !xp2->last_was_nl) { if (!(xp2->options & XFMT_O_SINGLE_LINE)) ostream_putc(xp2->os, '\n'); xp2->last_was_nl = TRUE; } xfmt_indent(xp2); /* * Look for the namespace matching the default namespace, in which * case we don't have to emit it. */ if ( nsuri != NULL && xp2->default_ns != NULL && 0 == strcmp(nsuri, xp2->default_ns) ) { nsuri = NULL; } if (nsuri != NULL) { const char *prefix = xfmt_uri_to_prefix(xp2, nsuri); ostream_printf(xp2->os, "<%s:%s", prefix, xnode_element_name(xn)); } else { ostream_printf(xp2->os, "<%s", xnode_element_name(xn)); } /* * Install default namespace on the root element, if any. */ if (1 == xp2->depth && xp2->default_ns != NULL) { int c = xfmt_quoting_char(xp2->default_ns); g_assert(c != '\0'); ostream_printf(xp2->os, " xmlns=%c%s%c", c, xp2->default_ns, c); } /* * Declare namespaces for the element's scope. */ xfmt_pass2_declare_ns(xp2, ns); g_slist_free(ns); /* * Emit attributes. */ xnode_prop_foreach(xn, xfmt_handle_pass2_attr, xp2); /* * Handle content-less elements specially: we don't let the * "leave" callback run. * * We consider an element with a single empty text child as * content-less, so we test with xnode_is_empty() instead of * !xnode_has_content(). */ xp2->had_text = FALSE; if (xnode_is_empty(xn)) { ostream_write(xp2->os, XFMT_EMPTY, CONST_STRLEN(XFMT_EMPTY)); if (!(xp2->options & XFMT_O_SINGLE_LINE)) ostream_putc(xp2->os, '\n'); xp2->last_was_nl = TRUE; xfmt_pass2_leaving(xp2); /* No children, no "leave" callback */ return FALSE; } ostream_write(xp2->os, XFMT_GT, CONST_STRLEN(XFMT_GT)); xp2->last_was_nl = FALSE; } else if (xnode_is_text(xn)) { const char *text = xnode_text(xn); size_t len; size_t overhead; bool amp; if (xp2->options & XFMT_O_SKIP_BLANKS) { const char *start; size_t tlen; start = xfmt_strip_blanks(text, &tlen); if (0 == tlen) goto ignore; /* FIXME: handle blank collapsing */ (void) start; } /* * If text is known to have entities, we must not escape the '&'. * This means the generated XML must define that entity in the DTD * part of the tree. * * Computes the required overhead to fully escape the text (0 meaning * that no escaping is required). If the overhead is larger than * a leading "<![CDATA[" and a closing ""]]>", we can emit a CDATA * section instead, provided the text does not contain "]]>". */ amp = !xnode_text_has_entities(xn); overhead = xfmt_text_escape_overhead(text, amp, FALSE, &len); if (0 == overhead) { ostream_write(xp2->os, text, len); } else if ( overhead >= XFMT_CDATA_OVERHEAD && NULL == strstr(text, XFMT_CDATA_END) ) { ostream_write(xp2->os, XFMT_CDATA_START, CONST_STRLEN(XFMT_CDATA_START)); ostream_write(xp2->os, text, len); ostream_write(xp2->os, XFMT_CDATA_END, CONST_STRLEN(XFMT_CDATA_END)); } else { char *escaped = xfmt_text_escape(text, amp, FALSE, len + overhead); ostream_write(xp2->os, escaped, len + overhead); hfree(escaped); } xp2->last_was_nl = FALSE; xp2->had_text = TRUE; } ignore: return TRUE; }