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

传真(Fax)编程

开发平台:

C/C++

  1. /* $Id: MsgFmt.c++,v 1.8 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. #include "MsgFmt.h"
  27. #include "StackBuffer.h"
  28. #include "TextFormat.h"
  29. #include <ctype.h>
  30. MsgFmt::MsgFmt()
  31. {
  32. }
  33. MsgFmt::MsgFmt(const MsgFmt& other)
  34.     : boldFont(other.boldFont)
  35.     , italicFont(other.italicFont)
  36.     , headToKeep(other.headToKeep)
  37. #ifdef notdef
  38.     , headMap(other.headMap)
  39. #endif
  40. {
  41.     headerStop = other.headerStop;
  42.     verbose = other.verbose;
  43. }
  44. MsgFmt::~MsgFmt()
  45. {
  46. }
  47. const fxStr*
  48. MsgFmt::findHeader(const fxStr& name) const
  49. {
  50.     for (u_int i = 0, n = fields.length(); i < n; i++)
  51. if (strcasecmp(fields[i], name) == 0)
  52.     return (&headers[i]);
  53.     return (NULL);
  54. }
  55. fxStr
  56. MsgFmt::mapHeader(const fxStr& name)
  57. {
  58.     for (fxStrDictIter hi(headMap); hi.notDone(); hi++)
  59. if (strcasecmp(hi.key(), name) == 0)
  60.     return (hi.value());
  61.     return (name);
  62. }
  63. bool
  64. MsgFmt::getLine(FILE* fd, fxStackBuffer& buf)
  65. {
  66.     buf.reset();
  67.     for (;;) {
  68. int c = getc(fd);
  69. c &= 0xff;
  70. if (c == EOF)
  71.     return (buf.getLength() > 0);
  72. if (c == 'r') {
  73.     c = getc(fd);
  74.     if (c == EOF)
  75. return (buf.getLength() > 0);
  76.     c &= 0xff;
  77.     if (c != 'n') {
  78. ungetc(c, fd);
  79. c = 'r';
  80.     }
  81. }
  82. if (c == 'n')
  83.     break;
  84. buf.put(c);
  85.     }
  86.     return (true);
  87. }
  88. /*
  89.  * This function replaces comments with a single white space.
  90.  * Unclosed comments are automatically closed at end of string.
  91.  * Stray closing parentheses are left untouched, as are other invalid chars.
  92.  * Headers which can contain quoted strings should not go through this
  93.  * revision of this function as is doesn't honnor them and could end up doing
  94.  * the wrong thing.
  95.  */
  96. fxStr
  97. MsgFmt::stripComments(const fxStr& s)
  98. {
  99.     fxStr q;
  100.     u_int depth = 0;
  101.     bool wasSpace = true;
  102.     for (u_int i = 0; i < s.length(); i++) {
  103.         switch (s[i]) {
  104.             case '(':
  105.                 depth++;
  106.                 break;
  107.             case ')':
  108.                 if (depth > 0)
  109.                     depth--;
  110.                 break;
  111.             case '\':
  112.                 if (depth == 0) {
  113.                     q.append(s[i++]);     // Don't decode them at this time
  114.                     q.append(s[i]);
  115.                     wasSpace = false;
  116.                 } else
  117.                   i++;
  118.                 break;
  119.             default:
  120.                 if (depth == 0) {
  121.                     if (!isspace(s[i]) || !wasSpace) {       // Trim consecutive spaces
  122.                         q.append(s[i]);
  123.                         wasSpace = isspace(s[i]);
  124.                     }
  125.                 }
  126.                 break;
  127.         }
  128.     }
  129.     while (q.length() > 0 && isspace(q[q.length()-1]))
  130.       q.remove(q.length()-1, 1);      // Trim trailing white space
  131.     return q;
  132. }
  133. void
  134. MsgFmt::parseHeaders(FILE* fd, u_int& lineno)
  135. {
  136.     fxStackBuffer buf;
  137.     fxStr field; // current header field
  138.     while (getLine(fd, buf)) {
  139. lineno++;
  140. if (buf.getLength() == 0)
  141.     break;
  142. /*
  143.  * Collect field name in a canonical format.
  144.  * If the line begins with whitespace, then
  145.  * it's the continuation of a previous header.
  146.  */ 
  147. fxStr line(&buf[0], buf.getLength());
  148. u_int len = line.length();
  149. while (len > 0 && isspace(line[line.length()-1])) {
  150.     line.remove(line.length()-1, 1); // trim trailing whitespace
  151.     len--;
  152. }
  153. if (len > 0 && !isspace(line[0])) { 
  154.     u_int l = 0;
  155.     field = line.token(l, ':');
  156.     if (field != "" && l < len) { // record new header
  157. fields.append(field);
  158. // skip leading whitespace
  159. for (; l < len && isspace(line[l]); l++)
  160.     ;
  161. headers.append(line.tail(len-l));
  162. if (verbose)
  163.     fprintf(stderr, "HEADER %s: %sn"
  164. , (const char*) fields[fields.length()-1]
  165. , (const char*) headers[headers.length()-1]
  166.     );
  167.     }
  168. } else if (field != "")  { // append continuation
  169.     headers[headers.length()-1].append(line);
  170.     if (verbose)
  171. fprintf(stderr, "+HEADER %s: %sn"
  172.     , (const char*) field
  173.     , (const char*) line
  174. );
  175. }
  176.     
  177.     }
  178.     /*
  179.      * Scan envelope for any meta-headers that
  180.      * control how formatting is to be done.
  181.      */
  182.     for (u_int i = 0, n = fields.length();  i < n; i++) {
  183. const fxStr& field = fields[i];
  184. if (strncasecmp(field, "x-fax-", 6) == 0)
  185.     setConfigItem(&field[6], headers[i]);
  186.     }
  187. }
  188. void
  189. MsgFmt::setupConfig()
  190. {
  191.     verbose = false;
  192.     boldFont = "Helvetica-Bold";
  193.     italicFont = "Helvetica-Oblique";
  194.     headToKeep.resize(0);
  195.     headToKeep.append("To");
  196.     headToKeep.append("From");
  197.     headToKeep.append("Subject");
  198.     headToKeep.append("Cc");
  199.     headToKeep.append("Date");
  200.     for (fxStrDictIter iter(headMap); iter.notDone(); iter++)
  201. headMap.remove(iter.key());
  202. }
  203. #undef streq
  204. #define streq(a,b) (strcasecmp(a,b)==0)
  205. bool
  206. MsgFmt::setConfigItem(const char* tag, const char* value)
  207. {
  208.     if (streq(tag, "headers")) {
  209.         char* cp = strcpy(new char[strlen(value) + 1], value);
  210.         char* tp;
  211.         do {
  212.             tp = strchr(cp, ' ');
  213.             if (tp) {
  214.                 *tp++ = '';
  215.             }
  216.             if (streq(cp, "clear")) {
  217.                 headToKeep.resize(0);
  218.             } else {
  219.                 headToKeep.append(cp);
  220.             }
  221.         } while ((cp = tp));
  222.         delete [] cp;
  223.     } else if (streq(tag, "mapheader")) {
  224. char* tp = (char *) strchr(value, ' ');
  225. if (tp) {
  226.     for (*tp++ = ''; isspace(*tp); tp++)
  227. ;
  228.     headMap[value] = tp;
  229. }
  230.     } else if (streq(tag, "boldfont")) {
  231. boldFont = value;
  232.     } else if (streq(tag, "italicfont")) {
  233. italicFont = value;
  234.     } else if (streq(tag, "verbose")) {
  235. verbose = FaxConfig::getBoolean(tag);
  236.     } else
  237. return (false);
  238.     return (true);
  239. }
  240. #undef streq
  241. u_int
  242. MsgFmt::headerCount(void)
  243. {
  244.     u_int nl = 0;
  245.     for (u_int i = 0, n = headToKeep.length(); i < n; i++)
  246. if (findHeader(headToKeep[i]))
  247.     nl++; // XXX handle wrapped lines
  248.     return (nl);
  249. }
  250. #ifdef roundup
  251. #undef roundup
  252. #endif
  253. #define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
  254. void
  255. MsgFmt::formatHeaders(TextFormat& fmt)
  256. {
  257.     /*
  258.      * Calculate tab stop for headers based on the
  259.      * maximum width of the headers we are to keep.
  260.      */
  261.     const TextFont* bold = fmt.getFont("Bold");
  262.     if (!bold)
  263. bold = fmt.addFont("Bold", boldFont);
  264.     headerStop = 0;
  265.     u_int i;
  266.     u_int nHead = headToKeep.length();
  267.     for (i = 0; i < nHead; i++) {
  268. TextCoord w = bold->strwidth(mapHeader(headToKeep[i]));
  269. if (w > headerStop)
  270.     headerStop = w;
  271.     }
  272.     headerStop += bold->charwidth(':');
  273.     TextCoord boldTab = 8 * bold->charwidth(' ');
  274.     headerStop = roundup(headerStop, boldTab);
  275.     /*
  276.      * Format headers we want; embolden field name
  277.      * and italicize field value.  We wrap long
  278.      * items to the field tab stop calculated above.
  279.      */
  280.     u_int nl = 0;
  281.     for (i = 0; i < nHead; i++) {
  282. const fxStr* value = findHeader(headToKeep[i]);
  283. if (value) {
  284.     fxStr v(*value);
  285.     decodeRFC2047(v);
  286.     fmt.beginLine();
  287. TextCoord hm = bold->show(fmt.getOutputFile(),
  288.     mapHeader(headToKeep[i]) | ":");
  289. fmt.hrMove(headerStop - hm);
  290. showItalic(fmt, v);
  291.     fmt.endLine();
  292.     nl++;
  293. }
  294.     }
  295.     if (nl > 0) {
  296. /*
  297.  * Insert a blank line between the envelope and the
  298.  * body.  Note that we ``know too much here''--we
  299.  * know to insert whitespace below to insure valid
  300.  * PostScript is generated (sigh).
  301.  */
  302. fmt.beginLine();
  303.     fputc(' ', fmt.getOutputFile()); // XXX whitespace needed
  304. fmt.endLine();
  305.     }
  306. }
  307. /*
  308.  * Display the string in italic, wrapping to the
  309.  * field header tab stop on any line overflows.
  310.  */
  311. void
  312. MsgFmt::showItalic(TextFormat& fmt, const char* cp)
  313. {
  314.     const TextFont* italic = fmt.getFont("Italic");
  315.     if (!italic)
  316. italic = fmt.addFont("Italic", italicFont);
  317.     while (isspace(*cp)) // trim leading whitespace
  318. cp++;
  319.     TextCoord x = fmt.getXOff(); // current x position on line
  320.     FILE* tf = fmt.getOutputFile(); // output stream
  321.     const char* tp = cp;
  322.     for (; *tp != ''; tp++) {
  323. if (*tp == 'r' && *(tp+1) == 'n') tp++;
  324. TextCoord hm = italic->charwidth(*tp);
  325. if (*tp == 'n' || x+hm > fmt.getRHS()) {// text would overflow line
  326.     italic->show(tf, cp, tp-cp), cp = tp;// flush pending text
  327.     if (!fmt.getLineWrapping()) // truncate line, don't wrap
  328. return;
  329.     fmt.endLine(); // terminate line
  330.     fmt.beginLine(); // begin another line
  331.     fmt.hrMove(headerStop); // reposition to header stop
  332.     x = fmt.getXOff();
  333.     if (*tp == 'n') { // remove leading white space
  334. for (tp++; isspace(*tp); tp++)
  335.     ;
  336. cp = --tp;
  337.     }
  338. }
  339. x += hm;
  340.     }
  341.     if (tp > cp)
  342. italic->show(tf, cp, tp-cp); // flush remainder
  343. }
  344. static int
  345. hex(char c)
  346. {
  347.     // NB: don't check if c is in set [0-9a-fA-F]
  348.     return (isdigit(c) ? c-'0' : isupper(c) ? 10+c-'A' : 10+c-'a');
  349. }
  350. #define ishex(c) ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))
  351. /*
  352.  * Decode a string that is RFC2047-encoded
  353.  */
  354. void
  355. MsgFmt::decodeRFC2047(fxStr& s)
  356. {
  357.     fxStackBuffer buf;
  358.     u_int bm = s.find(0, "=?"); // beginning marker
  359.     if (bm < s.length()) {
  360. u_int mm = s.find(bm+2, "?Q?"); // quoted printable
  361. if (mm == s.length()) s.find(bm, "?q?");
  362. if (mm < s.length()) {
  363.     u_int em = s.find(mm+3, "?="); // end marker
  364.     if (em < s.length()) {
  365. s.remove(em, 2);
  366. s.remove(bm, mm-bm+3);
  367. u_int i = 0;
  368. while (i < s.length()) {
  369.     if (s[i] == '_') s[i] = ' ';
  370.     i++;
  371. }
  372. copyQP(buf, s, s.length());
  373. buf.put('');
  374. s = buf;
  375.     }
  376. } else {
  377.     s.find(bm, "?B?"); // base64
  378.     if (mm == s.length()) s.find(bm, "?b?");
  379.     if (mm < s.length()) {
  380. u_int em = s.find(mm+3, "?="); // end marker
  381. if (em < s.length()) {
  382.     s.remove(em, 2);
  383.     s.remove(bm, mm-bm+3);
  384.     copyBase64(buf, s, s.length());
  385.     buf.put('');
  386.     s = buf;
  387. }
  388.     }
  389. }
  390.     }
  391. }
  392. void
  393. MsgFmt::copyQP(fxStackBuffer& buf, const char line[], u_int cc)
  394. {
  395.     // copy to buf & convert =XX escapes
  396.     for (u_int i = 0; i < cc; i++) {
  397. if (line[i] == '=' && cc-i >= 2) {
  398.     int v1 = hex(line[++i]);
  399.     int v2 = hex(line[++i]);
  400.     buf.put((v1<<4) + v2);
  401. } else
  402.     buf.put(line[i]);
  403.     }
  404. }
  405. void
  406. MsgFmt::copyBase64(fxStackBuffer& buf, const char line[], u_int cc)
  407. {
  408.     static const int base64[128] = {
  409. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  410. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
  411. -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
  412. 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
  413. -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,
  414. 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
  415. -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
  416. 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1
  417.     };
  418.     u_int i = 0;
  419.     while (i < cc) {
  420. int c1;
  421. do {
  422.     c1 = base64[(u_int)line[i++]];
  423. } while (c1 == -1 && i < cc);
  424. if (c1 != -1) {
  425.     int c2;
  426.     do {
  427. c2 = base64[(u_int)line[i++]];
  428.     } while (c2 == -1 && i < cc);
  429.     if (c2 != -1) {
  430. buf.put((c1<<2) | ((c2&0x30)>>4));
  431. int c3;
  432. do {
  433.     c3 = base64[(u_int)line[i++]];
  434. } while (c3 == -1 && i < cc);
  435. if (c3 != -1) {
  436.     buf.put(((c2&0x0f)<<4) | ((c3&0x3c)>>2));
  437.     int c4;
  438.     do {
  439. c4 = base64[(u_int)line[i++]];
  440.     } while (c4 == -1 && i < cc);
  441.     if (c4 != -1)
  442. buf.put((c3&0x3)<<6 | c4);
  443. }
  444.     }
  445. }
  446.     }
  447. }
  448. inline int DEC(char c) { return ((c - ' ') & 077); }
  449. void
  450. MsgFmt::copyUUDecode(fxStackBuffer& buf, const char line[], u_int)
  451. {
  452.     const char* cp = line;
  453.     int n = DEC(*cp);
  454.     if (n > 0) {
  455. // XXX check n against passed in byte count for line
  456. int c;
  457. for (cp++; n >= 3; cp += 4, n -= 3) {
  458.     c = (DEC(cp[0])<<2) | (DEC(cp[1])>>4); buf.put(c);
  459.     c = (DEC(cp[1])<<4) | (DEC(cp[2])>>2); buf.put(c);
  460.     c = (DEC(cp[2])<<6) |  DEC(cp[3]);     buf.put(c);
  461. }
  462. if (n >= 1)
  463.     c = (DEC(cp[0])<<2) | (DEC(cp[1])>>4), buf.put(c);
  464. if (n >= 2)
  465.     c = (DEC(cp[1])<<4) | (DEC(cp[2])>>2), buf.put(c);
  466. if (n >= 3)
  467.     c = (DEC(cp[2])<<6) |  DEC(cp[3]),     buf.put(c);
  468.     }
  469. }