cgitest.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. /*
  2. cgitest.c - Test CGI program
  3. Copyright (c) All Rights Reserved. See details at the end of the file.
  4. Usage:
  5. cgitest [switches]
  6. -a Output the args (used for ISINDEX queries)
  7. -b bytes Output content "bytes" long
  8. -e Output the environment
  9. -h lines Output header "lines" long
  10. -l location Output "location" header
  11. -n Non-parsed-header ouput
  12. -p Ouput the post data
  13. -q Ouput the query data
  14. -s status Output "status" header
  15. default Output args, env and query
  16. Alternatively, pass the arguments as an environment variable HTTP_SWITCHES="-a -e -q"
  17. */
  18. /********************************** Includes **********************************/
  19. #define _CRT_SECURE_NO_WARNINGS 1
  20. #ifndef _VSB_CONFIG_FILE
  21. #define _VSB_CONFIG_FILE "vsbConfig.h"
  22. #endif
  23. #include <errno.h>
  24. #include <ctype.h>
  25. #include <limits.h>
  26. #include <stdarg.h>
  27. #include <stdio.h>
  28. #include <string.h>
  29. #include <stdlib.h>
  30. #if _WIN32 || WINCE
  31. #include <fcntl.h>
  32. #include <io.h>
  33. #include <windows.h>
  34. #define access _access
  35. #define close _close
  36. #define fileno _fileno
  37. #define fstat _fstat
  38. #define getpid _getpid
  39. #define open _open
  40. #define putenv _putenv
  41. #define read _read
  42. #define stat _stat
  43. #define umask _umask
  44. #define unlink _unlink
  45. #define write _write
  46. #define strdup _strdup
  47. #define lseek _lseek
  48. #define getcwd _getcwd
  49. #define chdir _chdir
  50. #define strnset _strnset
  51. #define chmod _chmod
  52. #define mkdir(a,b) _mkdir(a)
  53. #define rmdir(a) _rmdir(a)
  54. typedef int ssize_t;
  55. #else
  56. #include <unistd.h>
  57. #endif
  58. /*********************************** Locals ***********************************/
  59. #define CMD_VXWORKS_EOF "_ _EOF_ _"
  60. #define CMD_VXWORKS_EOF_LEN 9
  61. #define MAX_ARGV 64
  62. static char *argvList[MAX_ARGV];
  63. static int getArgv(int *argc, char ***argv, int originalArgc, char **originalArgv);
  64. static int hasError;
  65. static int nonParsedHeader;
  66. static int numPostKeys;
  67. static int numQueryKeys;
  68. static int originalArgc;
  69. static char **originalArgv;
  70. static int outputArgs, outputEnv, outputPost, outputQuery;
  71. static int outputLines, outputHeaderLines, responseStatus;
  72. static char *outputLocation;
  73. static char *postBuf;
  74. static size_t postBufLen;
  75. static char **postKeys;
  76. static char *queryBuf;
  77. static size_t queryLen;
  78. static char **queryKeys;
  79. static char *responseMsg;
  80. static int timeout;
  81. /***************************** Forward Declarations ***************************/
  82. static void error(char *fmt, ...);
  83. static void descape(char *src);
  84. static char hex2Char(char *s);
  85. static int getVars(char ***cgiKeys, char *buf, size_t len);
  86. static int getPostData(char **buf, size_t *len);
  87. static int getQueryString(char **buf, size_t *len);
  88. static void printEnv(char **env);
  89. static void printQuery();
  90. static void printPost(char *buf, size_t len);
  91. static char *safeGetenv(char *key);
  92. /******************************************************************************/
  93. /*
  94. Test program entry point
  95. */
  96. #if VXWORKS
  97. int cgitest(int argc, char **argv, char **envp)
  98. #else
  99. int main(int argc, char **argv, char **envp)
  100. #endif
  101. {
  102. char *cp, *method;
  103. int i, j, err;
  104. err = 0;
  105. outputArgs = outputQuery = outputEnv = outputPost = 0;
  106. outputLines = outputHeaderLines = responseStatus = 0;
  107. outputLocation = 0;
  108. nonParsedHeader = 0;
  109. responseMsg = 0;
  110. hasError = 0;
  111. timeout = 0;
  112. queryBuf = 0;
  113. queryLen = 0;
  114. numQueryKeys = numPostKeys = 0;
  115. originalArgc = argc;
  116. originalArgv = argv;
  117. #if _WIN32 && !WINCE
  118. _setmode(0, O_BINARY);
  119. _setmode(1, O_BINARY);
  120. _setmode(2, O_BINARY);
  121. #endif
  122. if (strstr(argv[0], "nph-") != 0) {
  123. nonParsedHeader++;
  124. }
  125. if (getArgv(&argc, &argv, originalArgc, originalArgv) < 0) {
  126. error("Can't read CGI input");
  127. }
  128. for (i = 1; i < argc; i++) {
  129. if (argv[i][0] != '-') {
  130. continue;
  131. }
  132. for (cp = &argv[i][1]; *cp; cp++) {
  133. switch (*cp) {
  134. case 'a':
  135. outputArgs++;
  136. break;
  137. case 'b':
  138. if (++i >= argc) {
  139. err = __LINE__;
  140. } else {
  141. outputLines = atoi(argv[i]);
  142. }
  143. break;
  144. case 'e':
  145. outputEnv++;
  146. break;
  147. case 'h':
  148. if (++i >= argc) {
  149. err = __LINE__;
  150. } else {
  151. outputHeaderLines = atoi(argv[i]);
  152. nonParsedHeader++;
  153. }
  154. break;
  155. case 'l':
  156. if (++i >= argc) {
  157. err = __LINE__;
  158. } else {
  159. outputLocation = argv[i];
  160. if (responseStatus == 0) {
  161. responseStatus = 302;
  162. }
  163. }
  164. break;
  165. case 'n':
  166. nonParsedHeader++;
  167. break;
  168. case 'p':
  169. outputPost++;
  170. break;
  171. case 'q':
  172. outputQuery++;
  173. break;
  174. case 's':
  175. if (++i >= argc) {
  176. err = __LINE__;
  177. } else {
  178. responseStatus = atoi(argv[i]);
  179. }
  180. break;
  181. case 't':
  182. if (++i >= argc) {
  183. err = __LINE__;
  184. } else {
  185. timeout = atoi(argv[i]);
  186. }
  187. break;
  188. default:
  189. err = __LINE__;
  190. break;
  191. }
  192. }
  193. }
  194. if (err) {
  195. fprintf(stderr, "usage: cgitest -aenp [-b bytes] [-h lines]\n"
  196. "\t[-l location] [-s status] [-t timeout]\n"
  197. "\tor set the HTTP_SWITCHES environment variable\n");
  198. fprintf(stderr, "Error at cgitest:%d\n", __LINE__);
  199. exit(255);
  200. }
  201. if ((method = getenv("REQUEST_METHOD")) != 0 && strcmp(method, "POST") == 0) {
  202. if (getPostData(&postBuf, &postBufLen) < 0) {
  203. error("Can't read CGI input");
  204. }
  205. if (strcmp(safeGetenv("CONTENT_TYPE"), "application/x-www-form-urlencoded") == 0) {
  206. numPostKeys = getVars(&postKeys, postBuf, postBufLen);
  207. }
  208. }
  209. if (hasError) {
  210. if (! nonParsedHeader) {
  211. printf("HTTP/1.0 %d %s\r\n\r\n", responseStatus, responseMsg);
  212. printf("<HTML><BODY><p>Error: %d -- %s</p></BODY></HTML>\r\n", responseStatus, responseMsg);
  213. }
  214. fprintf(stderr, "cgitest: ERROR: %s\n", responseMsg);
  215. exit(2);
  216. }
  217. if (nonParsedHeader) {
  218. if (responseStatus == 0) {
  219. printf("HTTP/1.0 200 OK\r\n");
  220. } else {
  221. printf("HTTP/1.0 %d %s\r\n", responseStatus, responseMsg ? responseMsg: "");
  222. }
  223. printf("Connection: close\r\n");
  224. printf("X-CGI-CustomHeader: Any value at all\r\n");
  225. }
  226. printf("Content-type: %s\r\n", "text/html");
  227. if (outputHeaderLines) {
  228. for (i = 0; i < outputHeaderLines; i++) {
  229. printf("X-CGI-%d: A loooooooooooooooooooooooong string\r\n", i);
  230. }
  231. }
  232. if (outputLocation) {
  233. printf("Location: %s\r\n", outputLocation);
  234. }
  235. if (responseStatus) {
  236. printf("Status: %d\r\n", responseStatus);
  237. }
  238. printf("\r\n");
  239. if ((outputLines + outputArgs + outputEnv + outputQuery + outputPost + outputLocation + responseStatus) == 0) {
  240. outputArgs++;
  241. outputEnv++;
  242. outputQuery++;
  243. outputPost++;
  244. }
  245. if (outputLines) {
  246. for (j = 0; j < outputLines; j++) {
  247. printf("%010d\n", j);
  248. }
  249. } else {
  250. printf("<HTML><TITLE>cgitest: Output</TITLE><BODY>\r\n");
  251. if (outputArgs) {
  252. #if _WIN32
  253. printf("<P>CommandLine: %s</P>\r\n", GetCommandLine());
  254. #endif
  255. printf("<H2>Args</H2>\r\n");
  256. for (i = 0; i < argc; i++) {
  257. printf("<P>ARG[%d]=%s</P>\r\n", i, argv[i]);
  258. }
  259. }
  260. printEnv(envp);
  261. if (outputQuery) {
  262. printQuery();
  263. }
  264. if (outputPost) {
  265. printPost(postBuf, postBufLen);
  266. }
  267. printf("</BODY></HTML>\r\n");
  268. }
  269. #if VXWORKS
  270. /*
  271. VxWorks pipes need an explicit eof string
  272. Must not call exit(0) in Vxworks as that will exit the task before the CGI handler can cleanup. Must use return 0.
  273. */
  274. write(1, CMD_VXWORKS_EOF, CMD_VXWORKS_EOF_LEN);
  275. write(2, CMD_VXWORKS_EOF, CMD_VXWORKS_EOF_LEN);
  276. #endif
  277. fflush(stderr);
  278. fflush(stdout);
  279. return 0;
  280. }
  281. /*
  282. If there is a HTTP_SWITCHES argument in the query string, examine that instead of the original argv
  283. */
  284. static int getArgv(int *pargc, char ***pargv, int originalArgc, char **originalArgv)
  285. {
  286. static char sbuf[1024];
  287. char *switches, *next;
  288. int i;
  289. *pargc = 0;
  290. if (getQueryString(&queryBuf, &queryLen) < 0) {
  291. return -1;
  292. }
  293. numQueryKeys = getVars(&queryKeys, queryBuf, queryLen);
  294. switches = 0;
  295. for (i = 0; i < numQueryKeys; i += 2) {
  296. if (strcmp(queryKeys[i], "HTTP_SWITCHES") == 0) {
  297. switches = queryKeys[i+1];
  298. break;
  299. }
  300. }
  301. if (switches == 0) {
  302. switches = getenv("HTTP_SWITCHES");
  303. }
  304. if (switches) {
  305. strncpy(sbuf, switches, sizeof(sbuf) - 1);
  306. descape(sbuf);
  307. next = strtok(sbuf, " \t\n");
  308. i = 1;
  309. for (i = 1; next && i < (MAX_ARGV - 1); i++) {
  310. argvList[i] = next;
  311. next = strtok(0, " \t\n");
  312. }
  313. argvList[0] = originalArgv[0];
  314. *pargv = argvList;
  315. *pargc = i;
  316. } else {
  317. *pargc = originalArgc;
  318. *pargv = originalArgv;
  319. }
  320. return 0;
  321. }
  322. static void printEnv(char **envp)
  323. {
  324. printf("<H2>Environment Variables</H2>\r\n");
  325. printf("<P>AUTH_TYPE=%s</P>\r\n", safeGetenv("AUTH_TYPE"));
  326. printf("<P>CONTENT_LENGTH=%s</P>\r\n", safeGetenv("CONTENT_LENGTH"));
  327. printf("<P>CONTENT_TYPE=%s</P>\r\n", safeGetenv("CONTENT_TYPE"));
  328. printf("<P>DOCUMENT_ROOT=%s</P>\r\n", safeGetenv("DOCUMENT_ROOT"));
  329. printf("<P>GATEWAY_INTERFACE=%s</P>\r\n", safeGetenv("GATEWAY_INTERFACE"));
  330. printf("<P>HTTP_ACCEPT=%s</P>\r\n", safeGetenv("HTTP_ACCEPT"));
  331. printf("<P>HTTP_CONNECTION=%s</P>\r\n", safeGetenv("HTTP_CONNECTION"));
  332. printf("<P>HTTP_HOST=%s</P>\r\n", safeGetenv("HTTP_HOST"));
  333. printf("<P>HTTP_USER_AGENT=%s</P>\r\n", safeGetenv("HTTP_USER_AGENT"));
  334. printf("<P>PATH_INFO=%s</P>\r\n", safeGetenv("PATH_INFO"));
  335. printf("<P>PATH_TRANSLATED=%s</P>\r\n", safeGetenv("PATH_TRANSLATED"));
  336. printf("<P>QUERY_STRING=%s</P>\r\n", safeGetenv("QUERY_STRING"));
  337. printf("<P>REMOTE_ADDR=%s</P>\r\n", safeGetenv("REMOTE_ADDR"));
  338. printf("<P>REQUEST_METHOD=%s</P>\r\n", safeGetenv("REQUEST_METHOD"));
  339. printf("<P>REQUEST_URI=%s</P>\r\n", safeGetenv("REQUEST_URI"));
  340. printf("<P>REMOTE_USER=%s</P>\r\n", safeGetenv("REMOTE_USER"));
  341. printf("<P>SCRIPT_NAME=%s</P>\r\n", safeGetenv("SCRIPT_NAME"));
  342. printf("<P>SCRIPT_FILENAME=%s</P>\r\n", safeGetenv("SCRIPT_FILENAME"));
  343. printf("<P>SERVER_ADDR=%s</P>\r\n", safeGetenv("SERVER_ADDR"));
  344. printf("<P>SERVER_NAME=%s</P>\r\n", safeGetenv("SERVER_NAME"));
  345. printf("<P>SERVER_PORT=%s</P>\r\n", safeGetenv("SERVER_PORT"));
  346. printf("<P>SERVER_PROTOCOL=%s</P>\r\n", safeGetenv("SERVER_PROTOCOL"));
  347. printf("<P>SERVER_SOFTWARE=%s</P>\r\n", safeGetenv("SERVER_SOFTWARE"));
  348. #if !VXWORKS
  349. /*
  350. This is not supported on VxWorks as you can't get "envp" in main()
  351. */
  352. printf("\r\n<H2>All Defined Environment Variables</H2>\r\n");
  353. if (envp) {
  354. char *p;
  355. int i;
  356. for (i = 0, p = envp[0]; envp[i]; i++) {
  357. p = envp[i];
  358. printf("<P>%s</P>\r\n", p);
  359. }
  360. }
  361. #endif
  362. printf("\r\n");
  363. }
  364. static void printQuery()
  365. {
  366. int i;
  367. if (numQueryKeys == 0) {
  368. printf("<H2>No Query String Found</H2>\r\n");
  369. } else {
  370. printf("<H2>Decoded Query String Variables</H2>\r\n");
  371. for (i = 0; i < (numQueryKeys * 2); i += 2) {
  372. if (queryKeys[i+1] == 0) {
  373. printf("<p>QVAR %s=</p>\r\n", queryKeys[i]);
  374. } else {
  375. printf("<p>QVAR %s=%s</p>\r\n", queryKeys[i], queryKeys[i+1]);
  376. }
  377. }
  378. }
  379. printf("\r\n");
  380. }
  381. static void printPost(char *buf, size_t len)
  382. {
  383. int i;
  384. if (numPostKeys) {
  385. printf("<H2>Decoded Post Variables</H2>\r\n");
  386. for (i = 0; i < (numPostKeys * 2); i += 2) {
  387. printf("<p>PVAR %s=%s</p>\r\n", postKeys[i], postKeys[i+1]);
  388. }
  389. } else if (buf) {
  390. if (len < (50 * 1000)) {
  391. printf("<H2>Post Data %d bytes found (data below)</H2>\r\n", (int) len);
  392. fflush(stdout);
  393. if (write(1, buf, (int) len) != len) {}
  394. } else {
  395. printf("<H2>Post Data %d bytes found</H2>\r\n", (int) len);
  396. }
  397. } else {
  398. printf("<H2>No Post Data Found</H2>\r\n");
  399. }
  400. printf("\r\n");
  401. }
  402. static int getQueryString(char **buf, size_t *buflen)
  403. {
  404. *buflen = 0;
  405. *buf = 0;
  406. if (getenv("QUERY_STRING") == 0) {
  407. *buf = "";
  408. *buflen = 0;
  409. } else {
  410. *buf = getenv("QUERY_STRING");
  411. *buflen = (int) strlen(*buf);
  412. }
  413. return 0;
  414. }
  415. static int getPostData(char **bufp, size_t *lenp)
  416. {
  417. char *contentLength, *buf;
  418. ssize_t bufsize, bytes, size, limit, len;
  419. if ((contentLength = getenv("CONTENT_LENGTH")) != 0) {
  420. size = atoi(contentLength);
  421. limit = size;
  422. } else {
  423. size = 4096;
  424. limit = INT_MAX;
  425. }
  426. if ((buf = malloc(size + 1)) == 0) {
  427. error("Cannot allocate memory to read post data");
  428. return -1;
  429. }
  430. bufsize = size + 1;
  431. len = 0;
  432. while (len < limit) {
  433. if ((len + size + 1) > bufsize) {
  434. if ((buf = realloc(buf, len + size + 1)) == 0) {
  435. error("Cannot allocate memory to read post data");
  436. return -1;
  437. }
  438. bufsize = len + size + 1;
  439. }
  440. bytes = read(0, &buf[len], (int) size);
  441. if (bytes < 0) {
  442. error("Cannot read CGI input error=%d, len %d, %d", (int) errno, (int) len, (int) limit);
  443. return -1;
  444. } else if (bytes == 0) {
  445. /* EOF */
  446. #if UNUSED
  447. /*
  448. If using multipart-mime, the CONTENT_LENGTH won't match the length of the data actually received
  449. */
  450. if (contentLength && len != limit) {
  451. error("Missing content data (Content-Length: %s)", contentLength ? contentLength : "unspecified");
  452. }
  453. #endif
  454. break;
  455. }
  456. len += bytes;
  457. }
  458. buf[len] = 0;
  459. *lenp = len;
  460. *bufp = buf;
  461. return 0;
  462. }
  463. static int getVars(char ***cgiKeys, char *buf, size_t buflen)
  464. {
  465. char **keyList, *eq, *cp, *pp, *newbuf;
  466. int i, keyCount;
  467. if (buflen > 0) {
  468. if ((newbuf = malloc(buflen + 1)) == 0) {
  469. error("Can't allocate memory");
  470. return 0;
  471. }
  472. strncpy(newbuf, buf, buflen);
  473. newbuf[buflen] = '\0';
  474. buf = newbuf;
  475. }
  476. /*
  477. Change all plus signs back to spaces
  478. */
  479. keyCount = (buflen > 0) ? 1 : 0;
  480. for (cp = buf; cp < &buf[buflen]; cp++) {
  481. if (*cp == '+') {
  482. *cp = ' ';
  483. } else if (*cp == '&') {
  484. keyCount++;
  485. }
  486. }
  487. if (keyCount == 0) {
  488. return 0;
  489. }
  490. /*
  491. Crack the input into name/value pairs
  492. */
  493. keyList = malloc((keyCount * 2) * sizeof(char**));
  494. i = 0;
  495. for (pp = strtok(buf, "&"); pp; pp = strtok(0, "&")) {
  496. if ((eq = strchr(pp, '=')) != 0) {
  497. *eq++ = '\0';
  498. descape(pp);
  499. descape(eq);
  500. } else {
  501. descape(pp);
  502. }
  503. if (i < (keyCount * 2)) {
  504. keyList[i++] = pp;
  505. keyList[i++] = eq;
  506. }
  507. }
  508. *cgiKeys = keyList;
  509. return keyCount;
  510. }
  511. static char hex2Char(char *s)
  512. {
  513. char c;
  514. if (*s >= 'A') {
  515. c = toupper(*s & 0xFF) - 'A' + 10;
  516. } else {
  517. c = *s - '0';
  518. }
  519. s++;
  520. if (*s >= 'A') {
  521. c = (c * 16) + (toupper(*s & 0xFF) - 'A') + 10;
  522. } else {
  523. c = (c * 16) + (toupper(*s & 0xFF) - '0');
  524. }
  525. return c;
  526. }
  527. static void descape(char *src)
  528. {
  529. char *dest;
  530. dest = src;
  531. while (*src) {
  532. if (*src == '%') {
  533. *dest++ = hex2Char(++src) ;
  534. src += 2;
  535. } else {
  536. *dest++ = *src++;
  537. }
  538. }
  539. *dest = '\0';
  540. }
  541. static char *safeGetenv(char *key)
  542. {
  543. char *cp;
  544. cp = getenv(key);
  545. if (cp == 0) {
  546. return "";
  547. }
  548. return cp;
  549. }
  550. void error(char *fmt, ...)
  551. {
  552. va_list args;
  553. char buf[4096];
  554. if (responseMsg == 0) {
  555. va_start(args, fmt);
  556. vsprintf(buf, fmt, args);
  557. responseStatus = 400;
  558. responseMsg = strdup(buf);
  559. va_end(args);
  560. }
  561. hasError++;
  562. }
  563. #if VXWORKS
  564. /*
  565. VxWorks link resolution
  566. */
  567. int _cleanup() {
  568. return 0;
  569. }
  570. int _exit() {
  571. return 0;
  572. }
  573. #endif /* VXWORKS */
  574. /*
  575. @copy default
  576. Copyright (c) Embedthis Software LLC, 2003-2014. All Rights Reserved.
  577. This software is distributed under commercial and open source licenses.
  578. You may use the Embedthis Open Source license or you may acquire a
  579. commercial license from Embedthis Software. You agree to be fully bound
  580. by the terms of either license. Consult the LICENSE.md distributed with
  581. this software for full details and other copyrights.
  582. Local variables:
  583. tab-width: 4
  584. c-basic-offset: 4
  585. End:
  586. vim: sw=4 ts=4 expandtab
  587. */