cgi.c
上传用户:s81996212
上传日期:2007-01-04
资源大小:722k
文件大小:12k
源码类别:

WEB邮件程序

开发平台:

C/C++

  1. /*
  2. ** Copyright 1998 - 1999 Double Precision, Inc.
  3. ** See COPYING for distribution information.
  4. */
  5. /*
  6. ** $Id: cgi.c,v 1.18 2000/06/17 18:41:13 mrsam Exp $
  7. */
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include "cgi.h"
  12. #if HAVE_CONFIG_H
  13. #include "config.h"
  14. #endif
  15. #if HAVE_UNISTD_H
  16. #include <unistd.h>
  17. #endif
  18. #if TIME_WITH_SYS_TIME
  19. #include <sys/time.h>
  20. #include <time.h>
  21. #else
  22. #if HAVE_SYS_TIME_H
  23. #include <sys/time.h>
  24. #else
  25. #include <time.h>
  26. #endif
  27. #endif
  28. #ifndef CGIMAXARG
  29. #define CGIMAXARG 500000
  30. #endif
  31. #ifndef CGIMAXFORMDATAARG
  32. #define CGIMAXFORMDATAARG 2000000
  33. #endif
  34. #if CGIMAXARG < 256
  35. #error CGIMAXARG too small
  36. #endif
  37. #if CGIMAXFORMDATAARG < 1024
  38. #error CGIMAXFORMDATAARG too small
  39. #endif
  40. #if CGIFORMDATA
  41. #include <fcntl.h>
  42. #include <sys/types.h>
  43. #include <sys/stat.h>
  44. #include "rfc2045/rfc2045.h"
  45. static void cgi_formdata(unsigned long);
  46. #ifndef HAVE_STRNCASECMP
  47. extern int strncasecmp(const char *, const char *, size_t);
  48. #endif
  49. static int cgiformfd;
  50. static char hascgiformfd=0;
  51. static struct rfc2045 *rfc2045p=0;
  52. #endif
  53. extern void error(const char *);
  54. static void enomem()
  55. {
  56. error("Out of memory.");
  57. }
  58. static char *cgi_args=0;
  59. struct cgi_arglist *cgi_arglist=0;
  60. /*
  61. ** Set up CGI arguments.  Initializes cgi_arglist link list.
  62. **
  63. ** arg1<NUL>value1<NUL>arg2<NUL>value2<NUL> ... argn<NUL>valuen<NUL><NUL>
  64. */
  65. static void cgi_setup_1();
  66. void cgi_setup()
  67. {
  68. struct cgi_arglist *p;
  69. cgi_setup_1();
  70. /* Initialize the prev pointer */
  71. for (p=cgi_arglist; p; p=p->next)
  72. if (p->next)
  73. p->next->prev=p;
  74. }
  75. static void cgi_setup_1()
  76. {
  77. char *p=getenv("REQUEST_METHOD"), *q, *r;
  78. char *args;
  79. unsigned long cl;
  80. int c;
  81. struct cgi_arglist *argp;
  82. #if HAVE_LIBFCGI
  83. hascgiformfd=0;
  84. rfc2045p=0;
  85. if(cgi_args) {
  86. free(cgi_args);
  87. cgi_args=0;
  88. }
  89. if(cgi_arglist) {
  90. struct cgi_arglist *argp,*argP;
  91. for (argP=argp=cgi_arglist; argp; argp=argP) {
  92. /* argp->argname and argp->argvalue are a actually cgi_args,
  93.  * so don't free them
  94.  */
  95. argP = argp->next;
  96. if(argp) free(argp);
  97. }
  98. cgi_arglist=0;
  99. }
  100. #endif
  101. if (p && strcmp(p, "GET") == 0) /* This is a GET post */
  102. {
  103. args=getenv("QUERY_STRING");
  104. if (!args) return;
  105. if (strlen(args) > CGIMAXARG) enomem();
  106. cgi_args=malloc(strlen(args)+1); /* Extra insurance */
  107. if (!cgi_args) return;
  108. strcpy(cgi_args,args);
  109. args=cgi_args;
  110. }
  111. else if (p && strcmp(p, "POST") == 0)
  112. {
  113. args=getenv("CONTENT_TYPE");
  114. if (!args) return;
  115. #if CGIFORMDATA
  116. if (strncasecmp(args,"multipart/form-data;", 20) == 0)
  117. {
  118. args=getenv("CONTENT_LENGTH");
  119. if (!args) return;
  120. cl=atol(args);
  121. if (cl > CGIMAXFORMDATAARG)
  122. {
  123. printf("Content-Type: text/htmlnn");
  124. printf("<HTML><BODY><H1>Attachment size exceeds limit set by system administrator</H1></BODY></HTML>n");
  125. fake_exit(1);
  126. }
  127. cgi_formdata(cl);
  128. return;
  129. }
  130. #endif
  131. if (strcmp(args,"application/x-www-form-urlencoded"))
  132. return;
  133. args=getenv("CONTENT_LENGTH");
  134. if (!args) return;
  135. cl=atol(args);
  136. if (cl > CGIMAXARG)
  137. {
  138. printf("Content-Type: text/htmlnn");
  139. printf("<HTML><BODY><H1>Message size exceeds limit set by system administrator</H1></BODY></HTML>n");
  140. fake_exit(1);
  141. }
  142. cgi_args=malloc(cl+1); /* Extra insurance */
  143. if (!cgi_args) return;
  144. q=cgi_args;
  145. while (cl)
  146. {
  147. c=getchar();
  148. if (c < 0)
  149. {
  150. free(cgi_args);
  151. cgi_args=0;
  152. return;
  153. }
  154. *q++=c;
  155. --cl;
  156. }
  157. *q=0;
  158. args=cgi_args;
  159. }
  160. else return;
  161. q=args;
  162. while (*q)
  163. {
  164. argp=malloc(sizeof(*cgi_arglist));
  165. if (!argp) enomem();
  166. argp->next=cgi_arglist;
  167. cgi_arglist=argp;
  168. argp->argname=q;
  169. argp->argvalue="";
  170. p=q;
  171. while (*q && *q != '&')
  172. q++;
  173. if (*q) *q++=0;
  174. if ((r=strchr(p, '=')) != 0)
  175. {
  176. *r++='';
  177. argp->argvalue=r;
  178. cgiurldecode(r);
  179. }
  180. cgiurldecode(p);
  181. }
  182. }
  183. static char *cgiurlencode_common(const char *buf, const char *punct)
  184. {
  185. char *newbuf=0;
  186. size_t cnt=0;
  187. int pass;
  188. const char *p;
  189. static const char hex[]="0123456789ABCDEF";
  190. for (pass=0; pass<2; pass++)
  191. {
  192. if (pass && (newbuf=malloc(cnt+1)) == 0) enomem();
  193. cnt=0;
  194. for (p=buf; *p; p++)
  195. {
  196. if (strchr(punct, *p) || *p < 32 || *p >= 127)
  197. {
  198. if (pass)
  199. {
  200. newbuf[cnt]='%';
  201. newbuf[cnt+1]=hex[
  202. ((int)(unsigned char)*p) / 16];
  203. newbuf[cnt+2]=hex[ *p & 15 ];
  204. }
  205. cnt += 3;
  206. continue;
  207. }
  208. if (pass)
  209. newbuf[cnt]= *p == ' ' ? '+':*p;
  210. ++cnt;
  211. }
  212. }
  213. newbuf[cnt]=0;
  214. return (newbuf);
  215. }
  216. char *cgiurlencode(const char *buf)
  217. {
  218. return (cgiurlencode_common(buf, ""?;<>&=/:%+#"));
  219. }
  220. char *cgiurlencode_noamp(const char *buf)
  221. {
  222. return (cgiurlencode_common(buf, ""?<>=/:%+#"));
  223. }
  224. char *cgiurlencode_noeq(const char *buf)
  225. {
  226. return (cgiurlencode_common(buf, ""?;<>&/:%+#"));
  227. }
  228. void cgi_cleanup()
  229. {
  230. #if CGIFORMDATA
  231. if (hascgiformfd)
  232. {
  233. close(cgiformfd);
  234. hascgiformfd=0;
  235. }
  236. #endif
  237. }
  238. const char *cgi(const char *arg)
  239. {
  240. struct cgi_arglist *argp;
  241. for (argp=cgi_arglist; argp; argp=argp->next)
  242. if (strcmp(argp->argname, arg) == 0)
  243. return (argp->argvalue);
  244. return ("");
  245. }
  246. char *cgi_multiple(const char *arg, const char *sep)
  247. {
  248. struct cgi_arglist *argp;
  249. size_t l=1;
  250. char *buf;
  251. for (argp=cgi_arglist; argp; argp=argp->next)
  252. if (strcmp(argp->argname, arg) == 0)
  253. l += strlen(argp->argvalue)+strlen(sep);
  254. buf=malloc(l);
  255. if (!buf) return(0);
  256. *buf=0;
  257. /*
  258. ** Because the cgi list is build from the tail end up, we go backwards
  259. ** now, so that we return options in the same order they were selected.
  260. */
  261. argp=cgi_arglist;
  262. while (argp && argp->next)
  263. argp=argp->next;
  264. for (; argp; argp=argp->prev)
  265. if (strcmp(argp->argname, arg) == 0)
  266. {
  267. if (*buf) strcat(buf, sep);
  268. strcat(buf, argp->argvalue);
  269. }
  270. return (buf);
  271. }
  272. static char *nybble(char *p, int *n)
  273. {
  274. if ( *p >= '0' && *p <= '9')
  275. (*n) = (*n) * 16 + (*p++ - '0');
  276. else if ( *p >= 'A' && *p <= 'F')
  277. (*n) = (*n) * 16 + (*p++ - 'A' + 10);
  278. return (p);
  279. }
  280. void cgiurldecode(char *q)
  281. {
  282. char *p=q;
  283. int c;
  284. while (*q)
  285. {
  286. /*
  287. ** 0xA0 - Win32 versions of navigator convert TEXTAREA fillins
  288. ** of &nbsp; to 0xA0.
  289. */
  290. if (*q == '+' || *q == (char)0xA0)
  291. {
  292. *p++=' ';
  293. q++;
  294. continue;
  295. }
  296. if (*q != '%')
  297. {
  298. *p++=*q++;
  299. continue;
  300. }
  301. ++q;
  302. c=0;
  303. q=nybble(q, &c);
  304. q=nybble(q, &c);
  305. if ((char)c == (char)0xA0) c=' ';
  306. /* See above */
  307. if (c && c != 'r')
  308. /* Ignore CRs we get in TEXTAREAS */
  309. *p++=c;
  310. }
  311. *p++=0;
  312. }
  313. void cgi_put(const char *cginame, const char *cgivalue)
  314. {
  315. struct cgi_arglist *argp;
  316. for (argp=cgi_arglist; argp; argp=argp->next)
  317. if (strcmp(argp->argname, cginame) == 0)
  318. {
  319. argp->argvalue=cgivalue;
  320. return;
  321. }
  322. argp=malloc(sizeof(*cgi_arglist));
  323. if (!argp) enomem();
  324. argp->next=cgi_arglist;
  325. argp->prev=0;
  326. if (argp->next)
  327. argp->next->prev=argp;
  328. cgi_arglist=argp;
  329. argp->argname=cginame;
  330. argp->argvalue=cgivalue;
  331. }
  332. #if CGIFORMDATA
  333. /**************************************************************************/
  334. /* multipart/formdata decoding */
  335. static const char *disposition_name, *disposition_filename;
  336. static char *formargbuf;
  337. static char *formargptr;
  338. static int save_formdata(const char *p, size_t l, void *miscptr)
  339. {
  340. memcpy(formargptr, p, l);
  341. formargptr += l;
  342. return (0);
  343. }
  344. static void cgiformdecode(struct rfc2045 *p, struct rfc2045id *a, void *b)
  345. {
  346. const char *disposition;
  347. off_t start_pos, end_pos, start_body;
  348. char buf[512];
  349. int n;
  350. off_t dummy;
  351. a=a;
  352. b=b;
  353. rfc2045_dispositioninfo(p, &disposition, &disposition_name,
  354. &disposition_filename);
  355. if (!disposition || strcmp(disposition, "form-data")) return;
  356. if (!disposition_name || !*disposition_name) return;
  357. if (!disposition_filename || !*disposition_filename)
  358. {
  359. rfc2045_mimepos(p, &start_pos, &end_pos, &start_body,
  360. &dummy, &dummy);
  361. if (lseek(cgiformfd, start_body, SEEK_SET) == -1)
  362. enomem();
  363. formargbuf=malloc(end_pos - start_body+1);
  364. if (!formargbuf) enomem();
  365. formargptr=formargbuf;
  366. rfc2045_cdecode_start(p, &save_formdata, 0);
  367. while (start_body < end_pos)
  368. {
  369. n=sizeof(buf);
  370. if (n > end_pos - start_body)
  371. n=end_pos-start_body;
  372. n=read(cgiformfd, buf, n);
  373. if (n <= 0) enomem();
  374. rfc2045_cdecode(p, buf, n);
  375. start_body += n;
  376. }
  377. rfc2045_cdecode_end(p);
  378. *formargptr=0;
  379. {
  380. char *name=strdup(disposition_name);
  381. char *value=strdup(formargbuf);
  382. char *p, *q;
  383. /* Just like for GET/POSTs, strip CRs and convert
  384. ** 0xA0 to 0x20. */
  385. for (p=q=value; *p; p++)
  386. {
  387. if (*p == 'r') continue;
  388. *q++ = (char)*p == (char)0xA0 ? ' ':*p;
  389. }
  390. *q++='';
  391. cgi_put(name, value);
  392. }
  393. free(formargbuf);
  394. }
  395. }
  396. static const char *cgitempdir="/tmp";
  397. void cgiformdatatempdir(const char *p)
  398. {
  399. cgitempdir=p;
  400. }
  401. static void cgiformfdw(const char *p, size_t n)
  402. {
  403. while (n)
  404. {
  405. int k=write(cgiformfd, p, n);
  406. if (k <= 0) enomem();
  407. p += k;
  408. n -= k;
  409. }
  410. }
  411. static void cgi_formdata(unsigned long contentlength)
  412. {
  413. char pidbuf[MAXLONGSIZE];
  414. char timebuf[MAXLONGSIZE];
  415. char cntbuf[MAXLONGSIZE];
  416. time_t t;
  417. unsigned long cnt;
  418. int n;
  419. char *filename, *p;
  420. static const char fakeheader[]="MIME-Version: 1.0nContent-Type: ";
  421. char buf[BUFSIZ];
  422. sprintf(pidbuf, "%lu", (unsigned long)getpid());
  423. time(&t);
  424. sprintf(timebuf, "%lu", (unsigned long)t);
  425. cnt=0;
  426. buf[sizeof(buf)-1]=0;
  427. if (gethostname(buf, sizeof(buf)-1) != 0)
  428. buf[0]='';
  429. do
  430. {
  431. sprintf(cntbuf, "%lu", (unsigned long)cnt);
  432. filename=malloc(strlen(pidbuf)+strlen(timebuf)+strlen(cntbuf)
  433. +strlen(cgitempdir)+strlen(buf)+10);
  434. if (!filename) enomem();
  435. sprintf(filename, "%s/%s.%s_%s.%s", cgitempdir,
  436. timebuf, pidbuf, cntbuf, buf);
  437. cgiformfd=open(filename, O_RDWR | O_CREAT | O_EXCL, 0644);
  438. } while (cgiformfd < 0);
  439. unlink(filename); /* !!!MUST WORK!!! */
  440. hascgiformfd=1;
  441. p=getenv("CONTENT_TYPE");
  442. free(filename);
  443. cgiformfdw(fakeheader, strlen(fakeheader));
  444. cgiformfdw(p, strlen(p));
  445. cgiformfdw("nn", 2);
  446. clearerr(stdin);
  447. while (contentlength)
  448. {
  449. n=sizeof(buf);
  450. if (n > contentlength) n=contentlength;
  451. n=fread(buf, 1, n, stdin);
  452. if (n <= 0)
  453. enomem();
  454. cgiformfdw(buf, n);
  455. contentlength -= n;
  456. }
  457. rfc2045p=rfc2045_alloc();
  458. lseek(cgiformfd, 0L, SEEK_SET);
  459. while ((n=read(cgiformfd, buf, sizeof(buf))) > 0)
  460. rfc2045_parse(rfc2045p, buf, n);
  461. rfc2045_decode(rfc2045p, &cgiformdecode, 0);
  462. }
  463. struct cgigetfileinfo {
  464. int (*start_file)(const char *, const char *);
  465. int (*file)(const char *, size_t, void *);
  466. void (*end_file)(void);
  467. size_t filenum;
  468. } ;
  469. static void cgifiledecode(struct rfc2045 *p, struct rfc2045id *a, void *b)
  470. {
  471. const char *disposition;
  472. off_t start_pos, end_pos, start_body;
  473. char buf[512];
  474. int n;
  475. struct cgigetfileinfo *c;
  476. off_t dummy;
  477. a=a;
  478. c=(struct cgigetfileinfo *)b;
  479. if (c->filenum == 0) return; /* Already retrieved this one. */
  480. rfc2045_dispositioninfo(p, &disposition, &disposition_name,
  481. &disposition_filename);
  482. if (!disposition || strcmp(disposition, "form-data")) return;
  483. if (!disposition_name || !*disposition_name) return;
  484. if (!disposition_filename || !*disposition_filename) return;
  485. rfc2045_mimepos(p, &start_pos, &end_pos, &start_body,
  486. &dummy, &dummy);
  487. if (start_body == end_pos) /* NULL FILE */
  488. return;
  489. if ( --c->filenum ) return; /* Not this one */
  490. if ( (*c->start_file)(disposition_name, disposition_filename) )
  491. return;
  492. if (lseek(cgiformfd, start_body, SEEK_SET) == -1)
  493. enomem();
  494. rfc2045_cdecode_start(p, c->file, 0);
  495. while (start_body < end_pos)
  496. {
  497. n=sizeof(buf);
  498. if (n > end_pos - start_body)
  499. n=end_pos-start_body;
  500. n=read(cgiformfd, buf, n);
  501. if (n <= 0) enomem();
  502. rfc2045_cdecode(p, buf, n);
  503. start_body += n;
  504. }
  505. rfc2045_cdecode_end(p);
  506. (*c->end_file)();
  507. }
  508. int cgi_getfiles( int (*start_file)(const char *, const char *),
  509. int (*file)(const char *, size_t, void *),
  510. void (*end_file)(void), size_t filenum)
  511. {
  512. struct cgigetfileinfo gfi={start_file, file, end_file, filenum};
  513. if (rfc2045p) rfc2045_decode(rfc2045p, &cgifiledecode, &gfi);
  514. if (gfi.filenum) return (-1);
  515. return (0);
  516. }
  517. #endif