upnp_dev.c 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /*
  2. * Copyright (C) 2012-2013 by egnite GmbH
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without
  7. * modification, are permitted provided that the following conditions
  8. * are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. Neither the name of the copyright holders nor the names of
  16. * contributors may be used to endorse or promote products derived
  17. * from this software without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  20. * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  21. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  22. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  23. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  24. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  25. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  26. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  27. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
  29. * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  30. * SUCH DAMAGE.
  31. *
  32. * For additional information see http://www.ethernut.de/
  33. */
  34. #include <pro/uhttp/modules/mod_cgi_func.h>
  35. #include <pro/upnp.h>
  36. #include <string.h>
  37. /* Use for local debugging only. Do NOT include into NUTDEBUG.
  38. #define DEBUG_UPNPC
  39. */
  40. #ifdef DEBUG_UPNPC
  41. #include <stdio.h>
  42. #endif
  43. static SSDP_DEVICE *device_registration;
  44. static void XmlHead(HTTP_STREAM *stream)
  45. {
  46. HttpSendStreamHeaderTop(stream, 200);
  47. s_puts("SERVER: NutOS/5.0 UPnP/1.0 TestUPnP/1.0\r\n", stream);
  48. HttpSendStreamHeaderBottom(stream, "text", "xml", HTTP_CONN_CLOSE, -1);
  49. s_puts("<?xml version=\"1.0\"?>\r\n", stream);
  50. }
  51. static void WritePrepTag(HTTP_STREAM *stream, const char *tag, const char *val, const char *prepend)
  52. {
  53. if (val) {
  54. s_printf(stream, "<%s>", tag);
  55. if (prepend && *val == '+') {
  56. val++;
  57. s_puts(prepend, stream);
  58. }
  59. s_printf(stream, "%s</%s>\r\n", val, tag);
  60. }
  61. }
  62. static void WriteTag(HTTP_STREAM *stream, const char *tag, const char *val)
  63. {
  64. WritePrepTag(stream, tag, val, NULL);
  65. }
  66. static void SpecVersion(HTTP_STREAM *stream)
  67. {
  68. s_puts("<specVersion>\r\n", stream);
  69. s_puts("<major>1</major>\r\n", stream);
  70. s_puts("<minor>0</minor>\r\n", stream);
  71. s_puts("</specVersion>\r\n", stream);
  72. }
  73. /*!
  74. * \brief Send device description.
  75. *
  76. * This uHTTP CGI function will be registered when UpnpRegisterDeviceTree()
  77. * is called for the first time.
  78. */
  79. static int UpnpCgiDeviceDescription(HTTPD_SESSION *hs)
  80. {
  81. HTTP_STREAM *stream = hs->s_stream;
  82. SSDP_DEVICE *sdev = NULL;
  83. SSDP_SERVICE *ssvc;
  84. char *type;
  85. type = HttpArgParseFirst(&hs->s_req);
  86. if (type) {
  87. for (sdev = device_registration; sdev; sdev = sdev->sdev_next) {
  88. if (strcmp(sdev->sdev_type, type) == 0) {
  89. break;
  90. }
  91. }
  92. }
  93. if (sdev) {
  94. UPNP_DEVICE_INFO *udev = sdev->sdev_info;
  95. XmlHead(hs->s_stream);
  96. s_puts("<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\r\n", hs->s_stream);
  97. SpecVersion(stream);
  98. s_puts("<device>\r\n", stream);
  99. s_printf(stream, "<deviceType>urn:%s:device:%s:1</deviceType>\r\n", sdev->sdev_domain, sdev->sdev_type);
  100. WriteTag(stream, "friendlyName", udev->udev_name);
  101. WriteTag(stream, "manufacturer", udev->udev_mnf->umnf_name);
  102. WritePrepTag(stream, "manufacturerURL", udev->udev_mnf->umnf_url, "http://");
  103. WriteTag(stream, "modelDescription", udev->udev_mdl->umdl_desc);
  104. WriteTag(stream, "modelName", udev->udev_mdl->umdl_name);
  105. WriteTag(stream, "modelNumber", udev->udev_mdl->umdl_num);
  106. WritePrepTag(stream, "modelURL", udev->udev_mdl->umdl_url, "http://");
  107. WriteTag(stream, "UDN", sdev->sdev_uuid);
  108. if (sdev->sdev_svc) {
  109. s_puts("<serviceList>\r\n", stream);
  110. for (ssvc = sdev->sdev_svc; ssvc; ssvc = ssvc->ssvc_next) {
  111. UPNP_SERVICE_INFO *usvc = sdev->sdev_svc->ssvc_info;
  112. s_puts("<service>\r\n", stream);
  113. s_printf(stream, "<serviceType>urn:schemas-upnp-org:service:%s:1</serviceType>\r\n", sdev->sdev_svc->ssvc_type);
  114. s_printf(stream, "<serviceId>urn:upnp-org:serviceId:%s:1</serviceId>\r\n", sdev->sdev_svc->ssvc_type);
  115. s_printf(stream, "<SCPDURL>%s?%s=%s</SCPDURL>", usvc->usvc_url_scpd, sdev->sdev_type, ssvc->ssvc_type);
  116. s_printf(stream, "<controlURL>%s?%s=%s</controlURL>", usvc->usvc_url_ctrl, sdev->sdev_type, ssvc->ssvc_type);
  117. s_printf(stream, "<eventSubURL>%s?%s=%s</eventSubURL>", usvc->usvc_url_event, sdev->sdev_type, ssvc->ssvc_type);
  118. s_puts("</service>\r\n", stream);
  119. }
  120. s_puts("</serviceList>\r\n", stream);
  121. }
  122. WriteTag(stream, "presentationURL", udev->udev_presentation);
  123. s_puts("</device>\r\n", stream);
  124. s_puts("</root>\r\n", stream);
  125. }
  126. s_flush(stream);
  127. return 0;
  128. }
  129. /*!
  130. * \brief Send service description.
  131. *
  132. * This uHTTP CGI function will be registered when UpnpRegisterDeviceTree()
  133. * is called for the first time.
  134. */
  135. static int UpnpCgiServiceDescription(HTTPD_SESSION *hs)
  136. {
  137. SSDP_SERVICE *ssvc = NULL;
  138. SSDP_DEVICE *sdev = NULL;
  139. HTTP_STREAM *stream = hs->s_stream;
  140. const char *dev_type;
  141. dev_type = HttpArgParseFirst(&hs->s_req);
  142. if (dev_type) {
  143. const char *svc_type = HttpArgValue(&hs->s_req);
  144. if (dev_type) {
  145. for (sdev = device_registration; sdev; sdev = sdev->sdev_next) {
  146. if (strcmp(sdev->sdev_type, dev_type) == 0) {
  147. for (ssvc = sdev->sdev_svc; ssvc; ssvc = ssvc->ssvc_next) {
  148. if (strcmp(ssvc->ssvc_type, svc_type) == 0) {
  149. break;
  150. }
  151. }
  152. break;
  153. }
  154. }
  155. }
  156. }
  157. if (ssvc) {
  158. SOAP_PROCEDURE *act;
  159. UPNP_VARIABLE *stv;
  160. UPNP_SERVICE_INFO *usvc = ssvc->ssvc_info;
  161. XmlHead(hs->s_stream);
  162. s_puts("<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\r\n", stream);
  163. SpecVersion(stream);
  164. act = usvc->usvc_proc;
  165. if (act) {
  166. s_puts("<actionList>\r\n", stream);
  167. do {
  168. s_puts("<action>\r\n", stream);
  169. WriteTag(stream, "name", act->proc_name);
  170. if (act->proc_argi || act->proc_argo) {
  171. SOAP_ARG *arg;
  172. s_puts("<argumentList>\r\n", stream);
  173. for (arg = act->proc_argi; arg; arg = arg->arg_next) {
  174. s_puts("<argument>\r\n", stream);
  175. WriteTag(stream, "name", arg->arg_name);
  176. WriteTag(stream, "relatedStateVariable", ((UPNP_VARIABLE *) arg->arg_info)->ustv_name);
  177. WriteTag(stream, "direction", "in");
  178. s_puts("</argument>\r\n", stream);
  179. }
  180. for (arg = act->proc_argo; arg; arg = (SOAP_ARG *) arg->arg_next) {
  181. s_puts("<argument>\r\n", stream);
  182. WriteTag(stream, "name", arg->arg_name);
  183. WriteTag(stream, "relatedStateVariable", ((UPNP_VARIABLE *) arg->arg_info)->ustv_name);
  184. WriteTag(stream, "direction", "out");
  185. s_puts("</argument>\r\n", stream);
  186. }
  187. s_puts("</argumentList>\r\n", stream);
  188. }
  189. s_puts("</action>\r\n", stream);
  190. } while ((act = act->proc_next) != NULL);
  191. s_puts("</actionList>\r\n", stream);
  192. }
  193. stv = usvc->usvc_stv;
  194. if (stv) {
  195. s_puts("<serviceStateTable>\r\n", stream);
  196. do {
  197. s_printf(stream, "<stateVariable sendEvents=\"%s\">\r\n", stv->ustv_events ? "yes" : "no");
  198. WriteTag(stream, "name", stv->ustv_name);
  199. WriteTag(stream, "dataType", UpnpVarTypeString(stv->ustv_type));
  200. WriteTag(stream, "defaultValue", stv->ustv_default);
  201. s_puts("</stateVariable>\r\n", stream);
  202. } while((stv = stv->ustv_next) != NULL);
  203. s_puts("</serviceStateTable>\r\n", stream);
  204. }
  205. s_puts("</scpd>\r\n", stream);
  206. }
  207. s_flush(stream);
  208. return 0;
  209. }
  210. int UpnpRegisterDeviceTree(SSDP_DEVICE *parent, SSDP_DEVICE *sdev)
  211. {
  212. int rc = 0;
  213. static uint_fast8_t initialized;
  214. if (parent) {
  215. parent->sdev_next = (SSDP_DEVICE *) sdev;
  216. } else {
  217. sdev->sdev_next = (SSDP_DEVICE *) device_registration;
  218. device_registration = sdev;
  219. if (!initialized) {
  220. HttpRegisterCgiFunction(sdev->sdev_url_desc, UpnpCgiDeviceDescription);
  221. HttpRegisterCgiFunction(((UPNP_SERVICE_INFO *) sdev->sdev_svc->ssvc_info)->usvc_url_scpd, UpnpCgiServiceDescription);
  222. }
  223. rc = SsdpRegisterDeviceTree(device_registration);
  224. }
  225. return rc;
  226. }