/** * Declare user-defined mapping between a URI and a namespace. */ static void xfmt_prefix_declare(struct xfmt_pass2 *xp2, const char *uri, const char *prefix) { nv_pair_t *nv; nv = nv_table_lookup(xp2->uri2prefix, uri); if (nv != NULL) { /* * Silently ignore the mapping if we already have seen an identical one * in the XML tree during the first pass. */ if (strcmp(prefix, nv_pair_value_str(nv)) != 0) { g_carp("XFMT ignoring supplied prefix '%s' for '%s': " "already saw '%s' in the tree", prefix, uri, nv_pair_value_str(nv)); } } else { /* * New mapping. */ nv = nv_pair_make_static_str(uri, prefix); nv_table_insert_pair(xp2->uri2prefix, nv); } }
/** * Record a tree-defined mapping between a prefix and a namespace URI. */ static void xfmt_prefix_record(struct xfmt_pass1 *xp1, const char *prefix, const char *uri) { nv_pair_t *nv; /* * Our policy is to use one single prefix for a given namespace URI * throughout the document. Although several prefixes could be used. * this is confusing to read and serves no value: a human will be mislead * into thinking the two namespaces are different because they carry * distinct prefixes, and a machine will not care about the prefix value. */ nv = nv_table_lookup(xp1->uri2prefix, uri); if (nv != NULL) { /* * Silently ignore the mapping if we already have seen an identical one * in the XML tree. */ if (strcmp(prefix, nv_pair_value_str(nv)) != 0) { g_carp("XFMT ignoring prefix '%s' for '%s': " "already saw '%s' earlier in the tree", prefix, uri, nv_pair_value_str(nv)); } } else { /* * New mapping. */ nv = nv_pair_make_static_str(uri, prefix); nv_table_insert_pair(xp1->uri2prefix, nv); } }
/** * Wrapper function for xnode_ns_foreach(). */ static void xnode_ns_foreach_wrap(nv_pair_t *nv, void *u) { struct xnode_ns_foreach_ctx *ctx = u; (*ctx->func)(nv_pair_name(nv), nv_pair_value_str(nv), ctx->data); }
/** * Launch UPnP control request. * * The argv[] vector (with argc entries) contains the arguments and their * values to send to the remote UPnP device. * * If a structured reply is expected (and not just a returned status code), * a launch_cb callback must be provided to process the arguments returned * by the control request and populate a structure that will be passed to the * user callback to propagate the result of the control request. * * @param usd the service to contact * @param action the action to request * @param argv the argument list for the request * @param argc amount of arguments in argv[] * @param cb user-callback when action is completed * @param cb_arg additional callback argument * @param launch_cb internal launch callback invoked on SOAP reply * * @return UPnP request handle if the SOAP RPC was initiated, NULL otherwise * (in which case callbacks will never be called). */ static upnp_ctrl_t * upnp_ctrl_launch(const upnp_service_t *usd, const char *action, nv_pair_t **argv, size_t argc, upnp_ctrl_cb_t cb, void *cb_arg, upnp_ctrl_launch_cb_t launch_cb) { upnp_ctrl_t *ucd; xnode_t *root; size_t i; soap_rpc_t *sr; g_assert(usd != NULL); g_assert(action != NULL); g_assert(0 == argc || argv != NULL); WALLOC0(ucd); ucd->magic = UPNP_CTRL_MAGIC; ucd->lcb = launch_cb; ucd->cb = cb; ucd->cb_arg = cb_arg; /* * The root element of the UPnP request. * * Its serialized form looks like this: * * <u:action xmlns:u="urn:schemas-upnp-org:service:serviceType:v"> * <arg1>in value1</arg1> * <arg2>in value2</arg2> * : : : : * <argn>in valuen</argn> * </u:action> * * The "u" prefix is arbitrary but it is the one used in all examples * presented in the UPnP architecture, and naive implementations within * devices could choke on anything else. */ { char ns[256]; gm_snprintf(ns, sizeof ns, "%s%s:%u", UPNP_NS_BASE, upnp_service_type_to_string(upnp_service_type(usd)), upnp_service_version(usd)); root = xnode_new_element(NULL, ns, action); xnode_add_namespace(root, UPNP_PREFIX, ns); } /* * Attach each argument to the root. */ for (i = 0; i < argc; i++) { nv_pair_t *nv = argv[i]; xnode_t *xargs; xargs = xnode_new_element(root, NULL, nv_pair_name(nv)); xnode_new_text(xargs, nv_pair_value_str(nv), FALSE); } /* * Launch the SOAP RPC. * * We force "s" as the SOAP prefix. It shouldn't matter at all, but * since the UPnP architecture documents its examples with "s", naive * implementations in devices could choke on anything else. * * Likewise, since the UPnP architecture document uses all-caps HTTP header * names, we can expect that some implementations within devices will * not properly understand headers spelt with traditional mixed-cased, * although it mentions that headers are case-insensitive names. Hence, * force all-caps header names. * * If the SOAP RPC cannot be launched (payload too large), the XML tree * built above was freed anyway. */ { char action_uri[256]; guint32 options = SOAP_RPC_O_MAN_RETRY | SOAP_RPC_O_ALL_CAPS; /* * Grab our local IP address if it is unknown so far. */ if (host_addr_net(upnp_get_local_addr()) == NET_TYPE_NONE) options |= SOAP_RPC_O_LOCAL_ADDR; gm_snprintf(action_uri, sizeof action_uri, "%s#%s", xnode_element_ns(root), action); ucd->action = atom_str_get(action_uri); sr = soap_rpc(upnp_service_control_url(usd), action_uri, UPNP_REPLY_MAXSIZE, options, root, UPNP_SOAP_PREFIX, upnp_ctrl_soap_reply, upnp_ctrl_soap_error, ucd); } /* * We no longer need the arguments. */ for (i = 0; i < argc; i++) { nv_pair_free_null(&argv[i]); } /* * Cleanup if we were not able to launch the request because the * serialized XML payload is too large. */ if (NULL == sr) { if (GNET_PROPERTY(upnp_debug)) { g_warning("UPNP SOAP RPC \"%s\" to \"%s\" not launched: " "payload is too large", action, upnp_service_control_url(usd)); } upnp_ctrl_free(ucd); return NULL; } ucd->sr = sr; /* So that we may cancel it if needed */ return ucd; }