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

WEB邮件程序

开发平台:

C/C++

  1. /*
  2. ** Copyright 1998 - 1999 Double Precision, Inc.  See COPYING for
  3. ** distribution information.
  4. */
  5. /*
  6. ** $Id: html.c,v 1.12 2000/05/11 03:55:49 mrsam Exp $
  7. */
  8. #include <stdio.h>
  9. #include <ctype.h>
  10. #include <string.h>
  11. #include <stdlib.h>
  12. #include "config.h"
  13. #include "cgi/cgi.h"
  14. #include "sqwebmail.h"
  15. #include "rfc2045/rfc2045.h"
  16. void decodehtmlchar(char *p)
  17. {
  18. char *q;
  19. for (q=p; *p; )
  20. {
  21. int i;
  22. if (*p != '&')
  23. {
  24. *q++=*p++;
  25. continue;
  26. }
  27. if ( p[1] == '#')
  28. {
  29. unsigned c=0;
  30. for (p += 2; isdigit((int)(unsigned char)*p); p++)
  31. c=c * 10 + (*p++ - '0');
  32. c=(unsigned char)c;
  33. if (c) *q++=c;
  34. if (*p == ';') p++;
  35. }
  36. for (i=1; p[i]; i++)
  37. if (!isalpha((int)(unsigned char)p[i])) break;
  38. if (p[i] != ';')
  39. {
  40. *q++=*p++;
  41. continue;
  42. }
  43. for (i=0; p[i] != ';'; i++)
  44. p[i]=tolower(p[i]);
  45. ++i;
  46. if (strncmp(p, "&lt;", 4) == 0)
  47. {
  48. *q++ = '<';
  49. }
  50. else if ( strncmp(p, "&gt;",4) == 0)
  51. {
  52. *q++ = '>';
  53. }
  54. else if ( strncmp(p, "&amp;", 5) == 0)
  55. {
  56. *q++ = '&';
  57. }
  58. else if ( strncmp(p, "&quot;", 6) == 0)
  59. {
  60. *q++ = '"';
  61. }
  62. p += i;
  63. }
  64. *q=0;
  65. }
  66. /*
  67. HTML sanitization filter.  Transforms HTML as follows:
  68. The following tags are dropped:
  69. <SCRIPT>, </SCRIPT>, <APP>, </APP>, <APPLET>, </APPLET>, 
  70. <SERVER>, </SERVER>, <OBJECT>, </OBJECT>, <HTML>, </HTML>, 
  71. <HEAD>, </HEAD>, <BODY>, </BODY>, <META>, <TITLE>, </TITLE>,
  72. <FRAME>, </FRAME>, <LINK> <IFRAME> and </IFRAME>.
  73. The ONLOAD, ONMOUSEOVER, and all other ON* attributes are removed.
  74. Attributes TARGET, CODE, ACTION, CODETYPE and LANGUAGE are removed.
  75. TARGET=_blank is added to all <A> tags.
  76. HREF, SRC, or LOWSRC attributes that do not specify a URL of http:,
  77. https:, ftp:, gopher:, wais:, or telnet:, are removed.
  78. */
  79. char *tagbuf=0;
  80. size_t tagbufsize=0, tagbuflen;
  81. struct tagattrinfo {
  82. char *tagname;
  83. size_t tagnamelen;
  84. char *tagvalue;
  85. size_t tagvaluelen;
  86. char *atagstart; /* Entire tag=value location */
  87. size_t ataglen;
  88. } ;
  89. struct tagattrinfo *tagattr=0;
  90. size_t tagattrsize=0, tagattrlen;
  91. static void addtagbuf(int c)
  92. {
  93. if (tagbufsize >= 1024) return; /* DOS attack - get rid of the tag */
  94. if (tagbuflen >= tagbufsize)
  95. {
  96. char *newtagbuf= tagbuf ? (char *)realloc(tagbuf, tagbufsize+256)
  97. :(char *)malloc(tagbufsize+256);
  98. if (!newtagbuf) enomem();
  99. tagbuf=newtagbuf;
  100. tagbufsize += 256;
  101. }
  102. tagbuf[tagbuflen++]=c;
  103. }
  104. /* Parse the contents of tagbuf into individual attributes.  If argument is
  105. ** NULL, just the count of attributes is returned.  That's the first pass.
  106. ** On the second pass the argument points to a struct tagattrinfo array which
  107. ** we initialize.
  108. **
  109. ** The first attribute is -- obviously -- the actual tag.
  110. */
  111. static size_t parseattr(struct tagattrinfo *tai)
  112. {
  113. size_t c=0;
  114. char *p;
  115. for (p=tagbuf; *p; )
  116. {
  117. while (*p && isspace((int)(unsigned char)*p)) p++;
  118. if (!*p) break;
  119. ++c;
  120. if (tai)
  121. {
  122. tai->tagname=p;
  123. tai->tagnamelen=0;
  124. tai->atagstart=p;
  125. }
  126. while (*p && !isspace((int)(unsigned char)*p) && *p != '=')
  127. {
  128. ++p;
  129. if (tai) ++tai->tagnamelen;
  130. }
  131. if (*p != '=') /* No attribute value */
  132. {
  133. if (tai)
  134. {
  135. tai->tagvalue=0;
  136. tai->tagvaluelen=0;
  137. }
  138. }
  139. else
  140. {
  141. ++p;
  142. if (*p == '"') /* Attr value in quotes */
  143. {
  144. ++p;
  145. if (tai)
  146. {
  147. tai->tagvalue=p;
  148. tai->tagvaluelen=0;
  149. }
  150. while (*p && *p != '"')
  151. {
  152. ++p;
  153. if (tai) ++tai->tagvaluelen;
  154. }
  155. if (*p) p++;
  156. }
  157. else
  158. {
  159. if (tai)
  160. {
  161. tai->tagvalue=p;
  162. tai->tagvaluelen=0;
  163. }
  164. while (*p && !isspace((int)(unsigned char)*p))
  165. {
  166. p++;
  167. if (tai)
  168. {
  169. tai->tagvalue=p;
  170. tai->tagvaluelen=0;
  171. }
  172. }
  173. }
  174. }
  175. if (tai)
  176. {
  177. tai->ataglen=p-tai->atagstart;
  178. ++tai;
  179. }
  180. }
  181. return (c);
  182. }
  183. static void parsetagbuf()
  184. {
  185.         tagattrlen=parseattr(0);
  186.         if ( tagattrlen > tagattrsize)
  187.         {
  188.         struct tagattrinfo *newta= tagattr ? (struct tagattrinfo *)
  189.                 realloc(tagattr, (tagattrlen+16)*sizeof(*tagattr))
  190.                 :(struct tagattrinfo *)
  191. malloc((tagattrlen+16)*sizeof(*tagattr));
  192.                 if (!newta)     enomem();
  193. tagattrsize=tagattrlen+16;
  194. tagattr=newta;
  195.         }
  196.         parseattr(tagattr);
  197. }
  198. /* See if this attribute is the one we're looking for */
  199. static int is_attr(struct tagattrinfo *i, const char *l)
  200. {
  201. size_t ll=strlen(l);
  202. return (i->tagnamelen == ll && strncasecmp(i->tagname, l, ll) == 0);
  203. }
  204. /* If this is the tag we're looking for */
  205. static int is_htmltag(const char *l)
  206. {
  207. return (tagattrlen ? is_attr(tagattr, l):0);
  208. }
  209. /* See if the attribute value starts with what we're looking for */
  210. static int is_valuestart(const char *v, const char *l)
  211. {
  212. while (v && isspace((int)(unsigned char)*v))
  213. ++v;
  214. return (v && strncasecmp(v, l, strlen(l)) == 0);
  215. }
  216. /*
  217. htmlfilter() is repeatedly called to filter the HTML text.  htmlfilter()
  218. will call htmlfiltered() with the filtered text, more or less on a
  219. one to one basis.
  220. htmlfilter_init() must be called before the first invocation of
  221. htmlfilter().  Because the HTML can be fed in arbitrary quantities,
  222. htmlfilter() implements a state machine which htmlfilter_init()
  223. initializes.
  224. */
  225. enum htmlstate {
  226. intext, /* Initial value.  In plain text */
  227. seenlt, /* Seen < */
  228. seenltbang, /* Seen <! */
  229. seenltbangdash, /* Seen <!- */
  230. intag, /* Seen < but not <!, we're collecting the
  231. tag */
  232. incomment, /* <!--, in a comment, have not seen any
  233. dashes */
  234. incommentseendash, /* In a comment, seen - */
  235. incommentseendashdash /* In a comment, seen -- */
  236. } ;
  237. static enum htmlstate cur_state;
  238. static unsigned instyletag;
  239. static void filtered_tag();
  240. static void (*htmlfiltered_func)(const char *, size_t);
  241. static char *(*htmlconvertcid_func)(const char *, void *);
  242. static void *convertcid_arg;
  243. const char *washlink=0;
  244. const char *washlinkmailto=0;
  245. const char *contentbase=0;
  246. void htmlfilter_init( void (*func)(const char *, size_t))
  247. {
  248. cur_state=intext;
  249. htmlfiltered_func=func;
  250. instyletag=0;
  251. }
  252. /* Set prefix to wash HTML links */
  253. void htmlfilter_washlink( const char *p)
  254. {
  255. washlink=p;
  256. }
  257. void htmlfilter_contentbase( const char *p)
  258. {
  259. contentbase=p;
  260. }
  261. void htmlfilter_washlinkmailto( const char *p)
  262. {
  263. washlinkmailto=p;
  264. }
  265. void htmlfilter_convertcid( char *(*cidfunc)(const char *, void *), void *arg)
  266. {
  267. htmlconvertcid_func=cidfunc;
  268. convertcid_arg=arg;
  269. }
  270. void htmlfilter(const char *p, size_t s)
  271. {
  272. size_t l;
  273. size_t start=0;
  274. for (l=0; l<s; l++)
  275. {
  276. switch (cur_state) {
  277. case intext:
  278. if (p[l] != '<') continue;
  279. if (!instyletag)
  280. (*htmlfiltered_func)(p+start, l-start);
  281. /* Output everything up until the tag */
  282. cur_state=seenlt;
  283. tagbuflen=0;
  284. break;
  285. case seenlt:
  286. if (p[l] == '>')
  287. {
  288. cur_state=intext;
  289. start=l+1;
  290. if (!instyletag)
  291. (*htmlfiltered_func)("<>", 2);
  292. /* Eh? */
  293. continue;
  294. }
  295. addtagbuf(p[l]);
  296. if (p[l] == '!')
  297. cur_state=seenltbang;
  298. else
  299. cur_state=intag;
  300. break;
  301. case intag:
  302. /* We're in a tag (not a <!-- comment)
  303. collect the contents in tagbuf, until > is seen */
  304. do_intag:
  305. cur_state=intag;
  306. if (p[l] == '>')
  307. {
  308. start=l+1;
  309. cur_state=intext;
  310. filtered_tag(); /* Filter this tag */
  311. continue;
  312. }
  313. addtagbuf(p[l]);
  314. continue;
  315. case seenltbang:
  316. /* We have <!.  If - is not here, this is a SGML tag */
  317. if (p[l] != '-') goto do_intag;
  318. addtagbuf(p[l]);
  319. cur_state=seenltbangdash;
  320. continue;
  321. case seenltbangdash:
  322. /* We have <!-. If - is not here, this is a SGML tag,
  323. otherweise we're in a comment, which we can pass
  324. along */
  325. if (p[l] != '-') goto do_intag;
  326. if (!instyletag)
  327. (*htmlfiltered_func)("<!--", 4);
  328. start=l+1;
  329. cur_state=incomment;
  330. continue;
  331. /* Look for end of comment */
  332. case incomment:
  333. if (p[l] == '-') cur_state=incommentseendash;
  334. continue;
  335. case incommentseendash:
  336. cur_state= p[l] == '-' ? incommentseendashdash
  337. :incomment;
  338. continue;
  339. case incommentseendashdash:
  340. if (p[l] == '-') continue;
  341. if (p[l] != '>')
  342. {
  343. cur_state=incomment;
  344. continue;
  345. }
  346. if (!instyletag)
  347. (*htmlfiltered_func)(p+start, l+1-start);
  348. cur_state=intext;
  349. start=l+1;
  350. continue;
  351. }
  352. }
  353. /* When we're done with this chunk, if we're doing plain text, or if
  354. ** we're in a comment, just pass it along */
  355. switch (cur_state) {
  356. case intext:
  357. case incomment:
  358. case incommentseendash:
  359. case incommentseendashdash:
  360. if (!instyletag)
  361. (*htmlfiltered_func)(p+start, l-start);
  362. default:
  363. break;
  364. }
  365. }
  366. /* Ok, wash an HREF link.  The entire A (or whatever) element is in tagbuf.
  367. ** tag=value pairs have been parsed into tagattr array.
  368. **
  369. ** Our argument is the index of the HREF (or SRC) link, which points back
  370. ** into tagbuf.
  371. **
  372. ** We build a new element, and then rebuild the tagbuf structure.
  373. */
  374. static char *redirectencode(const char *, size_t );
  375. /* replacelink takes care of replacing the contents of one tag's value. */
  376. static void replacelink(size_t l, const char *p)
  377. {
  378. struct tagattrinfo *tagattrp=tagattr+l;
  379. char *newbuf;
  380. size_t plen=tagattrp->tagvalue - tagbuf;
  381. size_t i;
  382. newbuf=malloc(strlen(tagbuf)+strlen(p)+1);
  383. /* Yes, that's a bit too much.  That's OK */
  384. if (!newbuf) enomem();
  385. memcpy(newbuf, tagbuf, plen);
  386. strcpy(newbuf+plen, p);
  387. strcat(newbuf, tagattrp->tagvalue+tagattrp->tagvaluelen);
  388. tagbuflen=0;
  389. for (i=0; newbuf[i]; i++)
  390. addtagbuf(newbuf[i]);
  391. addtagbuf(0);
  392. parsetagbuf();
  393. free(newbuf);
  394. }
  395. static void dowashlink(size_t l, const char *value)
  396. {
  397. char *url, *p;
  398. url=redirectencode(value, strlen(value));
  399. p=malloc(strlen(url)+strlen(washlink)+1);
  400. if (!p) enomem();
  401. strcat(strcpy(p, washlink), url);
  402. replacelink(l, p);
  403. free(p);
  404. free(url);
  405. }
  406. static void dowashlinkmailto(size_t l, const char *mailtolink)
  407. {
  408. size_t mailtolinklen=strlen(mailtolink);
  409. char *newlink=malloc(strlen(washlinkmailto)+1+mailtolinklen);
  410. char *p;
  411. if (!newlink) enomem();
  412. strcpy(newlink, washlinkmailto);
  413. strncat(newlink, mailtolink, mailtolinklen);
  414. if ((p=strchr(newlink+strlen(washlinkmailto), '?')) != 0)
  415. *p='&';
  416. replacelink(l, newlink);
  417. free(newlink);
  418. }
  419. static void dowashcid(size_t l, const char *link)
  420. {
  421. size_t linklen=strlen(link);
  422. char *p;
  423. p=malloc(linklen+1);
  424. if (!p) enomem();
  425. memcpy(p, link, linklen);
  426. p[linklen]=0;
  427. if (!htmlconvertcid_func)
  428. *p=0;
  429. else
  430. {
  431. char *q=(*htmlconvertcid_func)(p+4, convertcid_arg);
  432. free(p);
  433. p=q;
  434. }
  435. replacelink(l, p);
  436. free(p);
  437. }
  438. static char *redirectencode(const char *p, size_t l)
  439. {
  440. char *q=malloc(l+1);
  441. char *r;
  442. if (!q) enomem();
  443. memcpy(q, p, l);
  444. q[l]=0;
  445. decodehtmlchar(q);
  446. r=cgiurlencode(q);
  447. free(q);
  448. return (r);
  449. }
  450. size_t find_tag(const char *tagname)
  451. {
  452. size_t l;
  453. for (l=1; l<tagattrlen; l++)
  454. if (is_attr(tagattr+l, tagname)) return (l);
  455. return (0);
  456. }
  457. /*
  458. Decide what to do with this tag
  459. */
  460. static void filtered_tag()
  461. {
  462. size_t l;
  463. int open_window=0;
  464.  
  465. addtagbuf(0);
  466. parsetagbuf();
  467. if ( is_htmltag("TITLE") || is_htmltag("/TITLE") ||
  468. is_htmltag("SCRIPT") || is_htmltag("/SCRIPT") ||
  469. is_htmltag("FRAME") || is_htmltag("/FRAME") ||
  470. is_htmltag("IFRAME") || is_htmltag("/IFRAME") ||
  471. is_htmltag("APP") || is_htmltag("/APP") ||
  472. is_htmltag("APPLET") || is_htmltag("/APPLET") ||
  473. is_htmltag("SERVER") || is_htmltag("/SERVER") ||
  474. is_htmltag("OBJECT") || is_htmltag("/OBJECT") ||
  475. is_htmltag("HTML") || is_htmltag("/HTML") ||
  476. is_htmltag("HEAD") || is_htmltag("/HEAD") ||
  477. is_htmltag("BODY") || is_htmltag("/BODY") ||
  478. is_htmltag("LINK") || is_htmltag("META"))
  479. {
  480. return;
  481. }
  482. if ( is_htmltag("STYLE"))
  483. {
  484. ++instyletag;
  485. return;
  486. }
  487. if ( is_htmltag("/STYLE"))
  488. {
  489. --instyletag;
  490. return;
  491. }
  492. if (instyletag) return;
  493. for (l=1; l<tagattrlen; l++)
  494. {
  495. if (tagattr[l].tagnamelen > 2 &&
  496. strncasecmp(tagattr[l].tagname, "ON", 2) == 0)
  497. {
  498. memset(tagattr[l].atagstart, ' ',
  499. tagattr[l].ataglen);
  500. }
  501. }
  502. if (is_htmltag("IMG"))
  503. {
  504. size_t nsrc=find_tag("src");
  505. size_t nalt=find_tag("alt");
  506. int ignore_img=0;
  507. /*
  508. ** An IMG with a cid: URL is passed along, untouched, with
  509. ** the URL being processed.  This is handled later.
  510. */
  511. if (nsrc && htmlconvertcid_func)
  512. {
  513. const char *p=tagattr[nsrc].tagvalue;
  514. size_t s=tagattr[nsrc].tagvaluelen;
  515. while (s)
  516. {
  517. if ( !isspace((int)(unsigned char)*p)) break;
  518. ++p;
  519. --s;
  520. }
  521. if (s >= 4 && strncasecmp(p, "cid:", 4) == 0)
  522. {
  523. nsrc=0;
  524. ignore_img=1;
  525. /* Handle tags below */
  526. }
  527. }
  528. if (nsrc)
  529. {
  530. char *r;
  531. char *q;
  532. char *alt=0;
  533. r=malloc(tagattr[nsrc].tagvaluelen+1);
  534. if (!r) enomem();
  535. memcpy(r, tagattr[nsrc].tagvalue,
  536. tagattr[nsrc].tagvaluelen);
  537. r[tagattr[nsrc].tagvaluelen]=0;
  538. q=rfc2045_append_url(contentbase, r);
  539. free(r);
  540. for (r=q; *r; r++)
  541. if (*r == '"' ||
  542. *r == '<' || *r == '>') *r=0;
  543. /* Someone's playing games with us */
  544. if (nalt)
  545. {
  546. alt=malloc(tagattr[nalt].tagvaluelen+1);
  547. if (!alt) enomem();
  548. memcpy(alt, tagattr[nalt].tagvalue,
  549. tagattr[nalt].tagvaluelen);
  550. alt[tagattr[nalt].tagvaluelen]=0;
  551. }
  552. (*htmlfiltered_func)("<A TARGET="_blank" HREF="", 25);
  553. if (washlink) dowashlink(nsrc, q);
  554. else replacelink(nsrc, q);
  555. (*htmlfiltered_func)(tagattr[nsrc].tagvalue,
  556. tagattr[nsrc].tagvaluelen);
  557. (*htmlfiltered_func)("">", 2);
  558. if (alt)
  559. (*htmlfiltered_func)(alt, strlen(alt));
  560. else
  561. {
  562. (*htmlfiltered_func)("[", 1);
  563. (*htmlfiltered_func)(q, strlen(q));
  564. (*htmlfiltered_func)("]", 1);
  565. }
  566. (*htmlfiltered_func)("</A>", 4);
  567. free(q);
  568. if (alt) free(alt);
  569. }
  570. if (!ignore_img)
  571. return;
  572. }
  573. /* Attempt to automatically plug in any holes */
  574. for (l=1; l<tagattrlen; l++)
  575. {
  576. if (is_attr(tagattr+l, "target") ||
  577. is_attr(tagattr+l, "code") ||
  578. is_attr(tagattr+l, "language") ||
  579. is_attr(tagattr+l, "action") ||
  580. is_attr(tagattr+l, "codetype"))
  581. memset(tagattr[l].atagstart, ' ',
  582. tagattr[l].ataglen);
  583. if (is_attr(tagattr+l, "href")
  584. || is_attr(tagattr+l, "src")
  585. || is_attr(tagattr+l, "lowsrc"))
  586. {
  587. char *p=malloc(tagattr[l].tagvaluelen+1), *q;
  588. size_t n;
  589. int goodhref=0;
  590. if (!p) enomem();
  591. memcpy(p, tagattr[l].tagvalue, tagattr[l].tagvaluelen);
  592. p[tagattr[l].tagvaluelen]=0;
  593. q=rfc2045_append_url(contentbase, p);
  594. free(p);
  595. for (p=q; *p; p++)
  596. if (*p == '"' ||
  597. *p == '<' || *p == '>') *p=0;
  598. /* Someone's playing games with us */
  599. for (n=0; q[n]; n++)
  600. if (q[n] == ':')
  601. {
  602. if (is_valuestart(q, "cid:"))
  603. {
  604. goodhref=1;
  605. dowashcid(l, q);
  606. }
  607. else if ((is_valuestart(q, "http:") ||
  608. is_valuestart(q, "https:")) &&
  609. is_attr(tagattr+l, "href"))
  610. /* block src/lowsrc tags in anything but IMG.
  611. Ex: <input type="image" src="http://"> -- don't render as a redirect
  612. URL
  613. */
  614. {
  615. goodhref=1;
  616. if (washlink)
  617. dowashlink(l, q);
  618. else
  619. replacelink(l, q);
  620. if (is_htmltag("A"))
  621. open_window=1;
  622. break;
  623. }
  624. else if ( is_valuestart(q, "mailto:"))
  625. {
  626. goodhref=1;
  627. dowashlinkmailto(l, strchr(q,
  628. ':')+1);
  629. }
  630. else if ( is_valuestart(q, "ftp:") ||
  631. is_valuestart(q, "gopher:") ||
  632. is_valuestart(q, "wais:") ||
  633. is_valuestart(q, "telnet:"))
  634. {
  635. goodhref=1;
  636. if (is_htmltag("A"))
  637. open_window=1;
  638. }
  639. break;
  640. }
  641. if (!goodhref)
  642. {
  643. memset(tagattr[l].atagstart, ' ',
  644. tagattr[l].ataglen);
  645. }
  646. free(q);
  647. }
  648. }
  649. (*htmlfiltered_func)("<", 1);
  650. (*htmlfiltered_func)(tagbuf, strlen(tagbuf));
  651. if (open_window)
  652. (*htmlfiltered_func)(" target="_blank"", 16);
  653. (*htmlfiltered_func)(">", 1);
  654. }