MIMEState.c++
上传用户:weiyuanprp
上传日期:2020-05-20
资源大小:1169k
文件大小:13k
源码类别:

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: MIMEState.c++,v 1.6 2008/02/11 04:12:22 faxguy Exp $ */
  2. /*
  3.  * Copyright (c) 1990-1996 Sam Leffler
  4.  * Copyright (c) 1991-1996 Silicon Graphics, Inc.
  5.  * HylaFAX is a trademark of Silicon Graphics
  6.  *
  7.  * Permission to use, copy, modify, distribute, and sell this software and 
  8.  * its documentation for any purpose is hereby granted without fee, provided
  9.  * that (i) the above copyright notices and this permission notice appear in
  10.  * all copies of the software and related documentation, and (ii) the names of
  11.  * Sam Leffler and Silicon Graphics may not be used in any advertising or
  12.  * publicity relating to the software without the specific, prior written
  13.  * permission of Sam Leffler and Silicon Graphics.
  14.  * 
  15.  * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, 
  16.  * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY 
  17.  * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  
  18.  * 
  19.  * IN NO EVENT SHALL SAM LEFFLER OR SILICON GRAPHICS BE LIABLE FOR
  20.  * ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND,
  21.  * OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  22.  * WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF 
  23.  * LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 
  24.  * OF THIS SOFTWARE.
  25.  */
  26. /*
  27.  * MIME Decoder Support.
  28.  */
  29. #include "MIMEState.h"
  30. #include "MsgFmt.h"
  31. #include "StackBuffer.h"
  32. #include <string.h>
  33. #include <ctype.h>
  34. MIMEState::MIMEState(const char* t, const char* st) : type(t), subtype(st)
  35. {
  36.     parent = NULL;
  37.     lastPart = false;
  38.     encode = ENC_7BIT;
  39.     charset = CS_USASCII;
  40.     blen = (u_int) -1; // NB: should insure no matches
  41.     lineno = 1;
  42.     external = false;
  43. }
  44. MIMEState::MIMEState(MIMEState& other)
  45.     : parent(&other)
  46.     , boundary(other.boundary)
  47. {
  48.     if (other.type == "multipart" && other.subtype == "digest") 
  49. type = "message", subtype = "rfc822";
  50.     else
  51. type = "text", subtype = "plain";
  52.     lastPart = false;
  53.     encode = other.encode;
  54.     charset = other.charset;
  55.     blen = other.blen;
  56.     lineno = other.lineno;
  57.     external = other.external;
  58. }
  59. MIMEState::MIMEState(MIMEState& other, const char* t, const char* st)
  60.     : parent(&other)
  61.     , type(t)
  62.     , subtype(st)
  63.     , boundary(other.boundary)
  64. {
  65.     lastPart = false;
  66.     encode = other.encode;
  67.     charset = other.charset;
  68.     blen = other.blen;
  69.     lineno = other.lineno;
  70.     external = other.external;
  71. }
  72. MIMEState::~MIMEState()
  73. {
  74.     if (parent) {
  75. parent->lineno = lineno;
  76.     }
  77. }
  78. void
  79. MIMEState::trace(FILE* fd)
  80. {
  81.     static const char* enames[] = {
  82. "7bit",
  83. "quoted-printable",
  84. "base64",
  85. "8bit",
  86. "binary",
  87. "x-uuencode"
  88.     };
  89.     static const char* cnames[] = {
  90. "us-ascii",
  91. "iso-8859-1",
  92. "iso-8859-2",
  93. "iso-8859-3",
  94. "iso-8859-4",
  95. "iso-8859-5",
  96. "iso-8859-6",
  97. "iso-8859-7",
  98. "iso-8859-8",
  99. "iso-8859-9",
  100.     };
  101.     fprintf(fd, "MIME part (line %u): %s/%s charset=%s encoding=%sn"
  102. , lineno
  103. , (const char*) type
  104. , (const char*) subtype
  105. , cnames[charset]
  106. , enames[encode]
  107.     );
  108. }
  109. bool
  110. MIMEState::parseToken(const char*& cp, const char delimeter, fxStr& result)
  111. {
  112.     while (*cp && isspace(*cp))
  113. cp++;
  114.     const char* bp = cp;
  115.     while (*cp && !isspace(*cp) && *cp != delimeter)
  116. cp++;
  117.     if (cp - bp > 0) {
  118. result = fxStr(bp, cp-bp);
  119. while (isspace(*cp)) // remove trailing ws
  120.     cp++;
  121. return (true);
  122.     } else
  123. return (false);
  124. }
  125. /*
  126.  * Extract MIME-related information from a set of
  127.  * message headers and fillin the MIME state block.
  128.  */
  129. bool
  130. MIMEState::parse(const MsgFmt& msg, fxStr& emsg)
  131. {
  132.     const fxStr* s = msg.findHeader("Content-Type");
  133.     if (s) { // type/subtype [; parameter]*
  134. const char* cp = &(*s)[0];
  135. if (parseToken(cp, '/', type)
  136.   && *cp++ == '/'
  137.   && parseToken(cp, ';', subtype)) {
  138.     if (*cp == ';') // parse optional parameters
  139. parseParameters(cp+1);
  140.     type.lowercase();
  141.     subtype.lowercase();
  142. } else {
  143.     emsg = "Syntax error parsing MIME Content-Type: " | *s;
  144.     type = "text"; // reset on parsing error
  145.     return (false);
  146. }
  147.     }
  148.     s = msg.findHeader("Content-Transfer-Encoding");
  149.     if (s)
  150. setEncoding(&(*s)[0]);
  151.     s = msg.findHeader("Content-Description");
  152.     if (s)
  153. desc = *s;
  154.     s = msg.findHeader("Content-ID");
  155.     if (s)
  156. cid = *s;
  157.     s = msg.findHeader("Content-Disposition");
  158.     if (s)
  159. disp = *s;
  160.     return (true);
  161. }
  162. /*
  163.  * Parse optional MIME parameters used by the decoder.
  164.  */
  165. void
  166. MIMEState::parseParameters(const char* cp)
  167. {
  168.     for (;;) {
  169. while (*cp && isspace(*cp))
  170.     cp++;
  171. const char* bp = cp;
  172. while (*cp && !isspace(*cp) && *cp != '=')
  173.     cp++;
  174. if (*cp == '') // end of string
  175.     break;
  176. fxStr param(bp, cp-bp);
  177. if (*cp != '=') { // param<ws>=
  178.     while (isspace(*cp++))
  179. ;
  180.     if (*cp != '=') // syntax error
  181. break;
  182. }
  183. for (cp++; isspace(*cp); cp++) // skip ws after =
  184.     ;
  185. const char* ev;
  186. if (*cp == '"') { // quoted value
  187.     for (ev = ++cp; *ev && *ev != '"'; ev++)
  188. ;
  189.     if (*ev != '"') // missing close quote
  190. break;
  191. } else {
  192.     for (ev = cp; *ev && !isspace(*ev) && *ev != ';'; ev++)
  193. ;
  194. }
  195. (void) setParameter(param, fxStr(cp, ev-cp));
  196. if (*ev != '' && *ev != ';') { // scan to trailing ;
  197.     do {
  198. ev++;
  199.     } while (*ev && *ev != ';');
  200. }
  201. if (*ev != ';')
  202.     break;
  203. cp = ++ev;
  204.     }
  205. }
  206. /*
  207.  * Set a MIME parameter used by the decoder.
  208.  */
  209. bool
  210. MIMEState::setParameter(const fxStr& p, const fxStr& value)
  211. {
  212.     fxStr param(p);
  213.     param.lowercase();
  214.     if (param.length() == 7 && param == "charset") {
  215. setCharset(value); // character set
  216.     } else if (param.length() == 8 && param == "boundary") {
  217. setBoundary(value); // part boundary marker
  218.     } else
  219. return (false);
  220.     return (true);
  221. }
  222. /*
  223.  * Set the content-transfer-encoding.
  224.  */
  225. void
  226. MIMEState::setEncoding(const char* cp)
  227. {
  228.     if (strcasecmp(cp, "7bit") == 0)
  229. encode = ENC_7BIT;
  230.     else if (strcasecmp(cp, "quoted-printable") == 0)
  231. encode = ENC_QPRINT;
  232.     else if (strcasecmp(cp, "base64") == 0)
  233. encode = ENC_BASE64;
  234.     else if (strcasecmp(cp, "8bit") == 0)
  235. encode = ENC_8BIT;
  236.     else if (strcasecmp(cp, "binary") == 0)
  237. encode = ENC_BINARY;
  238.     else if (strcasecmp(cp, "x-uuencode") == 0)
  239. encode = ENC_UUENCODE;
  240. }
  241. /*
  242.  * Set the body part boundary.
  243.  */
  244. void
  245. MIMEState::setBoundary(const char* s)
  246. {
  247.     boundary = s;
  248.     boundary.insert("--"); // NB: insert "--" to simplify parsing
  249.     boundary.append("--"); // NB: append "--" to simplify parsing
  250.     blen = boundary.length()-2; // optimized for checking
  251. }
  252. /*
  253.  * Set the text character set.
  254.  */
  255. void
  256. MIMEState::setCharset(const char* cp)
  257. {
  258.     if (strcasecmp(cp, "us-ascii") == 0)
  259. charset = CS_USASCII;
  260.     else if (strcasecmp(cp, "iso-8859-1") == 0)
  261. charset = CS_ISO8859_1;
  262.     else if (strcasecmp(cp, "iso-8859-2") == 0)
  263. charset = CS_ISO8859_2;
  264.     else if (strcasecmp(cp, "iso-8859-3") == 0)
  265. charset = CS_ISO8859_3;
  266.     else if (strcasecmp(cp, "iso-8859-4") == 0)
  267. charset = CS_ISO8859_4;
  268.     else if (strcasecmp(cp, "iso-8859-5") == 0)
  269. charset = CS_ISO8859_5;
  270.     else if (strcasecmp(cp, "iso-8859-6") == 0)
  271. charset = CS_ISO8859_6;
  272.     else if (strcasecmp(cp, "iso-8859-7") == 0)
  273. charset = CS_ISO8859_7;
  274.     else if (strcasecmp(cp, "iso-8859-8") == 0)
  275. charset = CS_ISO8859_8;
  276.     else if (strcasecmp(cp, "iso-8859-9") == 0)
  277. charset = CS_ISO8859_9;
  278. }
  279. extern void fxFatal(const char* fmt ...);
  280. /*
  281.  * Return a line of data according to the current
  282.  * setting of the content-transfer-encoding and
  283.  * the body part boundary.  If the boundary marker
  284.  * or EOF is reached false is returned, otherwise
  285.  * this method returns true and the decoded line.
  286.  * If the data is encoded with a text-oriented
  287.  * scheme (7bit, 8bit, binary, or quoted-printable)
  288.  * then the trailing newline character is returned
  289.  * in the buffer.  Otherwise any trailing newline
  290.  * is discarded and only the decoded data is returned.
  291.  */
  292. bool
  293. MIMEState::getLine(FILE* fd, fxStackBuffer& buf)
  294. {
  295.     buf.reset();
  296.     switch (encode) {
  297.     case ENC_7BIT:
  298.         for (;;) {
  299.             int c = getc(fd);
  300.             if (c == EOF) {
  301.                 return (buf.getLength() > 0);
  302.             }
  303.             c &= 0xff;
  304.     if (c == 'r') {
  305. c = getc(fd);
  306. if (c == EOF) {
  307.     return (buf.getLength() > 0);
  308. }
  309. c &= 0xff;
  310. if (c != 'n') {
  311.     ungetc(c, fd);
  312.     c = 'r';
  313. }
  314.     }
  315.             if (c == 'n') { // check for boundary marker
  316.             lineno++;
  317.             u_int cc = buf.getLength();
  318.             if (cc >= blen && buf[0] == '-') {
  319.                 if (cc == blen && strneq(buf, boundary, blen)) {
  320.                 return (false);
  321.                     }
  322.                 if (cc == blen+2 && strneq(buf, boundary, blen+2)) {
  323.                 lastPart = true;
  324.                 return (false);
  325.                 }
  326.              }
  327.                 buf.put('n');
  328.                 return (true);
  329.             }
  330.             buf.put(c);
  331.         }
  332.     /*NOTREACHED*/
  333.     case ENC_8BIT:
  334.     case ENC_BINARY:
  335.         for (;;) {
  336.             int c = getc(fd);
  337.             if (c == EOF) {
  338.             return (buf.getLength() > 0);
  339.             }
  340.             c &= 0xff;
  341.     if (c == 'r') {
  342. c = getc(fd);
  343. if (c == EOF) {
  344.     return (buf.getLength() > 0);
  345. }
  346. c &= 0xff;
  347. if (c != 'n') {
  348.     ungetc(c, fd);
  349.     c = 'r';
  350. }
  351.     }
  352.             if (c == 'n') { // check for boundary marker
  353.             lineno++;
  354.                 u_int cc = buf.getLength();
  355.                 if (cc >= blen && buf[0] == '-') {
  356.                     if (cc == blen && strneq(buf, boundary, blen)) {
  357.                         return (false);
  358.                     }
  359.                 if (cc == blen+2 && strneq(buf, boundary, blen+2)) {
  360.                      lastPart = true;
  361.                      return (false);
  362.                     }
  363.             }
  364.                 buf.put('n');
  365.                 return (true);
  366.             }
  367.             buf.put(c);
  368.         }
  369.     /*NOTREACHED*/
  370.     case ENC_QPRINT: return getQuotedPrintableLine(fd, buf);
  371.     case ENC_BASE64: return getBase64Line(fd, buf);
  372.     case ENC_UUENCODE: return getUUDecodeLine(fd, buf);
  373.     }
  374.     fxFatal("Internal error, unsupported Content-Transfer-Encoding %u", encode);
  375.     /*NOTREACHED*/
  376.     return (false);
  377. }
  378. /*
  379.  * Return a decoded line of quoted-printable text.
  380.  */
  381. bool
  382. MIMEState::getQuotedPrintableLine(FILE* fd, fxStackBuffer& buf)
  383. {
  384.     MsgFmt msg;
  385.     char line[80]; // spec says never more than 76
  386.     u_int cc = 0; // chars in current line
  387.     for (;;) {
  388. int c = getc(fd);
  389. if (c == EOF) {
  390.     msg.copyQP(buf, line, cc);
  391.     return (buf.getLength() > 0);
  392. }
  393. c &= 0xff;
  394. if (c == 'r') {
  395.     c = getc(fd);
  396.     if (c == EOF) {
  397. msg.copyQP(buf, line, cc);
  398. return (buf.getLength() > 0);
  399.     }
  400.     c &= 0xff;
  401.     if (c != 'n') {
  402. ungetc(c, fd);
  403. c = 'r';
  404.     }
  405. }
  406. if (c == 'n') { // check for boundary marker
  407.     lineno++;
  408.     if (cc > 0 && line[cc-1] == '=') { // soft line break
  409. msg.copyQP (buf, line, cc-1);       // everything up to ``="''
  410. cc = 0;
  411. continue;
  412.     }
  413.     if (cc >= blen && line[0] == '-') {
  414. if (cc == blen && strneq(line, boundary, blen))
  415.     return (false);
  416. if (cc == blen+2 && strneq(line, boundary, blen+2)) {
  417.     lastPart = true;
  418.     return (false);
  419. }
  420.     }
  421.     msg.copyQP(buf, line, cc);
  422.     buf.put('n');
  423.     return (true);
  424. }
  425. if (cc < sizeof (line)-1)
  426.     line[cc++] = c;
  427.     }
  428.     /*NOTREACHED*/
  429. }
  430. /*
  431.  * Return a decoded line of base64 data.
  432.  */
  433. bool
  434. MIMEState::getBase64Line(FILE* fd, fxStackBuffer& buf)
  435. {
  436.     MsgFmt msg;
  437.     char line[80]; // spec says never more than 76
  438.     u_int cc = 0; // chars in current line
  439.     for (;;) {
  440. int c = getc(fd);
  441. if (c == EOF) {
  442.     msg.copyBase64(buf, line, cc);
  443.     return (buf.getLength() > 0);
  444. }
  445. c &= 0x7f;
  446. if (c == 'r') {
  447.     c = getc(fd);
  448.     if (c == EOF) {
  449. msg.copyBase64(buf, line, cc);
  450. return (buf.getLength() > 0);
  451.     }
  452.     c &= 0x7f;
  453.     if (c != 'n') {
  454. ungetc(c, fd);
  455. c = 'r';
  456.     }
  457. }
  458. if (c == 'n') { // check for boundary marker
  459.     lineno++;
  460.     if (cc >= blen && line[0] == '-') {
  461. if (cc == blen && strneq(line, boundary, blen))
  462.     return (false);
  463. if (cc == blen+2 && strneq(line, boundary, blen+2)) {
  464.     lastPart = true;
  465.     return (false);
  466. }
  467.     }
  468.     msg.copyBase64(buf, line, cc);
  469.     return (true);
  470. }
  471. if (cc < sizeof (line)-1)
  472.     line[cc++] = c;
  473.     }
  474.     /*NOTREACHED*/
  475. }
  476. /*
  477.  * Return a decoded line of uuencode'd data.
  478.  */
  479. bool
  480. MIMEState::getUUDecodeLine(FILE* fd, fxStackBuffer& buf)
  481. {
  482.     MsgFmt msg;
  483.     char line[80]; // spec says never more than 62
  484.     u_int cc = 0; // chars in current line
  485.     for (;;) {
  486. int c = getc(fd);
  487. if (c == EOF) {
  488.     msg.copyUUDecode(buf, line, cc);
  489.     return (buf.getLength() > 0);
  490. }
  491. c &= 0x7f;
  492. if (c == 'r') {
  493.     c = getc(fd);
  494.     if (c == EOF) {
  495. msg.copyUUDecode(buf, line, cc);
  496. return (buf.getLength() > 0);
  497.     }
  498.     c &= 0x7f;
  499.     if (c != 'n') {
  500. ungetc(c, fd);
  501. c = 'r';
  502.     }
  503. }
  504. if (c == 'n') {
  505.     lineno++;
  506.     if (cc >= blen && line[0] == '-') { // check for boundary marker
  507. if (cc == blen && strneq(line, boundary, blen))
  508.     return (false);
  509. if (cc == blen+2 && strneq(line, boundary, blen+2)) {
  510.     lastPart = true;
  511.     return (false);
  512. }
  513.     } else if (cc >= 6 && strneq(line, "begin ", 6)) {
  514. return (getUUDecodeLine(fd, buf));
  515.     } else if (cc == 3 && streq(line, "end")) {
  516. // consume to boundary marker
  517. while (getUUDecodeLine(fd, buf))
  518.     ;
  519. return (false);
  520.     }
  521.     msg.copyUUDecode(buf, line, cc);
  522.     return (true);
  523. }
  524. if (cc < sizeof (line)-1)
  525.     line[cc++] = c;
  526.     }
  527.     /*NOTREACHED*/
  528. }