soapc.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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/tcphost.h>
  35. #include <pro/uri.h>
  36. #include <pro/soap.h>
  37. #include <stdlib.h>
  38. #include <string.h>
  39. /* Use for local debugging only. Do NOT include into NUTDEBUG.
  40. #define DEBUG_SOAPC
  41. */
  42. #ifdef DEBUG_SOAPC
  43. #include <stdio.h>
  44. #endif
  45. static int ReadUntilChars(FILE *sp, const char *delim, const char *ignore, char *buf, int siz)
  46. {
  47. int rc = 0;
  48. int skip = 0;
  49. char ch;
  50. /* Do not read more characters than requested. */
  51. while (rc < siz) {
  52. ch = fgetc(sp);
  53. #ifdef DEBUG_SOAPC
  54. if (ch != EOF) {
  55. putchar(ch);
  56. }
  57. #endif
  58. if (rc == 0 && ch == ' ') {
  59. /* Skip leading spaces. */
  60. skip++;
  61. } else {
  62. rc++;
  63. if (delim && strchr(delim, ch)) {
  64. /* Delimiter found. */
  65. break;
  66. }
  67. if (buf && (ignore == NULL || strchr(ignore, ch) == NULL)) {
  68. /* Add valid character to application buffer. */
  69. *buf++ = ch;
  70. }
  71. }
  72. }
  73. if (buf) {
  74. *buf = '\0';
  75. }
  76. return rc + skip;
  77. }
  78. static int TagRead(FILE *stream, SOAP_TAG *tag, int avail)
  79. {
  80. int rc;
  81. char *cp;
  82. memset(tag, 0, sizeof(*tag));
  83. avail = avail > SOAP_MAX_TAG_SIZE ? SOAP_MAX_TAG_SIZE : avail;
  84. rc = ReadUntilChars(stream, ">", NULL, tag->soap_buff, avail);
  85. if (rc > 0) {
  86. cp = tag->soap_buff;
  87. if (*cp == '/') {
  88. cp++;
  89. tag->soap_ttf = SOAPTYPE_ETAG;
  90. }
  91. tag->soap_name._name = cp;
  92. while (*cp && *cp != ' ') {
  93. if (*cp == ':') {
  94. *cp++ = '\0';
  95. tag->soap_name._namespace = tag->soap_name._name;
  96. tag->soap_name._name = cp;
  97. } else {
  98. cp++;
  99. }
  100. }
  101. if (tag->soap_ttf == 0) {
  102. int ac;
  103. for (ac = 0; *cp && ac < SOAP_MAX_TAG_ATTRIBUTES; ac++) {
  104. *cp = '\0';
  105. while (*++cp == ' ');
  106. if (*cp == '/' && *(cp + 1) == '\0') {
  107. tag->soap_ttf = SOAPTYPE_EOTAG;
  108. break;
  109. }
  110. tag->soap_attr[ac].attr_name._name = cp;
  111. while (*cp && *cp != '=') {
  112. if (*cp == ':') {
  113. *cp++ = '\0';
  114. tag->soap_attr[ac].attr_name._namespace = tag->soap_attr[ac].attr_name._name;
  115. tag->soap_attr[ac].attr_name._name = cp;
  116. } else {
  117. cp++;
  118. }
  119. }
  120. if (*cp) {
  121. uint_fast8_t quoted = *++cp == '"';
  122. cp += quoted;
  123. tag->soap_attr[ac].attr_value = cp;
  124. while (*cp) {
  125. if (quoted) {
  126. if (*cp == '"') {
  127. break;
  128. }
  129. }
  130. else if (*cp == ' ') {
  131. break;
  132. }
  133. cp++;
  134. }
  135. if (*cp) {
  136. *cp++ = '\0';
  137. }
  138. }
  139. }
  140. }
  141. }
  142. return rc;
  143. }
  144. static int ReadResultBody(FILE *stream, int avail, SOAP_PROCEDURE *proc)
  145. {
  146. SOAP_ARG *arg = NULL;
  147. SOAP_TAG *tag;
  148. int bufsiz;
  149. int got;
  150. int in_body = 0;
  151. tag = malloc(sizeof(*tag));
  152. if (avail < 16 || tag == NULL) {
  153. return -1;
  154. }
  155. while (avail) {
  156. /* Read all characters up to the next tag, but honor limits. */
  157. bufsiz = avail > SOAP_MAX_TAG_SIZE ? SOAP_MAX_TAG_SIZE : avail;
  158. got = ReadUntilChars(stream, "<", NULL, tag->soap_buff, bufsiz);
  159. avail -= got;
  160. if (got <= 0 || avail <= 4) {
  161. break;
  162. }
  163. tag->soap_buff[got] = '\0';
  164. /* If we are inside an argument, then this is the value. */
  165. if (arg) {
  166. free(arg->arg_val);
  167. arg->arg_val = strdup(tag->soap_buff);
  168. }
  169. /* Now read the tag. */
  170. got = TagRead(stream, tag, avail);
  171. avail -= got;
  172. if (got <= 0) {
  173. break;
  174. }
  175. /* Check for envelope tag. */
  176. if (strcmp(tag->soap_name._name, "Envelope") == 0) {
  177. if (tag->soap_ttf) {
  178. /* Closing tag, update state. */
  179. in_body = 0;
  180. }
  181. }
  182. /* Check for body tag. */
  183. else if (strcmp(tag->soap_name._name, "Body") == 0) {
  184. /* Update state according to opening or closing tags. */
  185. in_body = tag->soap_ttf == 0;
  186. }
  187. /* Check for other tags only if we are inside a body. */
  188. else if (in_body) {
  189. /* Following tags in a body contain arguments. */
  190. if (tag->soap_ttf == 0) {
  191. /* Opening tag. */
  192. for (arg = proc->proc_argo; arg; arg = arg->arg_next) {
  193. if (strcasecmp(tag->soap_name._name, arg->arg_name) == 0) {
  194. break;
  195. }
  196. }
  197. } else {
  198. arg = NULL;
  199. }
  200. }
  201. }
  202. free(tag);
  203. return 0;
  204. }
  205. static int ReadResult(FILE *stream, SOAP_PROCEDURE *proc)
  206. {
  207. int rc = -1;
  208. char *line;
  209. char *val;
  210. int avail = 0;
  211. line = malloc(HTTP_MAX_REQUEST_SIZE);
  212. if (line) {
  213. if (fgets(line, HTTP_MAX_REQUEST_SIZE, stream) == NULL) {
  214. /* Broken connection, stop parsing. */
  215. }
  216. else if (atoi(line + 9) == 200) {
  217. #ifdef DEBUG_SOAPC
  218. printf("%s", line);
  219. #endif
  220. for (;;) {
  221. if (fgets(line, HTTP_MAX_REQUEST_SIZE, stream) == NULL) {
  222. /* Broken connection, stop parsing. */
  223. break;
  224. }
  225. #ifdef DEBUG_SOAPC
  226. printf("%s", line);
  227. #endif
  228. val = strchr(line, ':');
  229. if (val == NULL) {
  230. /* Hopefully we reached the end of the header. */
  231. break;
  232. }
  233. *val++ = '\0';
  234. if (strcasecmp(line, "CONTENT-LENGTH") == 0) {
  235. avail = atoi(val);
  236. }
  237. }
  238. rc = ReadResultBody(stream, avail, proc);
  239. }
  240. free(line);
  241. }
  242. return rc;
  243. }
  244. static int FillBody(char *body, int size, const char *urn, SOAP_PROCEDURE *proc)
  245. {
  246. int len;
  247. SOAP_ARG *arg;
  248. strcpy(body, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n"
  249. "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
  250. "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n"
  251. "<s:Body>\r\n");
  252. len = strlen(body);
  253. sprintf(body + len, "<u:%s xmlns:u=\"urn:%s\">\r\n", proc->proc_name, urn);
  254. len += strlen(body + len);
  255. for (arg = proc->proc_argi; arg; arg = arg->arg_next) {
  256. if (arg->arg_val) {
  257. sprintf(body + len, "<%s>%s</%s>\r\n", arg->arg_name, arg->arg_val, arg->arg_name);
  258. } else {
  259. sprintf(body + len, "<%s />\r\n", arg->arg_name);
  260. }
  261. len += strlen(body + len);
  262. }
  263. sprintf(body + len, "</u:%s>\r\n", proc->proc_name);
  264. len += strlen(body + len);
  265. strcpy(body + len, "</s:Body>\r\n"
  266. "</s:Envelope>\r\n");
  267. len += strlen(body + len);
  268. return len;
  269. }
  270. int SoapProcCallResource(SOAP_PROCEDURE *proc, const char *url, const char *uri, const char *urn, uint32_t tmo)
  271. {
  272. int rc = -1;
  273. URI_SCHEME *schm;
  274. schm = UriSchemeSplit(url + 7);
  275. if (schm) {
  276. TCPSOCKET *sock = NutTcpCreateSocket();
  277. if (sock) {
  278. FILE *stream;
  279. stream = TcpHostConnectStream(sock, schm->schm_host, schm->schm_portnum, tmo);
  280. if (stream) {
  281. int len;
  282. char *body = malloc(2048);
  283. if (body) {
  284. len = FillBody(body, 2048, urn, proc);
  285. fprintf(stream, "POST %s HTTP/1.1\r\n", uri);
  286. fprintf(stream, "HOST: %s:%s\r\n", schm->schm_host, schm->schm_port);
  287. fputs("Content-Type: text/xml; charset=\"utf-8\"\r\n", stream);
  288. if (urn) {
  289. fprintf(stream, "SOAPACTION: \"urn:%s#%s\"\r\n", urn, proc->proc_name);
  290. }
  291. fprintf(stream, "Content-Length: %d\r\n\r\n", len);
  292. fputs(body, stream);
  293. #ifdef DEBUG_SOAPC
  294. printf("POST %s HTTP/1.1\n", uri);
  295. printf("HOST: %s:%s\n", schm->schm_host, schm->schm_port);
  296. puts("Content-Type: text/xml; charset=\"utf-8\"");
  297. if (urn) {
  298. printf("SOAPACTION: \"urn:%s#%s\"\n", urn, proc->proc_name);
  299. }
  300. printf("Content-Length: %d\n\n", len);
  301. puts(body);
  302. #endif
  303. free(body);
  304. if (fflush(stream) == 0) {
  305. rc = ReadResult(stream, proc);
  306. }
  307. }
  308. fclose(stream);
  309. }
  310. NutTcpCloseSocket(sock);
  311. }
  312. UriSchemeRelease(schm);
  313. }
  314. return rc;
  315. }