Example #1
0
/**
 * 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);
}
Example #2
0
/**
 * 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;
}