cgiutil.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. /** --- BEGIN COPYRIGHT BLOCK ---
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  35. * Copyright (C) 2005 Red Hat, Inc.
  36. * All rights reserved.
  37. --- END COPYRIGHT BLOCK --- */
  38. /*
  39. * cgiutil.c -- CGI-related utility functions -- HTTP gateway
  40. *
  41. * Note: tihs code is derived from the extras/changepw.c code that ships
  42. * with the FastTrack 2.0 server
  43. */
  44. #include "dsgw.h"
  45. #include "dbtdsgw.h"
  46. #include <prprf.h>
  47. #include <unicode/ucnv.h>
  48. #include <unicode/ustring.h>
  49. /* globals */
  50. static char **formvars = NULL;
  51. /* functions */
  52. static char **dsgw_string_to_vec(char *in);
  53. static void
  54. dsgw_vec_convert (char** vec)
  55. /* Convert input from the charset named in it (if any) to UTF_8.
  56. Either return s, or free(s) and return the converted string.
  57. */
  58. {
  59. static const char* prefix = "charset=";
  60. const size_t prefix_len = strlen (prefix);
  61. char** v;
  62. if (vec) for (v = vec; *v; ++v) {
  63. if (!strncmp (*v, prefix, prefix_len)) {
  64. char* charset = *v + prefix_len;
  65. UConverter* converter = NULL;
  66. UErrorCode err = U_ZERO_ERROR;
  67. if ( ! is_UTF_8 (charset) && (converter = ucnv_open(charset, &err)) &&
  68. (err == U_ZERO_ERROR) ) {
  69. for (v = vec; *v; ++v) {
  70. char* s = strchr (*v, '=');
  71. if (s != NULL) {
  72. char *t = NULL;
  73. const size_t nlen = (++s) - *v;
  74. const size_t slen = strlen (s);
  75. size_t tlen = 0;
  76. size_t reallen = 0;
  77. int result;
  78. if (ucnv_getMaxCharSize(converter) == 1) {
  79. tlen = slen + 2; /* best case - ascii or other 7/8 bit */
  80. } else { /* assume worst case utf8 - each char is 3 bytes */
  81. tlen = (slen * 3) + 2;
  82. }
  83. do {
  84. char *tptr;
  85. size_t realSlen = 0;
  86. err = U_ZERO_ERROR;
  87. if (t) {
  88. t = dsgw_ch_realloc(t, nlen + tlen);
  89. } else {
  90. t = dsgw_ch_malloc(nlen + tlen);
  91. }
  92. tptr = t + nlen;
  93. /* copy the converted characters into t after the '=', and
  94. leave room for the trailing 0 */
  95. result = dsgw_convert(DSGW_TO_UTF8, converter,
  96. &tptr, (tlen - nlen - 1), &reallen,
  97. s, slen, &realSlen, &err);
  98. tlen += slen; /* if failed, make more room */
  99. } while (result == 0);
  100. if ((result == 1) && (err == U_ZERO_ERROR)) {
  101. memcpy (t, *v, nlen);
  102. t[nlen+reallen] = '\0';
  103. free (*v);
  104. *v = t;
  105. } else {
  106. free (t);
  107. }
  108. ucnv_reset (converter); /* back to initial shift state */
  109. }
  110. }
  111. ucnv_close (converter);
  112. }
  113. if (U_FAILURE(err)) {
  114. dsgw_error(DSGW_ERR_CHARSET_NOT_SUPPORTED, charset, 0, 0, 0);
  115. }
  116. break;
  117. }
  118. }
  119. }
  120. /* Read in the variables from stdin, unescape them, and then put them in
  121. * the static vector.
  122. *
  123. * Return 0 if all goes well; DSGW error code otherwise
  124. */
  125. int
  126. dsgw_post_begin(FILE *in)
  127. {
  128. char *ct, *vars = NULL, *tmp = NULL;
  129. int cl;
  130. if (( ct = getenv( "CONTENT_TYPE" )) == NULL ||
  131. strcasecmp( ct, "application/x-www-form-urlencoded" ) != 0 ||
  132. ( tmp = getenv( "CONTENT_LENGTH" )) == NULL ) {
  133. return( DSGW_ERR_BADFORMDATA );
  134. }
  135. cl = atoi(tmp);
  136. vars = (char *)dsgw_ch_malloc(cl+1);
  137. if ( fread(vars, 1, cl, in) != cl ) {
  138. return( DSGW_ERR_BADFORMDATA );
  139. }
  140. vars[cl] = '\0';
  141. #ifdef DSGW_DEBUG
  142. dsgw_log ("vars=\"%s\"\n", vars);
  143. #endif
  144. formvars = dsgw_string_to_vec (vars);
  145. free( vars );
  146. dsgw_vec_convert (formvars);
  147. #ifdef DSGW_DEBUG
  148. dsgw_logstringarray( "formvars", formvars );
  149. if (0) {
  150. char** var = formvars;
  151. if (var) {
  152. printf ("Content-type: text/html;charset=UTF-8\n\n<HTML><BODY>\n");
  153. for (; *var; ++var) {
  154. printf ("%s<br>\n", *var);
  155. }
  156. printf ("</BODY></HTML>\n");
  157. exit (1);
  158. }
  159. }
  160. #endif
  161. return( 0 );
  162. }
  163. /* Unescape the %xx variables as they're sent in. */
  164. void
  165. dsgw_form_unescape(char *str)
  166. {
  167. register int x = 0, y = 0;
  168. int l = strlen(str);
  169. char digit;
  170. while(x < l) {
  171. if((str[x] == '%') && (x < (l - 2))) {
  172. ++x;
  173. digit = (str[x] >= 'A' ?
  174. ((str[x] & 0xdf) - 'A')+10 : (str[x] - '0'));
  175. digit *= 16;
  176. ++x;
  177. digit += (str[x] >= 'A' ?
  178. ((str[x] & 0xdf) - 'A')+10 : (str[x] - '0'));
  179. str[y] = digit;
  180. }
  181. else if(str[x] == '+') {
  182. str[y] = ' ';
  183. } else {
  184. str[y] = str[x];
  185. }
  186. x++;
  187. y++;
  188. }
  189. str[y] = '\0';
  190. }
  191. /* Return the value of a POSTed variable, or NULL if none was sent. */
  192. char *
  193. dsgw_get_cgi_var(char *varname, int required)
  194. {
  195. register int x = 0;
  196. int len = strlen(varname);
  197. char *ans = NULL;
  198. while(formvars != NULL && formvars[x]) {
  199. /* We want to get rid of the =, so len, len+1 */
  200. if((!strncmp(formvars[x], varname, len)) &&
  201. (*(formvars[x]+len) == '=')) {
  202. ans = dsgw_ch_strdup(formvars[x] + len + 1);
  203. if(!strcmp(ans, "")) {
  204. free(ans);
  205. ans = NULL;
  206. }
  207. break;
  208. } else
  209. x++;
  210. }
  211. if ( required == DSGW_CGIVAR_REQUIRED && ans == NULL ) {
  212. char errbuf[ 256 ];
  213. PR_snprintf( errbuf, 256,
  214. XP_GetClientStr(DBT_missingFormDataElement100s_), varname );
  215. dsgw_error( DSGW_ERR_BADFORMDATA, errbuf, DSGW_ERROPT_EXIT, 0, NULL );
  216. }
  217. return ans;
  218. }
  219. /*
  220. * Return integer equivalent of POSTed value. If no variable POSTed,
  221. * return defval.
  222. */
  223. int
  224. dsgw_get_int_var( char *varname, int required, int defval )
  225. {
  226. char *val;
  227. int rc;
  228. if (( val = dsgw_get_cgi_var( varname, required )) == NULL ) {
  229. rc = defval;
  230. } else {
  231. rc = atoi( val );
  232. free( val );
  233. }
  234. return( rc );
  235. }
  236. /*
  237. * Return non-zero if POSTed variable is "true" or "yes". If !required
  238. * and no variable POSTed, return defval.
  239. */
  240. int
  241. dsgw_get_boolean_var( char *varname, int required, int defval )
  242. {
  243. char *val;
  244. int rc;
  245. if (( val = dsgw_get_cgi_var( varname, required )) == NULL ) {
  246. rc = defval;
  247. } else {
  248. rc = ( strcasecmp( val, "true" ) == 0 ||
  249. strcasecmp( val, "yes" ) == 0 );
  250. free( val );
  251. }
  252. return( rc );
  253. }
  254. /*
  255. * If a CGI variable named "varname_escaped" was POST'd, unescape it and
  256. * return its value.
  257. * Otherwise if "varname" is not NULL and a CGI variable called "varname"
  258. * was POST'd, return its value.
  259. * Otherwise return NULL.
  260. */
  261. char *
  262. dsgw_get_escaped_cgi_var( char *varname_escaped, char *varname, int required )
  263. {
  264. char *val;
  265. if (( val = dsgw_get_cgi_var( varname_escaped,
  266. ( varname == NULL ) ? required: DSGW_CGIVAR_OPTIONAL )) != NULL ) {
  267. dsgw_form_unescape( val );
  268. } else if ( varname != NULL ) {
  269. val = dsgw_get_cgi_var( varname, required );
  270. }
  271. return( val );
  272. }
  273. /* Convert the input from stdin to a usable variable vector. */
  274. static char **
  275. dsgw_string_to_vec(char *in)
  276. {
  277. char **ans;
  278. int vars = 0;
  279. register int x = 0;
  280. char *tmp;
  281. while(in[x])
  282. if(in[x++]=='=')
  283. vars++;
  284. ans = (char **) dsgw_ch_malloc((sizeof(char *)) * (vars+1));
  285. x=0;
  286. /* strtok() is not MT safe, but it is okay to call here because it is used in monothreaded env */
  287. tmp = strtok(in, "&");
  288. ans[x]=dsgw_ch_strdup(tmp);
  289. dsgw_form_unescape(ans[x++]);
  290. while((tmp = strtok(NULL, "&"))) {
  291. if ( strchr( tmp, '=' ) == NULL ) {
  292. break;
  293. }
  294. ans[x] = dsgw_ch_strdup(tmp);
  295. dsgw_form_unescape(ans[x++]);
  296. }
  297. ans[x] = NULL;
  298. return(ans);
  299. }
  300. /*
  301. * Step through all the CGI POSTed variables. A malloc'd copy of the variable
  302. * name is returned and *valuep is set to point to the value (not malloc'd).
  303. * If there are no more variables, NULL is returned.
  304. *
  305. * The first time this is called, *indexp should be zero. On subsequent
  306. * calls, pass the same indexp as on the first call.
  307. */
  308. char *
  309. dsgw_next_cgi_var( int *indexp, char **valuep )
  310. {
  311. char *name;
  312. int namelen;
  313. if ( formvars == NULL || formvars[ *indexp ] == NULL ) {
  314. return( NULL );
  315. }
  316. if (( *valuep = strchr( formvars[ *indexp ], '=' )) == NULL ) {
  317. namelen = strlen( formvars[ *indexp ] );
  318. } else {
  319. namelen = *valuep - formvars[ *indexp ];
  320. ++(*valuep);
  321. }
  322. name = dsgw_ch_malloc( namelen + 1 );
  323. memcpy( name, formvars[ *indexp ], namelen );
  324. name[ namelen ] = '\0';
  325. *indexp += 1;
  326. return( name );
  327. }
  328. /*
  329. * converts a buffer of characters to/from UTF8 from/to a native charset
  330. * the given converter will handle the native charset
  331. * returns 0 if not all of source was converted, 1 if all of source
  332. * was converted, -1 upon error
  333. * all of source will be converted if there is enough room in dest to contain
  334. * the entire conversion, or if dest is null and we are malloc'ing space for dest
  335. */
  336. int
  337. dsgw_convert(
  338. int direction, /* false for native->utf8, true for utf8->native */
  339. UConverter *nativeConv, /* convert from/to native charset */
  340. char **dest, /* *dest is the destination buffer - if *dest == NULL, it will be malloced */
  341. size_t destSize, /* size of dest buffer (ignored if *dest == NULL) */
  342. size_t *nDest, /* number of chars written to dest */
  343. const char *source, /* source buffer to convert - either in native encoding (from) or utf8 (to) */
  344. size_t sourceSize, /* size of source buffer - if 0, assume source is NULL terminated */
  345. size_t *nSource, /* number of chars read from source buffer */
  346. UErrorCode *pErrorCode /* will be reset each time through */
  347. )
  348. {
  349. #define CHUNK_SIZE 1024
  350. UChar pivotBuffer[CHUNK_SIZE];
  351. UChar *pivot, *pivot2;
  352. static UConverter *utf8Converter = NULL;
  353. UConverter *inConverter, *outConverter;
  354. char *myDest;
  355. const char *mySource;
  356. const char *destLimit;
  357. const char *sourceLimit;
  358. int destAlloc = 0; /* set to true if we allocated *dest */
  359. *pErrorCode = U_ZERO_ERROR;
  360. if(sourceSize<0 || source==NULL || nDest==NULL || nSource==NULL)
  361. {
  362. *pErrorCode=U_ILLEGAL_ARGUMENT_ERROR;
  363. return -1;
  364. }
  365. *nSource = 0;
  366. *nDest = 0;
  367. /* if source size is 0, assume source is null terminated and use strlen */
  368. if(sourceSize==0) {
  369. sourceSize = strlen(source);
  370. }
  371. /* create the converters */
  372. if (!utf8Converter) {
  373. utf8Converter = ucnv_open(UNICODE_ENCODING_UTF_8, pErrorCode);
  374. if(U_FAILURE(*pErrorCode)) {
  375. return -1;
  376. }
  377. }
  378. /* reset utf8Converter if done or error */
  379. if (direction) {
  380. inConverter = utf8Converter; /* source is utf8 */
  381. outConverter = nativeConv; /* dest is native charset */
  382. } else {
  383. inConverter = nativeConv; /* source is native charset */
  384. outConverter = utf8Converter; /* dest is utf8 */
  385. }
  386. /* if dest is NULL, allocate space for it - may be reallocated later */
  387. if (!*dest) {
  388. /* good approximation of size is n chars in source * max dest char size */
  389. destSize = ucnv_getMaxCharSize(outConverter) * sourceSize;
  390. *dest = dsgw_ch_malloc(destSize);
  391. destAlloc = 1;
  392. }
  393. /* set up the other variables */
  394. mySource = source;
  395. sourceLimit = source + sourceSize;
  396. pivot = pivot2 = pivotBuffer;
  397. myDest = *dest;
  398. destLimit = *dest + destSize;
  399. /*
  400. * loops until the input buffer is completely consumed
  401. * or an error is encountered;
  402. * first we convert from inConverter codepage to Unicode
  403. * then from Unicode to outConverter codepage
  404. */
  405. do {
  406. pivot = pivotBuffer;
  407. ucnv_toUnicode(inConverter,
  408. &pivot, pivotBuffer + CHUNK_SIZE,
  409. &mySource, sourceLimit,
  410. NULL,
  411. TRUE,
  412. pErrorCode);
  413. /* U_BUFFER_OVERFLOW_ERROR only means that the pivot buffer is full */
  414. if(U_SUCCESS(*pErrorCode) || (*pErrorCode == U_BUFFER_OVERFLOW_ERROR)) {
  415. pivot2 = pivotBuffer;
  416. /* convert and write bytes from the pivot buffer to the dest -
  417. if dest is allocated and we run out of space in dest, grow
  418. dest and try again - otherwise, just bail out and let the
  419. caller know that their dest buffer is full and they need
  420. to try again */
  421. do {
  422. *pErrorCode = U_ZERO_ERROR;
  423. ucnv_fromUnicode(outConverter,
  424. &myDest, destLimit,
  425. (const UChar **)&pivot2, pivot,
  426. NULL,
  427. (UBool)(mySource == sourceLimit),
  428. pErrorCode);
  429. /* we overflowed dest and dest is allocated, so let's increase
  430. the dest size */
  431. if ((*pErrorCode == U_BUFFER_OVERFLOW_ERROR) && destAlloc) {
  432. /* figure out where myDest was pointing */
  433. size_t myDestOffset = myDest - *dest;
  434. /* probably don't need this much more room . . . */
  435. destSize += CHUNK_SIZE;
  436. /* realloc *dest for new size */
  437. *dest = dsgw_ch_realloc(*dest, destSize);
  438. /* reset myDest in new *dest */
  439. myDest = *dest + myDestOffset;
  440. /* set new destLimit */
  441. destLimit = *dest + destSize;
  442. } else {
  443. break; /* skip it */
  444. }
  445. } while(*pErrorCode == U_BUFFER_OVERFLOW_ERROR);
  446. /*
  447. * If this overflows the fixed size dest, then we must stop
  448. * converting and return what we already have
  449. * in this case, pErrorCode will be buffer overflow error because
  450. * we have overflowed the dest buffer
  451. * the outer while loop will break because !U_SUCCESS
  452. */
  453. }
  454. } while(U_SUCCESS(*pErrorCode) && source != sourceLimit);
  455. *nSource = mySource - source; /* n chars read from source */
  456. *nDest = myDest - *dest; /* n chars written to dest */
  457. if (U_SUCCESS(*pErrorCode) && source == sourceLimit) {
  458. /* reset internal converter */
  459. ucnv_reset(utf8Converter);
  460. return 1; /* converted entire string */
  461. }
  462. if (source != sourceLimit) {
  463. /* not done with conversion yet */
  464. /* no reset here - preserve state for next call */
  465. return 0;
  466. }
  467. /* error */
  468. ucnv_reset(utf8Converter);
  469. return -1;
  470. }