home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libmime / mimehdrs.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  74.0 KB  |  2,785 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /* mimehdrs.c --- MIME header parser, version 2 (see mimei.h).
  20.    Created: Jamie Zawinski <jwz@netscape.com>, 15-May-96.
  21.  */
  22.  
  23.  
  24. #include "mimei.h"
  25. #include "xpgetstr.h"
  26. #include "libi18n.h"
  27.  
  28. #ifndef MOZILLA_30
  29. # include "msgcom.h"
  30. #include "prefapi.h"
  31. #endif /* !MOZILLA_30 */
  32.  
  33. extern int MK_OUT_OF_MEMORY;
  34. extern int MK_MSG_NO_HEADERS;
  35. extern int MK_MSG_MIME_MAC_FILE;
  36. extern int MK_MSG_IN_MSG_X_USER_WROTE;
  37. extern int MK_MSG_USER_WROTE;
  38. extern int MK_MSG_UNSPECIFIED_TYPE;
  39. extern int MK_MSG_XSENDER_INTERNAL;
  40. extern int MK_MSG_ADDBOOK_MOUSEOVER_TEXT;
  41.  
  42. extern int MK_MIMEHTML_DISP_SUBJECT;
  43. extern int MK_MIMEHTML_DISP_RESENT_COMMENTS;
  44. extern int MK_MIMEHTML_DISP_RESENT_DATE;
  45. extern int MK_MIMEHTML_DISP_RESENT_SENDER;
  46. extern int MK_MIMEHTML_DISP_RESENT_FROM;
  47. extern int MK_MIMEHTML_DISP_RESENT_TO;
  48. extern int MK_MIMEHTML_DISP_RESENT_CC;
  49. extern int MK_MIMEHTML_DISP_DATE;
  50. extern int MK_MIMEHTML_DISP_SUBJECT;
  51. extern int MK_MIMEHTML_DISP_SENDER;
  52. extern int MK_MIMEHTML_DISP_FROM;
  53. extern int MK_MIMEHTML_DISP_REPLY_TO;
  54. extern int MK_MIMEHTML_DISP_ORGANIZATION;
  55. extern int MK_MIMEHTML_DISP_TO;
  56. extern int MK_MIMEHTML_DISP_CC;
  57. extern int MK_MIMEHTML_DISP_NEWSGROUPS;
  58. extern int MK_MIMEHTML_DISP_FOLLOWUP_TO;
  59. extern int MK_MIMEHTML_DISP_REFERENCES;
  60. extern int MK_MIMEHTML_DISP_MESSAGE_ID;
  61. extern int MK_MIMEHTML_DISP_RESENT_MESSAGE_ID;
  62. extern int MK_MIMEHTML_DISP_BCC;
  63. extern int MK_MIMEHTML_SHOW_SECURITY_ADVISOR;
  64.  
  65. extern int MK_MIMEHTML_ENC_AND_SIGNED;
  66. extern int MK_MIMEHTML_SIGNED;
  67. extern int MK_MIMEHTML_ENCRYPTED;
  68. extern int MK_MIMEHTML_CERTIFICATES;
  69. extern int MK_MIMEHTML_ENC_SIGNED_BAD;
  70. extern int MK_MIMEHTML_SIGNED_BAD;
  71. extern int MK_MIMEHTML_ENCRYPTED_BAD;
  72. extern int MK_MIMEHTML_CERTIFICATES_BAD;
  73.  
  74.  
  75.  
  76.  
  77.  
  78. MimeHeaders *
  79. MimeHeaders_new (void)
  80. {
  81.   MimeHeaders *hdrs = (MimeHeaders *) XP_ALLOC(sizeof(MimeHeaders));
  82.   if (!hdrs) return 0;
  83.  
  84.   XP_MEMSET(hdrs, 0, sizeof(*hdrs));
  85.   hdrs->done_p = FALSE;
  86.  
  87.   return hdrs;
  88. }
  89.  
  90. void
  91. MimeHeaders_free (MimeHeaders *hdrs)
  92. {
  93.   if (!hdrs) return;
  94.   FREEIF(hdrs->all_headers);
  95.   FREEIF(hdrs->heads);
  96.   FREEIF(hdrs->obuffer);
  97.   FREEIF(hdrs->munged_subject);
  98.   hdrs->obuffer_fp = 0;
  99.   hdrs->obuffer_size = 0;
  100.  
  101. # ifdef DEBUG__
  102.   {
  103.     int i, size = sizeof(*hdrs);
  104.     uint32 *array = (uint32*) hdrs;
  105.     for (i = 0; i < (size / sizeof(*array)); i++)
  106.       array[i] = (uint32) 0xDEADBEEF;
  107.   }
  108. # endif /* DEBUG */
  109.  
  110.   XP_FREE(hdrs);
  111. }
  112.  
  113.  
  114. MimeHeaders *
  115. MimeHeaders_copy (MimeHeaders *hdrs)
  116. {
  117.   MimeHeaders *hdrs2;
  118.   if (!hdrs) return 0;
  119.  
  120.   hdrs2 = (MimeHeaders *) XP_ALLOC(sizeof(*hdrs));
  121.   if (!hdrs2) return 0;
  122.   XP_MEMSET(hdrs2, 0, sizeof(*hdrs2));
  123.  
  124.   if (hdrs->all_headers)
  125.     {
  126.       hdrs2->all_headers = (char *) XP_ALLOC(hdrs->all_headers_fp);
  127.       if (!hdrs2->all_headers)
  128.         {
  129.           XP_FREE(hdrs2);
  130.           return 0;
  131.         }
  132.       XP_MEMCPY(hdrs2->all_headers, hdrs->all_headers, hdrs->all_headers_fp);
  133.  
  134.       hdrs2->all_headers_fp   = hdrs->all_headers_fp;
  135.       hdrs2->all_headers_size = hdrs->all_headers_fp;
  136.     }
  137.  
  138.   hdrs2->done_p = hdrs->done_p;
  139.  
  140.   if (hdrs->heads)
  141.     {
  142.       int i;
  143.       hdrs2->heads = (char **) XP_ALLOC(hdrs->heads_size
  144.                                         * sizeof(*hdrs->heads));
  145.       if (!hdrs2->heads)
  146.         {
  147.           FREEIF(hdrs2->all_headers);
  148.           XP_FREE(hdrs2);
  149.           return 0;
  150.         }
  151.       hdrs2->heads_size = hdrs->heads_size;
  152.       for (i = 0; i < hdrs->heads_size; i++)
  153.         {
  154.           hdrs2->heads[i] = (hdrs2->all_headers +
  155.                              (hdrs->heads[i] - hdrs->all_headers));
  156.         }
  157.     }
  158.   return hdrs2;
  159. }
  160.  
  161. /* Discard the buffer, when we probably won't be needing it any more. */
  162. static void
  163. MimeHeaders_compact (MimeHeaders *hdrs)
  164. {
  165.   XP_ASSERT(hdrs);
  166.   if (!hdrs) return;
  167.  
  168.   FREEIF(hdrs->obuffer);
  169.   hdrs->obuffer_fp = 0;
  170.   hdrs->obuffer_size = 0;
  171.  
  172.   /* These really shouldn't have gotten out of whack again. */
  173.   XP_ASSERT(hdrs->all_headers_fp <= hdrs->all_headers_size &&
  174.             hdrs->all_headers_fp + 100 > hdrs->all_headers_size);
  175. }
  176.  
  177.  
  178. static int MimeHeaders_build_heads_list(MimeHeaders *hdrs);
  179.  
  180. int
  181. MimeHeaders_parse_line (const char *buffer, int32 size, MimeHeaders *hdrs)
  182. {
  183.   int status = 0;
  184.   int desired_size;
  185.  
  186.   XP_ASSERT(hdrs);
  187.   if (!hdrs) return -1;
  188.  
  189.   /* Don't try and feed me more data after having fed me a blank line... */
  190.   XP_ASSERT(!hdrs->done_p);
  191.   if (hdrs->done_p) return -1;
  192.  
  193.   if (!buffer || size == 0 || *buffer == CR || *buffer == LF)
  194.     {
  195.       /* If this is a blank line, we're done.
  196.        */
  197.       hdrs->done_p = TRUE;
  198.       return MimeHeaders_build_heads_list(hdrs);
  199.     }
  200.  
  201.   /* Tack this data on to the end of our copy.
  202.    */
  203.   desired_size = hdrs->all_headers_fp + size + 1;
  204.   if (desired_size >= hdrs->all_headers_size)
  205.     {
  206.       status = msg_GrowBuffer (desired_size, sizeof(char), 255,
  207.                                &hdrs->all_headers, &hdrs->all_headers_size);
  208.       if (status < 0) return status;
  209.     }
  210.   XP_MEMCPY(hdrs->all_headers+hdrs->all_headers_fp, buffer, size);
  211.   hdrs->all_headers_fp += size;
  212.  
  213.   return 0;
  214. }
  215.  
  216. static int
  217. MimeHeaders_build_heads_list(MimeHeaders *hdrs)
  218. {
  219.   char *s;
  220.   char *end;
  221.   int i;
  222.   XP_ASSERT(hdrs);
  223.   if (!hdrs) return -1;
  224.  
  225.   XP_ASSERT(hdrs->done_p && !hdrs->heads);
  226.   if (!hdrs->done_p || hdrs->heads)
  227.     return -1;
  228.  
  229.   if (hdrs->all_headers_fp == 0)
  230.     {
  231.       /* Must not have been any headers (we got the blank line right away.) */
  232.       FREEIF (hdrs->all_headers);
  233.       hdrs->all_headers_size = 0;
  234.       return 0;
  235.     }
  236.  
  237.   /* At this point, we might as well realloc all_headers back down to the
  238.      minimum size it must be (it could be up to 1k bigger.)  But don't
  239.      bother if we're only off by a tiny bit. */
  240.   XP_ASSERT(hdrs->all_headers_fp <= hdrs->all_headers_size);
  241.   if (hdrs->all_headers_fp + 60 <= hdrs->all_headers_size)
  242.     {
  243.       char *s = XP_REALLOC(hdrs->all_headers, hdrs->all_headers_fp);
  244.       if (s) /* can this ever fail?  we're making it smaller... */
  245.         {
  246.           hdrs->all_headers = s;  /* in case it got relocated */
  247.           hdrs->all_headers_size = hdrs->all_headers_fp;
  248.         }
  249.     }
  250.  
  251.  
  252.   /* First go through and count up the number of headers in the block.
  253.    */
  254.   end = hdrs->all_headers + hdrs->all_headers_fp;
  255.   for (s = hdrs->all_headers; s <= end-1; s++)
  256.     {
  257.       if (s <= (end-1) && s[0] == CR && s[1] == LF) /* CRLF -> LF */
  258.         s++;
  259.  
  260.       if ((s[0] == CR || s[0] == LF) &&            /* we're at a newline, and */
  261.           (s >= (end-1) ||                        /* we're at EOF, or */
  262.            !(s[1] == ' ' || s[1] == '\t')))        /* next char is nonwhite */
  263.         hdrs->heads_size++;
  264.     }
  265.       
  266.   /* Now allocate storage for the pointers to each of those headers.
  267.    */
  268.   hdrs->heads = (char **) XP_ALLOC((hdrs->heads_size + 1) * sizeof(char *));
  269.   if (!hdrs->heads)
  270.     return MK_OUT_OF_MEMORY;
  271.   XP_MEMSET(hdrs->heads, 0, (hdrs->heads_size + 1) * sizeof(char *));
  272.  
  273.  
  274.   /* Now make another pass through the headers, and this time, record the
  275.      starting position of each header.
  276.    */
  277.  
  278.   i = 0;
  279.   hdrs->heads[i++] = hdrs->all_headers;
  280.   s = hdrs->all_headers;
  281.  
  282.   while (s <= end)
  283.     {
  284.     SEARCH_NEWLINE:
  285.       while (s <= end-1 && *s != CR && *s != LF)
  286.         s++;
  287.  
  288.       if (s+1 >= end)
  289.         break;
  290.  
  291.       /* If "\r\n " or "\r\n\t" is next, that doesn't terminate the header. */
  292.       else if (s+2 < end &&
  293.                (s[0] == CR  && s[1] == LF) &&
  294.                (s[2] == ' ' || s[2] == '\t'))
  295.         {
  296.           s += 3;
  297.           goto SEARCH_NEWLINE;
  298.         }
  299.       /* If "\r " or "\r\t" or "\n " or "\n\t" is next, that doesn't terminate
  300.          the header either. */
  301.       else if ((s[0] == CR  || s[0] == LF) &&
  302.                (s[1] == ' ' || s[1] == '\t'))
  303.         {
  304.           s += 2;
  305.           goto SEARCH_NEWLINE;
  306.         }
  307.  
  308.       /* At this point, `s' points before a header-terminating newline.
  309.          Move past that newline, and store that new position in `heads'.
  310.        */
  311.       if (*s == CR) s++;
  312.       if (*s == LF) s++;
  313.  
  314.       if (s < end)
  315.         {
  316.           XP_ASSERT(! (i > hdrs->heads_size));
  317.           if (i > hdrs->heads_size) return -1;
  318.           hdrs->heads[i++] = s;
  319.         }
  320.     }
  321.  
  322.   return 0;
  323. }
  324.  
  325.  
  326. char *
  327. MimeHeaders_get (MimeHeaders *hdrs, const char *header_name,
  328.                  XP_Bool strip_p, XP_Bool all_p)
  329. {
  330.   int i;
  331.   int name_length;
  332.   char *result = 0;
  333.  
  334.   XP_ASSERT(hdrs);
  335.   if (!hdrs) return 0;
  336.   XP_ASSERT(header_name);
  337.   if (!header_name) return 0;
  338.  
  339.   /* Specifying strip_p and all_p at the same time doesn't make sense... */
  340.   XP_ASSERT(!(strip_p && all_p));
  341.  
  342.   /* One shouldn't be trying to read headers when one hasn't finished
  343.      parsing them yet... but this can happen if the message ended
  344.      prematurely, and has no body at all (as opposed to a null body,
  345.      which is more normal.)   So, if we try to read from the headers,
  346.      let's assume that the headers are now finished.  If they aren't
  347.      in fact finished, then a later attempt to write to them will assert.
  348.    */
  349.   if (!hdrs->done_p)
  350.     {
  351.       int status;
  352.       hdrs->done_p = TRUE;
  353.       status = MimeHeaders_build_heads_list(hdrs);
  354.       if (status < 0) return 0;
  355.     }
  356.  
  357.   if (!hdrs->heads)      /* Must not have been any headers. */
  358.     {
  359.       XP_ASSERT(hdrs->all_headers_fp == 0);
  360.       return 0;
  361.     }
  362.  
  363.   name_length = XP_STRLEN(header_name);
  364.  
  365.   for (i = 0; i < hdrs->heads_size; i++)
  366.     {
  367.       char *head = hdrs->heads[i];
  368.       char *end = (i == hdrs->heads_size-1
  369.                    ? hdrs->all_headers + hdrs->all_headers_fp
  370.                    : hdrs->heads[i+1]);
  371.       char *colon, *ocolon;
  372.  
  373.       XP_ASSERT(head);
  374.       if (!head) continue;
  375.  
  376.       /* Quick hack to skip over BSD Mailbox delimiter. */
  377.       if (i == 0 && head[0] == 'F' && !XP_STRNCMP(head, "From ", 5))
  378.         continue;
  379.  
  380.       /* Find the colon. */
  381.       for (colon = head; colon < end; colon++)
  382.         if (*colon == ':') break;
  383.  
  384.       if (colon >= end) continue;
  385.  
  386.       /* Back up over whitespace before the colon. */
  387.       ocolon = colon;
  388.       for (; colon > head && XP_IS_SPACE(colon[-1]); colon--)
  389.         ;
  390.  
  391.       /* If the strings aren't the same length, it doesn't match. */
  392.       if (name_length != colon - head )
  393.         continue;
  394.  
  395.       /* If the strings differ, it doesn't match. */
  396.       if (strncasecomp(header_name, head, name_length))
  397.         continue;
  398.  
  399.       /* Otherwise, we've got a match. */
  400.       {
  401.         char *contents = ocolon + 1;
  402.         char *s;
  403.  
  404.         /* Skip over whitespace after colon. */
  405.         while (contents <= end && XP_IS_SPACE(*contents))
  406.           contents++;
  407.  
  408.         /* If we're supposed to strip at the frist token, pull `end' back to
  409.            the first whitespace or ';' after the first token.
  410.          */
  411.         if (strip_p)
  412.           {
  413.             for (s = contents;
  414.                  s <= end && *s != ';' && *s != ',' && !XP_IS_SPACE(*s);
  415.                  s++)
  416.               ;
  417.             end = s;
  418.           }
  419.  
  420.         /* Now allocate some storage.
  421.            If `result' already has a value, enlarge it.
  422.            Otherwise, just allocate a block.
  423.            `s' gets set to the place where the new data goes.
  424.          */
  425.         if (!result)
  426.           {
  427.             result = (char *) XP_ALLOC(end - contents + 1);
  428.             if (!result)
  429.               return 0;
  430.             s = result;
  431.           }
  432.         else
  433.           {
  434.             int32 L = XP_STRLEN(result);
  435.             s = (char *) XP_REALLOC(result, (L + (end - contents + 10)));
  436.             if (!s)
  437.               {
  438.                 XP_FREE(result);
  439.                 return 0;
  440.               }
  441.             result = s;
  442.             s = result + L;
  443.  
  444.             /* Since we are tacking more data onto the end of the header
  445.                field, we must make it be a well-formed continuation line,
  446.                by seperating the old and new data with CR-LF-TAB.
  447.              */
  448.             *s++ = ',';                /* #### only do this for addr headers? */
  449.             *s++ = LINEBREAK[0];
  450. # if (LINEBREAK_LEN == 2)
  451.             *s++ = LINEBREAK[1];
  452. # endif
  453.             *s++ = '\t';
  454.           }
  455.  
  456.         /* Take off trailing whitespace... */
  457.         while (end > contents && XP_IS_SPACE(end[-1]))
  458.           end--;
  459.  
  460.         /* Now copy the header's contents in...
  461.          */
  462.         XP_MEMCPY(s, contents, end - contents);
  463.         s[end - contents] = 0;
  464.  
  465.         /* If we only wanted the first occurence of this header, we're done. */
  466.         if (!all_p) break;
  467.       }
  468.     }
  469.  
  470.   if (result && !*result)  /* empty string */
  471.     {
  472.       XP_FREE(result);
  473.       return 0;
  474.     }
  475.  
  476.   return result;
  477. }
  478.  
  479. char *
  480. MimeHeaders_get_parameter (const char *header_value, const char *parm_name)
  481. {
  482.   const char *str;
  483.   int32 parm_len;
  484.   if (!header_value || !parm_name || !*header_value || !*parm_name)
  485.     return 0;
  486.  
  487.   /* The format of these header lines is
  488.      <token> [ ';' <token> '=' <token-or-quoted-string> ]*
  489.    */
  490.  
  491.   str = header_value;
  492.   parm_len = XP_STRLEN(parm_name);
  493.  
  494.   /* Skip forward to first ';' */
  495.   for (; *str && *str != ';' && *str != ','; str++)
  496.     ;
  497.   if (*str)
  498.     str++;
  499.   /* Skip over following whitespace */
  500.   for (; *str && XP_IS_SPACE(*str); str++)
  501.     ;
  502.   if (!*str)
  503.     return 0;
  504.  
  505.   while (*str)
  506.     {
  507.       const char *token_start = str;
  508.       const char *token_end = 0;
  509.       const char *value_start = str;
  510.       const char *value_end = 0;
  511.  
  512.       XP_ASSERT(!XP_IS_SPACE(*str)); /* should be after whitespace already */
  513.  
  514.       /* Skip forward to the end of this token. */
  515.       for (; *str && !XP_IS_SPACE(*str) && *str != '=' && *str != ';'; str++)
  516.         ;
  517.       token_end = str;
  518.  
  519.       /* Skip over whitespace, '=', and whitespace */
  520.       while (XP_IS_SPACE (*str)) str++;
  521.       if (*str == '=') str++;
  522.       while (XP_IS_SPACE (*str)) str++;
  523.  
  524.       if (*str != '"')
  525.         {
  526.           /* The value is a token, not a quoted string. */
  527.           value_start = str;
  528.           for (value_end = str;
  529.                *value_end && !XP_IS_SPACE (*value_end) && *value_end != ';';
  530.                value_end++)
  531.             ;
  532.           str = value_end;
  533.         }
  534.       else
  535.         {
  536.           /* The value is a quoted string. */
  537.           str++;
  538.           value_start = str;
  539.           for (value_end = str; *value_end; value_end++)
  540.             {
  541.               if (*value_end == '\\')
  542.                 value_end++;
  543.               else if (*value_end == '"')
  544.                 break;
  545.             }
  546.           str = value_end+1;
  547.         }
  548.  
  549.       /* See if this is the parameter we're looking for.
  550.          If so, copy it and return.
  551.        */
  552.       if (token_end - token_start == parm_len &&
  553.           !strncasecomp(token_start, parm_name, parm_len))
  554.         {
  555.           char *s = (char *) XP_ALLOC ((value_end - value_start) + 1);
  556.           if (! s) return 0;  /* MK_OUT_OF_MEMORY */
  557.           XP_MEMCPY (s, value_start, value_end - value_start);
  558.           s [value_end - value_start] = 0;
  559.           return s;
  560.         }
  561.  
  562.       /* str now points after the end of the value.
  563.          skip over whitespace, ';', whitespace. */
  564.       while (XP_IS_SPACE (*str)) str++;
  565.       if (*str == ';') str++;
  566.       while (XP_IS_SPACE (*str)) str++;
  567.     }
  568.   return 0;
  569. }
  570.  
  571.  
  572. /* Emitting HTML
  573.  */
  574.  
  575. #define MIME_HEADER_TABLE "<TABLE CELLPADDING=0 CELLSPACING=0 BORDER=0>"
  576. #define HEADER_START_JUNK  "<TR><TH VALIGN=BASELINE ALIGN=RIGHT NOWRAP>"
  577. #define HEADER_MIDDLE_JUNK ": </TH><TD>"
  578. #define HEADER_END_JUNK    "</TD></TR>"
  579.  
  580.  
  581. #define MimeHeaders_write(OPT,DATA,LENGTH) \
  582.     MimeOptions_write((OPT), (DATA), (LENGTH), TRUE);
  583.  
  584.  
  585. static char *
  586. MimeHeaders_default_news_link_generator (const char *dest, void *closure,
  587.                                          MimeHeaders *headers)
  588. {
  589.   /* This works as both the generate_news_url_fn and as the
  590.      generate_reference_url_fn. */
  591.   char *prefix = "news:";
  592.   char *new_dest = NET_Escape (dest, URL_XALPHAS);
  593.   char *result = (char *) XP_ALLOC (XP_STRLEN (new_dest) +
  594.                                     XP_STRLEN (prefix) + 1);
  595.   if (result)
  596.     {
  597.       XP_STRCPY (result, prefix);
  598.       XP_STRCAT (result, new_dest);
  599.     }
  600.   FREEIF (new_dest);
  601.   return result;
  602. }
  603.  
  604.  
  605. #ifdef MOZILLA_30
  606. static char *
  607. MimeHeaders_default_mailto_link_generator (const char *dest, void *closure,
  608.                                            MimeHeaders *headers)
  609. {
  610.   /* For now, don't emit mailto links over email addresses. */
  611.   return 0;
  612. }
  613.  
  614. #else  /* !MOZILLA_30 */
  615.  
  616. static char *mime_escape_quotes (char *src)
  617. {
  618.     /* Make sure any single or double quote is escaped with a backslash */
  619.     char *dst = XP_ALLOC((2 * XP_STRLEN(src)) + 1);
  620.     if (dst)
  621.     {
  622.         char *walkDst = dst;
  623.         while (*src)
  624.         {
  625.             if (*src == '\'' || *src == '\"' ) /* is it a quote? */
  626.                 if (walkDst == dst || *(src-1) != '\\')  /* is it escaped? */
  627.                     *walkDst++ = '\\';
  628.             *walkDst++ = *src++;
  629.         }
  630.         *walkDst = '\0';
  631.     }
  632.     return dst;
  633. }
  634.  
  635. static char *
  636. MimeHeaders_default_addbook_link_generator (const char *dest, void *closure,
  637.                                            MimeHeaders *headers)
  638. {
  639.   char* names;
  640.   char* addresses;
  641.   char* name = NULL;
  642.   char* addr = NULL;
  643.   char* unquotedName;
  644.   char* unquotedAddr;
  645.   char* result = NULL;
  646.   char* tmp;
  647.   char* tmp2;
  648.   char* mouseOverText = NULL;
  649.   int j;
  650.   int num = MSG_ParseRFC822Addresses(dest, &names, &addresses);
  651.   XP_ASSERT(num >= 1);
  652.   for (j=0 ; j<num ; j++) {
  653.     if (addr) {
  654.       addr = addr + XP_STRLEN(addr) + 1;
  655.       name = name + XP_STRLEN(name) + 1;
  656.     } else {
  657.       addr = addresses;
  658.       name = names;
  659.     }
  660.     if (!addr || !*addr) continue;
  661.  
  662.     unquotedName = NULL;
  663.     unquotedAddr = NULL;
  664.     MSG_UnquotePhraseOrAddr (addr, &unquotedAddr);
  665.     if (!unquotedAddr) 
  666.         continue;
  667.     if (name)
  668.         MSG_UnquotePhraseOrAddr (name, &unquotedName);
  669.  
  670.     tmp = PR_smprintf("begin:vcard\nfn:%s\nemail;internet:%s\nend:vcard\n",
  671.                       (unquotedName && *unquotedName) ? unquotedName : unquotedAddr,
  672.                       unquotedAddr);
  673.  
  674.     /* Since the addbook: URL is a little gross to look at, try to use Javascript's 
  675.      * mouseOver event to plug some more human readable text into the status bar.
  676.      * If the user doesn't have JS enabled, they just get the gross URL.
  677.      */
  678.     if (closure && !mouseOverText)
  679.     {
  680.         /* Make sure we don't feed any unescaped single or double quotes to the
  681.          * Javascript interpreter, since that will cause the string termination
  682.          * to be wrong, and things go downhill fast from there
  683.          */
  684.         char *jsSafeName = mime_escape_quotes ((unquotedName && *unquotedName) ? unquotedName : unquotedAddr);
  685.         if (jsSafeName)
  686.         {
  687.             char *localizedPart = PR_smprintf (XP_GetString (MK_MSG_ADDBOOK_MOUSEOVER_TEXT), jsSafeName);
  688.             if (localizedPart)
  689.             {
  690.                 mouseOverText = PR_smprintf ("onMouseOver=\"window.status='%s'; return true\" onMouseOut=\"window.status=''\"", localizedPart);
  691.                 XP_FREE(localizedPart);
  692.                 *((char**)closure) = mouseOverText;
  693.             }
  694.             XP_FREE(jsSafeName);
  695.         }
  696.     }
  697.  
  698.     FREEIF(unquotedAddr);
  699.     FREEIF(unquotedName);
  700.  
  701.     if (!tmp) break;
  702.     tmp2 = NET_Escape(tmp, URL_XALPHAS);
  703.     XP_FREE(tmp);
  704.     if (!tmp2) break;
  705.     result = PR_smprintf("addbook:add?vcard=%s", tmp2);
  706.     XP_FREE(tmp2);
  707.     break;
  708.   }
  709.   FREEIF(names);
  710.   FREEIF(addresses);
  711.   return result;
  712. }
  713.  
  714. #endif /* !MOZILLA_30 */
  715.  
  716.  
  717. #define MimeHeaders_grow_obuffer(hdrs, desired_size) \
  718.   ((((long) (desired_size)) >= ((long) (hdrs)->obuffer_size)) ? \
  719.    msg_GrowBuffer ((desired_size), sizeof(char), 255, \
  720.                    &(hdrs)->obuffer, &(hdrs)->obuffer_size) \
  721.    : 0)
  722.  
  723.  
  724. static int
  725. MimeHeaders_convert_rfc1522(MimeDisplayOptions *opt,
  726.                             const char *input, int32 input_length,
  727.                             char **output_ret, int32 *output_length_ret)
  728. {
  729.   *output_ret = 0;
  730.   *output_length_ret = 0;
  731.   if (input && *input && opt && opt->rfc1522_conversion_fn)
  732.     {
  733.       char *converted = 0;
  734.       int32 converted_len = 0;
  735.       const char *output_charset = (opt->override_charset
  736.                                     ? opt->override_charset
  737.                                     : opt->default_charset);
  738.       int status =
  739.         opt->rfc1522_conversion_fn(input, input_length,
  740.                                    0, output_charset,  /* no input charset? */
  741.                                    &converted, &converted_len,
  742.                                    opt->stream_closure);
  743.       if (status < 0)
  744.         {
  745.           FREEIF(converted);
  746.           return status;
  747.         }
  748.  
  749.       if (converted)
  750.         {
  751.           *output_ret = converted;
  752.           *output_length_ret = converted_len;
  753.         }
  754.     }
  755.   return 0;
  756. }
  757.  
  758.  
  759.  
  760. static const char *
  761. MimeHeaders_localize_header_name(const char *name, MimeDisplayOptions *opt)
  762. {
  763.   const char *new_name = 0;
  764.   int16 output_csid = 0;
  765.   int display_key =
  766.    (!strcasecomp(name,HEADER_BCC)             ? MK_MIMEHTML_DISP_BCC :
  767.     !strcasecomp(name,HEADER_CC)             ? MK_MIMEHTML_DISP_CC :
  768.     !strcasecomp(name,HEADER_DATE)             ? MK_MIMEHTML_DISP_DATE :
  769.     !strcasecomp(name,HEADER_FOLLOWUP_TO)     ? MK_MIMEHTML_DISP_FOLLOWUP_TO :
  770.     !strcasecomp(name,HEADER_FROM)             ? MK_MIMEHTML_DISP_FROM :
  771.     !strcasecomp(name,HEADER_MESSAGE_ID)     ? MK_MIMEHTML_DISP_MESSAGE_ID :
  772.     !strcasecomp(name,HEADER_NEWSGROUPS)     ? MK_MIMEHTML_DISP_NEWSGROUPS :
  773.     !strcasecomp(name,HEADER_ORGANIZATION)     ? MK_MIMEHTML_DISP_ORGANIZATION :
  774.     !strcasecomp(name,HEADER_REFERENCES)     ? MK_MIMEHTML_DISP_REFERENCES :
  775.     !strcasecomp(name,HEADER_REPLY_TO)         ? MK_MIMEHTML_DISP_REPLY_TO :
  776.     !strcasecomp(name,HEADER_RESENT_CC)         ? MK_MIMEHTML_DISP_RESENT_CC :
  777.     !strcasecomp(name,HEADER_RESENT_COMMENTS)
  778.                                            ? MK_MIMEHTML_DISP_RESENT_COMMENTS :
  779.     !strcasecomp(name,HEADER_RESENT_DATE)     ? MK_MIMEHTML_DISP_RESENT_DATE :
  780.     !strcasecomp(name,HEADER_RESENT_FROM)     ? MK_MIMEHTML_DISP_RESENT_FROM :
  781.     !strcasecomp(name,HEADER_RESENT_MESSAGE_ID)
  782.                                          ? MK_MIMEHTML_DISP_RESENT_MESSAGE_ID :
  783.     !strcasecomp(name,HEADER_RESENT_SENDER)  ? MK_MIMEHTML_DISP_RESENT_SENDER :
  784.     !strcasecomp(name,HEADER_RESENT_TO)         ? MK_MIMEHTML_DISP_RESENT_TO :
  785.     !strcasecomp(name,HEADER_SENDER)         ? MK_MIMEHTML_DISP_SENDER :
  786.     !strcasecomp(name,HEADER_SUBJECT)         ? MK_MIMEHTML_DISP_SUBJECT :
  787.     !strcasecomp(name,HEADER_TO)             ? MK_MIMEHTML_DISP_TO :
  788.     !strcasecomp(name,HEADER_SUBJECT)         ? MK_MIMEHTML_DISP_SUBJECT :
  789.     0);
  790.   
  791.   if (!display_key)
  792.     return name;
  793.  
  794. #ifndef MOZILLA_30
  795.   output_csid = INTL_CharSetNameToID((opt->override_charset
  796.                                       ? opt->override_charset
  797.                                       : opt->default_charset));
  798.   new_name = XP_GetStringForHTML(display_key, output_csid, (char*)name);
  799. #endif /* !MOZILLA_30 */
  800.  
  801.   if (new_name)
  802.     return new_name;
  803.   else
  804.     return name;
  805. }
  806.  
  807.  
  808. static int
  809. MimeHeaders_write_random_header_1 (MimeHeaders *hdrs,
  810.                                    const char *name, const char *contents,
  811.                                    MimeDisplayOptions *opt,
  812.                                    XP_Bool subject_p)
  813. {
  814.   int status = 0;
  815.   int32 contents_length;
  816.   char *out;
  817.   char *converted = 0;
  818.   int32 converted_length = 0;
  819.  
  820.   if (!contents && subject_p)
  821.     contents = "";
  822.   else if (!contents)
  823.     return 0;
  824.  
  825.   name = MimeHeaders_localize_header_name(name, opt);
  826.  
  827.   contents_length = XP_STRLEN(contents);
  828.   status = MimeHeaders_convert_rfc1522(opt, contents, contents_length,
  829.                                        &converted, &converted_length);
  830.   if (status < 0) return status;
  831.   if (converted)
  832.     {
  833.       contents = converted;
  834.       contents_length = converted_length;
  835.     }
  836.  
  837.   status = MimeHeaders_grow_obuffer (hdrs, XP_STRLEN (name) + 100 +
  838.                                      (contents_length * 4));
  839.   if (status < 0)
  840.     {
  841.       FREEIF(converted);
  842.       return status;
  843.     }
  844.  
  845.   out = hdrs->obuffer;
  846.  
  847.   if (subject_p && contents)
  848.     {
  849.       XP_ASSERT(hdrs->munged_subject == NULL);
  850.       hdrs->munged_subject = XP_STRDUP(contents);
  851.     }
  852.  
  853.   if (opt->fancy_headers_p)
  854.     {
  855.       XP_STRCPY (out, HEADER_START_JUNK); out += XP_STRLEN (out);
  856.       XP_STRCPY (out, name); out += XP_STRLEN (out);
  857.       XP_STRCPY (out, HEADER_MIDDLE_JUNK); out += XP_STRLEN (out);
  858.       if (subject_p)
  859.         {
  860.           XP_STRCPY (out, "<B>"); out += XP_STRLEN (out);
  861.         }
  862.  
  863.       /* Copy `contents' to `out', quoting HTML along the way.
  864.          Note: this function does no charset conversion; that has
  865.          already been done.
  866.        */
  867.       status = NET_ScanForURLs (
  868. #ifndef MOZILLA_30
  869.                                 NULL,
  870. #endif /* !MOZILLA_30 */
  871.                                 contents, contents_length, out,
  872.                                 hdrs->obuffer_size - (out - hdrs->obuffer) -10,
  873.                                 TRUE);
  874.       if (status < 0) return status;
  875.       out += XP_STRLEN(out);
  876.       XP_ASSERT(out < (hdrs->obuffer + hdrs->obuffer_size));
  877.  
  878.       XP_STRCPY (out, HEADER_END_JUNK); out += XP_STRLEN (out);
  879.     }
  880.   else
  881.     {
  882.       /* The non-fancy version (no tables): for converting to plain text. */
  883.       char *s = NET_EscapeHTML (contents);
  884.       if (s)
  885.         {
  886.           char *start, *end, *data_end;
  887.           XP_STRCPY (out, "<NOBR><B>"); out += XP_STRLEN (out);
  888.           XP_STRCPY (out, name); out += XP_STRLEN (out);
  889.           XP_STRCPY (out, ": </B>"); out += XP_STRLEN (out);
  890.  
  891.           data_end = s + XP_STRLEN (s);
  892.           start = s;
  893.           end = start;
  894.           while (end < data_end)
  895.             for (end = start; end < data_end; end++)
  896.               if (*end == CR || *end == LF)
  897.                 {
  898.                   XP_MEMCPY (out, start, end - start); out += (end - start);
  899.                   XP_STRCPY (out, "<BR>    ");
  900.                   out += XP_STRLEN (out);
  901.                   if (*end == CR && end < data_end && end[1] == LF)
  902.                     end++;
  903.                   start = end + 1;
  904.                 }
  905.           if (start < end)
  906.             {
  907.               XP_MEMCPY (out, start, end - start); out += (end - start);
  908.             }
  909.           XP_STRCPY (out, "</NOBR><BR>"); out += XP_STRLEN (out);
  910.           XP_FREE (s);
  911.         }
  912.     }
  913.   *out = 0; 
  914.  
  915.   status = MimeHeaders_write(opt, hdrs->obuffer, out - hdrs->obuffer);
  916.   FREEIF(converted);
  917.   if (status < 0)
  918.     return status;
  919.   else
  920.     return 1;
  921. }
  922.  
  923.  
  924. static int
  925. MimeHeaders_write_random_header (MimeHeaders *hdrs, const char *name,
  926.                                  XP_Bool all_p, MimeDisplayOptions *opt)
  927. {
  928.   int status = 0;
  929.   char *contents = MimeHeaders_get (hdrs, name, FALSE, all_p);
  930.   if (!contents) return 0;
  931.   status = MimeHeaders_write_random_header_1(hdrs, name, contents, opt, FALSE);
  932.   XP_FREE(contents);
  933.   return status;
  934. }
  935.  
  936.  
  937. static int
  938. MimeHeaders_write_subject_header_1 (MimeHeaders *hdrs, const char *name,
  939.                                     const char *contents,
  940.                                     MimeDisplayOptions *opt)
  941. {
  942.   return MimeHeaders_write_random_header_1(hdrs, name, contents, opt, TRUE);
  943. }
  944.  
  945. static int
  946. MimeHeaders_write_subject_header (MimeHeaders *hdrs, const char *name,
  947.                                   MimeDisplayOptions *opt)
  948. {
  949.   int status = 0;
  950.   char *contents = MimeHeaders_get (hdrs, name, FALSE, FALSE);
  951.   status = MimeHeaders_write_subject_header_1 (hdrs, name, contents, opt);
  952.   FREEIF(contents);
  953.   return status;
  954. }
  955.  
  956.  
  957. static int
  958. MimeHeaders_write_grouped_header_1 (MimeHeaders *hdrs, const char *name,
  959.                                     const char *contents,
  960.                                     MimeDisplayOptions *opt,
  961.                                     XP_Bool mail_header_p)
  962. {
  963.   int status = 0;
  964.   int32 contents_length;
  965.   char *converted = 0;
  966.   char *out;
  967.   if (!contents)
  968.     return 0;
  969.  
  970.   if (!opt->fancy_headers_p)
  971.     return MimeHeaders_write_random_header (hdrs, name, TRUE, opt);
  972.  
  973.   if (name) name = MimeHeaders_localize_header_name(name, opt);
  974.  
  975.   contents_length = XP_STRLEN(contents);
  976.  
  977.   if (mail_header_p)
  978.     {
  979.       int32 converted_length = 0;
  980.       status = MimeHeaders_convert_rfc1522(opt, contents, contents_length,
  981.                                            &converted, &converted_length);
  982.       if (status < 0) return status;
  983.       if (converted)
  984.         {
  985.           contents = converted;
  986.           contents_length = converted_length;
  987.         }
  988.     }
  989.  
  990. #ifndef MOZILLA_30
  991.   /* grow obuffer with potential XSENDER AUTH string */
  992.   status = MimeHeaders_grow_obuffer (hdrs, (name ? XP_STRLEN (name) : 0)
  993.                                      + 100 + 2 *
  994.                                      XP_STRLEN(XP_GetString(MK_MSG_XSENDER_INTERNAL)) +
  995.                                      (contents_length * 4));
  996. #else
  997.   status = MimeHeaders_grow_obuffer (hdrs, (name ? XP_STRLEN (name) : 0)
  998.                                      + 100 +
  999.                                      (contents_length * 4));
  1000. #endif
  1001.   if (status < 0) goto FAIL;
  1002.  
  1003.   out = hdrs->obuffer;
  1004.   if (name) {
  1005.       XP_STRCPY (out, HEADER_START_JUNK); out += XP_STRLEN (out);
  1006.       XP_STRCPY (out, name); out += XP_STRLEN (out);
  1007.       XP_STRCPY (out, HEADER_MIDDLE_JUNK); out += XP_STRLEN (out);
  1008.   }
  1009.  
  1010.   status = MimeHeaders_write(opt, hdrs->obuffer, out - hdrs->obuffer);
  1011.   if (status < 0) goto FAIL;
  1012.  
  1013.   {
  1014.     const char *data_end = contents + contents_length;
  1015.     const char *last_end = 0;
  1016.     const char *this_start = contents;
  1017.     const char *this_end = this_start;
  1018.     while (this_end < data_end)
  1019.       {
  1020.         int32 paren_depth = 0;
  1021.  
  1022.         /* Skip over whitespace and commas. */
  1023.         while (this_start < data_end &&
  1024.                (XP_IS_SPACE (*this_start) || *this_start == ','))
  1025.           this_start++;
  1026.  
  1027.         this_end = this_start;
  1028.  
  1029.         /* Skip to the end, or the next comma, taking quoted strings,
  1030.            comments, and backslashes into account. */
  1031.         while (this_end < data_end &&
  1032.                !(*this_end == ',' && paren_depth <= 0))
  1033.           {
  1034.             if (*this_end == '\\')
  1035.               this_end++;
  1036.             else if (*this_end == '"')
  1037.               {
  1038.                 this_end++;
  1039.                 while (this_end < data_end && *this_end != '"')
  1040.                   {
  1041.                     if (*this_end == '\\')
  1042.                       this_end++;
  1043.                     this_end++;
  1044.                   }
  1045.               }
  1046.  
  1047.             if (*this_end == '(')
  1048.               paren_depth++;
  1049.             else if (*this_end == ')' && paren_depth > 0)
  1050.               paren_depth--;
  1051.  
  1052.             this_end++;
  1053.           }
  1054.  
  1055.         /* Push out the preceeding comma, whitespace, etc. */
  1056.         if (last_end && last_end != this_start)
  1057.           {
  1058.             char *s;
  1059.             XP_STRCPY (hdrs->obuffer, "</NOBR>");
  1060.             status = MimeHeaders_write(opt, hdrs->obuffer,
  1061.                                        XP_STRLEN(hdrs->obuffer));
  1062.             if (status < 0) goto FAIL;
  1063.  
  1064.             XP_MEMCPY (hdrs->obuffer, last_end, this_start - last_end);
  1065.             hdrs->obuffer [this_start - last_end] = 0;
  1066.             s = NET_EscapeHTML (hdrs->obuffer);
  1067.             if (!s)
  1068.               {
  1069.                 status = MK_OUT_OF_MEMORY;
  1070.                 goto FAIL;
  1071.               }
  1072.             status = MimeHeaders_write(opt, s, XP_STRLEN (s));
  1073.             XP_FREE (s);
  1074.             if (status < 0) goto FAIL;
  1075.  
  1076.             /* Emit a space, to make sure long newsgroup lines, or any
  1077.                header line with no spaces after the commas, wrap. */
  1078.             hdrs->obuffer[0] = ' ';
  1079.             hdrs->obuffer[1] = 0;
  1080.             status = MimeHeaders_write(opt, hdrs->obuffer,
  1081.                                        XP_STRLEN (hdrs->obuffer));
  1082.             if (status < 0) goto FAIL;
  1083.           }
  1084.  
  1085.         /* Push out this address-or-newsgroup. */
  1086.         if (this_start != this_end)
  1087.           {
  1088.             char *s;
  1089.             MimeHTMLGeneratorFunction fn = 0;
  1090.             void *arg;
  1091.             char *link, *link2;
  1092.             XP_Bool mail_p;
  1093.             char *extraAnchorText = NULL;
  1094.  
  1095.             XP_MEMCPY (hdrs->obuffer, this_start, this_end - this_start);
  1096.             hdrs->obuffer [this_end - this_start] = 0;
  1097.             s = NET_EscapeHTML (hdrs->obuffer);
  1098.             if (!s)
  1099.               {
  1100.                 status = MK_OUT_OF_MEMORY;
  1101.                 goto FAIL;
  1102.               }
  1103.             mail_p = (mail_header_p || !strcasecomp ("poster", hdrs->obuffer));
  1104.  
  1105.             if (mail_p)
  1106.               {
  1107.                 fn = opt->generate_mailto_url_fn;
  1108.                 arg = opt->html_closure;
  1109.               }
  1110.             else
  1111.               {
  1112.                 fn = opt->generate_news_url_fn;
  1113.                 arg = opt->html_closure;
  1114.               }
  1115.             if (! fn)
  1116.               {
  1117.                 if (mail_p)
  1118.                   {
  1119. #ifdef MOZILLA_30
  1120.                     fn = MimeHeaders_default_mailto_link_generator;
  1121.                     arg = 0;
  1122. #else  /* !MOZILLA_30 */
  1123.                     fn = MimeHeaders_default_addbook_link_generator;
  1124.                     arg = NULL; /* ###phil fix this &extraAnchorText; */
  1125. #endif /* !MOZILLA_30 */
  1126.                   }
  1127.                 else
  1128.                   {
  1129.                     fn = MimeHeaders_default_news_link_generator;
  1130.                     arg = 0;
  1131.                   }
  1132.               }
  1133.  
  1134.             /* If this is the "From" header, or if this is a
  1135.                "Followup-To: poster" entry, let the Reply-To address win.
  1136.              */
  1137.             if (mail_header_p
  1138.                 ? (name && !strcasecomp(name, HEADER_FROM))
  1139.                 : mail_p)
  1140.               {
  1141.                 char *r = 0;
  1142.                 r         = MimeHeaders_get (hdrs, HEADER_REPLY_TO,
  1143.                                              FALSE, TRUE);
  1144.                 if (!r) r = MimeHeaders_get (hdrs, HEADER_FROM, FALSE, TRUE);
  1145.                 if (!r) r = MimeHeaders_get (hdrs, HEADER_SENDER, FALSE, TRUE);
  1146.  
  1147.                 if (r)
  1148.                   {
  1149.                     XP_STRCPY (hdrs->obuffer, r);
  1150.                     XP_FREE(r);
  1151.                   }
  1152.                 else
  1153.                   *hdrs->obuffer = 0;
  1154.               }
  1155.  
  1156.             if (*hdrs->obuffer && opt->fancy_links_p)
  1157.               link = fn (hdrs->obuffer, arg, hdrs);
  1158.             else
  1159.               link = 0;
  1160.  
  1161.             link2 = (link ? NET_EscapeHTML (link) : 0);
  1162.             FREEIF (link);
  1163.             link = link2;
  1164.  
  1165.             if (link)
  1166.               {
  1167.                 status = MimeHeaders_grow_obuffer (hdrs, XP_STRLEN (link) +
  1168.                                                    XP_STRLEN (s) + 100);
  1169.                 if (status < 0) goto FAIL;
  1170.               }
  1171.             out = hdrs->obuffer;
  1172.             if (link)
  1173.               {
  1174.                 XP_STRCPY (out, "<A HREF=\""); out += XP_STRLEN (out);
  1175.                 XP_STRCPY (out, link); out += XP_STRLEN (out);
  1176.                 XP_STRCPY (out, "\""); out += 1;
  1177.                 if (extraAnchorText)
  1178.                 {
  1179.                     XP_STRCPY (out, extraAnchorText);
  1180.                     out += XP_STRLEN(out);
  1181.                 }
  1182.                 XP_STRCPY (out, ">"); out += 1;
  1183.               }
  1184.             XP_STRCPY (out, "<NOBR>"); out += XP_STRLEN (out);
  1185.  
  1186.             XP_STRCPY (out, s); out += XP_STRLEN (out);
  1187.  
  1188.             XP_FREE (s);
  1189.             if (link)
  1190.               {
  1191.                 XP_STRCPY (out, "</A>"); out += XP_STRLEN (out);
  1192.                 XP_FREE (link);
  1193.               }
  1194.             if (extraAnchorText)
  1195.                 XP_FREE (extraAnchorText);
  1196. #ifndef MOZILLA_30
  1197.             /* Begin hack of out of envelope XSENDER info */
  1198.             if (name && !strcasecomp(name, HEADER_FROM)) { 
  1199.                 char * statusLine = MimeHeaders_get (hdrs, HEADER_X_MOZILLA_STATUS, FALSE, FALSE);
  1200.                 uint16 flags =0;
  1201.  
  1202.                 #define UNHEX(C) \
  1203.                                 ((C >= '0' && C <= '9') ? C - '0' : \
  1204.                                  ((C >= 'A' && C <= 'F') ? C - 'A' + 10 : \
  1205.                                   ((C >= 'a' && C <= 'f') ? C - 'a' + 10 : 0)))
  1206.  
  1207.                 if (statusLine && XP_STRLEN(statusLine) == 4) {
  1208.                     int i;
  1209.                     char *cp = statusLine;
  1210.                     for (i=0; i < 4; i++, cp++) {
  1211.                         flags = (flags << 4) | UNHEX(*cp);
  1212.                     }
  1213.                     FREEIF(statusLine);
  1214.                 }
  1215.  
  1216.                 if (flags & MSG_FLAG_SENDER_AUTHED) {
  1217.                     XP_STRCPY (out, XP_GetString(MK_MSG_XSENDER_INTERNAL));
  1218.                     out += XP_STRLEN (out);
  1219.                 }
  1220.             }
  1221.             /* End of XSENDER info */
  1222. #endif  /* MOZILLA_30 */
  1223.             status = MimeHeaders_write(opt, hdrs->obuffer,
  1224.                                        XP_STRLEN (hdrs->obuffer));
  1225.             if (status < 0) goto FAIL;
  1226.           }
  1227.  
  1228.         /* now go around again */
  1229.         last_end = this_end;
  1230.         this_start = last_end;
  1231.       }
  1232.   }
  1233.  
  1234.   out = hdrs->obuffer;
  1235.   XP_STRCPY (out, "</NOBR>"); out += XP_STRLEN (out);
  1236.   if (name) XP_STRCPY (out, HEADER_END_JUNK); out += XP_STRLEN (out);
  1237.   *out = 0;
  1238.   status = MimeHeaders_write(opt, hdrs->obuffer, out - hdrs->obuffer);
  1239.   if (status < 0) goto FAIL;
  1240.   else status = 1;
  1241.  
  1242.  FAIL:
  1243.   FREEIF(converted);
  1244.   return status;
  1245. }
  1246.  
  1247. static int
  1248. MimeHeaders_write_grouped_header (MimeHeaders *hdrs, const char *name,
  1249.                                   MimeDisplayOptions *opt,
  1250.                                   XP_Bool mail_header_p)
  1251. {
  1252.   int status = 0;
  1253.   char *contents = MimeHeaders_get (hdrs, name, FALSE, TRUE);
  1254.   if (!contents) return 0;
  1255.   status = MimeHeaders_write_grouped_header_1 (hdrs, name, contents, opt,
  1256.                                                mail_header_p);
  1257.   XP_FREE(contents);
  1258.   return status;
  1259. }
  1260.  
  1261. #define MimeHeaders_write_address_header(hdrs,name,opt) \
  1262.     MimeHeaders_write_grouped_header(hdrs,name,opt,TRUE)
  1263. #define MimeHeaders_write_news_header(hdrs,name,opt) \
  1264.     MimeHeaders_write_grouped_header(hdrs,name,opt,FALSE)
  1265.  
  1266. #define MimeHeaders_write_address_header_1(hdrs,name,contents,opt) \
  1267.     MimeHeaders_write_grouped_header_1(hdrs,name,contents,opt,TRUE)
  1268. #define MimeHeaders_write_news_header_1(hdrs,name,contents,opt) \
  1269.     MimeHeaders_write_grouped_header_1(hdrs,name,contents,opt,FALSE)
  1270.  
  1271.  
  1272. static int
  1273. MimeHeaders_write_id_header_1 (MimeHeaders *hdrs, const char *name,
  1274.                                const char *contents, XP_Bool show_ids,
  1275.                                MimeDisplayOptions *opt)
  1276. {
  1277.   int status = 0;
  1278.   int32 contents_length;
  1279.   char *out;
  1280.   if (!contents)
  1281.     return 0;
  1282.  
  1283.   if (!opt->fancy_headers_p)
  1284.     return MimeHeaders_write_random_header (hdrs, name, TRUE, opt);
  1285.  
  1286.   name = MimeHeaders_localize_header_name(name, opt);
  1287.  
  1288.   contents_length = XP_STRLEN(contents);
  1289.   status = MimeHeaders_grow_obuffer (hdrs, XP_STRLEN (name) + 100 +
  1290.                                      (contents_length * 4));
  1291.   if (status < 0) goto FAIL;
  1292.  
  1293.   out = hdrs->obuffer;
  1294.   XP_STRCPY (out, HEADER_START_JUNK); out += XP_STRLEN (out);
  1295.   XP_STRCPY (out, name); out += XP_STRLEN (out);
  1296.   XP_STRCPY (out, HEADER_MIDDLE_JUNK); out += XP_STRLEN (out);
  1297.  
  1298.   status = MimeHeaders_write(opt, hdrs->obuffer, out - hdrs->obuffer);
  1299.   if (status < 0) goto FAIL;
  1300.  
  1301.   {
  1302.     const char *data_end = contents + contents_length;
  1303.     const char *last_end = 0;
  1304.     const char *this_start = contents;
  1305.     const char *this_end = this_start;
  1306.     int id_count = 0;
  1307.     while (this_end < data_end)
  1308.       {
  1309.         /* Skip over whitespace. */
  1310.         while (this_start < data_end && XP_IS_SPACE (*this_start))
  1311.           this_start++;
  1312.  
  1313.         this_end = this_start;
  1314.  
  1315.         /* At this point *this_start should be '<', but if it's not,
  1316.            there's not a lot we can do about it... */
  1317.  
  1318.         /* Skip to the end of the message ID, taking quoted strings and
  1319.            backslashes into account. */
  1320.         while (this_end < data_end && *this_end != '>')
  1321.           {
  1322.             if (*this_end == '\\')
  1323.               this_end++;
  1324.             else if (*this_end == '"')
  1325.               while (this_end < data_end && *this_end != '"')
  1326.                 {
  1327.                   if (*this_end == '\\')
  1328.                     this_end++;
  1329.                   this_end++;
  1330.                 }
  1331.             this_end++;
  1332.           }
  1333.  
  1334.         if (*this_end == '>')  /* If it's not, that's illegal. */
  1335.           this_end++;
  1336.  
  1337.         /* Push out the preceeding whitespace. */
  1338.         if (last_end && last_end != this_start)
  1339.           {
  1340.             char *s;
  1341.             XP_MEMCPY (hdrs->obuffer, last_end, this_start - last_end);
  1342.             hdrs->obuffer [this_start - last_end] = 0;
  1343.             s = NET_EscapeHTML (hdrs->obuffer);
  1344.             if (!s)
  1345.               {
  1346.                 status = MK_OUT_OF_MEMORY;
  1347.                 goto FAIL;
  1348.               }
  1349.             status = MimeHeaders_write(opt, s, XP_STRLEN (s));
  1350.             XP_FREE (s);
  1351.             if (status < 0) goto FAIL;
  1352.           }
  1353.  
  1354.         /* Push out this ID. */
  1355.         if (this_start != this_end)
  1356.           {
  1357.             char *s;
  1358.             MimeHTMLGeneratorFunction fn = 0;
  1359.             void *arg;
  1360.             char *link;
  1361.  
  1362.             fn = opt->generate_reference_url_fn;
  1363.             arg = opt->html_closure;
  1364.             if (! fn)
  1365.               {
  1366.                 fn = MimeHeaders_default_news_link_generator;
  1367.                 arg = 0;
  1368.               }
  1369.  
  1370.             {
  1371.               char *link2 = NULL;
  1372.               const char *id = this_start;
  1373.               int32 size = this_end - this_start;
  1374.               if (*id == '<')
  1375.                 id++, size--;
  1376.               XP_MEMCPY (hdrs->obuffer, id, size);
  1377.               hdrs->obuffer [size] = 0;
  1378.               if (hdrs->obuffer [size-1] == '>')
  1379.                 hdrs->obuffer [size-1] = 0;
  1380.  
  1381.               link = fn (hdrs->obuffer, arg, hdrs);
  1382.               if (link)
  1383.                 {
  1384.                   link2 = NET_EscapeHTML(link);
  1385.                   XP_FREE(link);
  1386.                 }
  1387.               link = link2;
  1388.             }
  1389.  
  1390.             if (show_ids)
  1391.               {
  1392.                 XP_MEMCPY (hdrs->obuffer, this_start, this_end - this_start);
  1393.                 hdrs->obuffer [this_end - this_start] = 0;
  1394.                 s = NET_EscapeHTML (hdrs->obuffer);
  1395.                 if (!s)
  1396.                   {
  1397.                     status = MK_OUT_OF_MEMORY;
  1398.                     goto FAIL;
  1399.                   }
  1400.               }
  1401.             else
  1402.               {
  1403.                 char buf[50];
  1404.                 XP_SPRINTF(buf, "%d", ++id_count);
  1405.                 s = XP_STRDUP(buf);
  1406.                 if (!s)
  1407.                   {
  1408.                     status = MK_OUT_OF_MEMORY;
  1409.                     goto FAIL;
  1410.                   }
  1411.               }
  1412.  
  1413.             if (link)
  1414.               {
  1415.                 status = MimeHeaders_grow_obuffer (hdrs, XP_STRLEN (link) +
  1416.                                                    XP_STRLEN (s) + 100);
  1417.                 if (status < 0) goto FAIL;
  1418.               }
  1419.             out = hdrs->obuffer;
  1420.  
  1421.             if (!show_ids && id_count > 1)
  1422.               {
  1423.                 XP_STRCPY (out, ", "); out += XP_STRLEN (out);
  1424.               }
  1425.               
  1426.             if (link)
  1427.               {
  1428.                 XP_STRCPY (out, "<A HREF=\""); out += XP_STRLEN (out);
  1429.                 XP_STRCPY (out, link); out += XP_STRLEN (out);
  1430.                 XP_STRCPY (out, "\">"); out += XP_STRLEN (out);
  1431.               }
  1432.             XP_STRCPY (out, s); out += XP_STRLEN (out);
  1433.             XP_FREE (s);
  1434.             if (link)
  1435.               {
  1436.                 XP_STRCPY (out, "</A>"); out += XP_STRLEN (out);
  1437.                 XP_FREE (link);
  1438.               }
  1439.             status = MimeHeaders_write(opt, hdrs->obuffer,
  1440.                                        XP_STRLEN (hdrs->obuffer));
  1441.             if (status < 0) goto FAIL;
  1442.           }
  1443.  
  1444.         /* now go around again */
  1445.         last_end = this_end;
  1446.         this_start = last_end;
  1447.       }
  1448.   }
  1449.  
  1450.   out = hdrs->obuffer;
  1451.   XP_STRCPY (out, HEADER_END_JUNK); out += XP_STRLEN (out);
  1452.   *out = 0;
  1453.   status = MimeHeaders_write(opt, hdrs->obuffer, out - hdrs->obuffer);
  1454.   if (status < 0) goto FAIL;
  1455.   else status = 1;
  1456.  
  1457.  FAIL:
  1458.   return status;
  1459. }
  1460.  
  1461. static int
  1462. MimeHeaders_write_id_header (MimeHeaders *hdrs, const char *name,
  1463.                              XP_Bool show_ids, MimeDisplayOptions *opt)
  1464. {
  1465.   int status = 0;
  1466.   char *contents = MimeHeaders_get (hdrs, name, FALSE, TRUE);
  1467.   if (!contents) return 0;
  1468.   status = MimeHeaders_write_id_header_1 (hdrs, name, contents, show_ids, opt);
  1469.   XP_FREE(contents);
  1470.   return status;
  1471. }
  1472.  
  1473.  
  1474.  
  1475. static int MimeHeaders_write_all_headers (MimeHeaders *, MimeDisplayOptions *);
  1476. static int MimeHeaders_write_microscopic_headers (MimeHeaders *,
  1477.                                                   MimeDisplayOptions *);
  1478. static int MimeHeaders_write_citation_headers (MimeHeaders *,
  1479.                                                MimeDisplayOptions *);
  1480.  
  1481.  
  1482. /* This list specifies the headers shown in the "normal" headers display mode,
  1483.    and also specifies the preferred ordering.  Headers which do not occur are
  1484.    not shown (except Subject) and certain header-pairs have some special
  1485.    behavior (see comments.)
  1486.  
  1487.   #### We should come up with some secret way to set this from a user 
  1488.   preference, perhaps from a special file in the ~/.netscape/ directory.
  1489.  */
  1490. static const char *mime_interesting_headers[] = {
  1491.   HEADER_SUBJECT,
  1492.   HEADER_RESENT_COMMENTS,
  1493.   HEADER_RESENT_DATE,
  1494.   HEADER_RESENT_SENDER,        /* not shown if HEADER_RESENT_FROM is present */
  1495.   HEADER_RESENT_FROM,
  1496.   HEADER_RESENT_TO,
  1497.   HEADER_RESENT_CC,
  1498.   HEADER_DATE,
  1499.   HEADER_SENDER,            /* not shown if HEADER_FROM is present */
  1500.   HEADER_FROM,
  1501.   HEADER_REPLY_TO,            /* not shown if HEADER_FROM has the same addr. */
  1502.   HEADER_ORGANIZATION,
  1503.   HEADER_TO,
  1504.   HEADER_CC,
  1505.   HEADER_BCC,
  1506.   HEADER_NEWSGROUPS,
  1507.   HEADER_FOLLOWUP_TO,
  1508.   HEADER_REFERENCES,        /* not shown in MimeHeadersSomeNoRef mode. */
  1509.  
  1510. #ifdef DEBUG_jwz
  1511.   HEADER_X_MAILER,            /* jwz finds these useful for debugging... */
  1512.   HEADER_X_NEWSREADER,
  1513.   HEADER_X_POSTING_SOFTWARE,
  1514. #endif
  1515.   0
  1516. };
  1517.  
  1518.  
  1519. static int
  1520. MimeHeaders_write_interesting_headers (MimeHeaders *hdrs,
  1521.                                        MimeDisplayOptions *opt)
  1522. {
  1523.   XP_Bool wrote_any_p = FALSE;
  1524.   int status = 0;
  1525.   const char **rest;
  1526.   XP_Bool did_from = FALSE;
  1527.   XP_Bool did_resent_from = FALSE;
  1528.  
  1529.   status = MimeHeaders_grow_obuffer (hdrs, 200);
  1530.   if (status < 0) return status;
  1531.  
  1532.   for (rest = mime_interesting_headers; *rest; rest++)
  1533.     {
  1534.       const char *name = *rest;
  1535.  
  1536.       /* The Subject header gets written in bold.
  1537.        */
  1538.       if (!strcasecomp(name, HEADER_SUBJECT))
  1539.         {
  1540.           status = MimeHeaders_write_subject_header (hdrs, name, opt);
  1541.         }
  1542.  
  1543.       /* The References header is never interesting if we're emitting paper,
  1544.          since you can't click on it.
  1545.        */
  1546.       else if (!strcasecomp(name, HEADER_REFERENCES))
  1547.         {
  1548.           if (opt->headers != MimeHeadersSomeNoRef)
  1549.             status = MimeHeaders_write_id_header (hdrs, name, FALSE, opt);
  1550.         }
  1551.  
  1552.       /* Random other Message-ID headers.  These aren't shown by default, but
  1553.          if the user adds them to the list, display them clickably.
  1554.        */
  1555.       else if (!strcasecomp(name, HEADER_MESSAGE_ID) ||
  1556.                !strcasecomp(name, HEADER_RESENT_MESSAGE_ID))
  1557.         {
  1558.           status = MimeHeaders_write_id_header (hdrs, name, TRUE, opt);
  1559.         }
  1560.  
  1561.       /* The From header supercedes the Sender header.
  1562.        */
  1563.       else if (!strcasecomp(name, HEADER_SENDER) ||
  1564.                !strcasecomp(name, HEADER_FROM))
  1565.         {
  1566.           if (did_from) continue;
  1567.           did_from = TRUE;
  1568.           status = MimeHeaders_write_address_header (hdrs, HEADER_FROM, opt);
  1569.           if (status == 0)
  1570.             status = MimeHeaders_write_address_header (hdrs, HEADER_SENDER,
  1571.                                                        opt);
  1572.         }
  1573.  
  1574.       /* Likewise, the Resent-From header supercedes the Resent-Sender header.
  1575.        */
  1576.       else if (!strcasecomp(name, HEADER_RESENT_SENDER) ||
  1577.                !strcasecomp(name, HEADER_RESENT_FROM))
  1578.         {
  1579.           if (did_resent_from) continue;
  1580.           did_resent_from = TRUE;
  1581.           status = MimeHeaders_write_address_header (hdrs, HEADER_RESENT_FROM,
  1582.                                                      opt);
  1583.           if (status == 0)
  1584.             status = MimeHeaders_write_address_header (hdrs,
  1585.                                                        HEADER_RESENT_SENDER,
  1586.                                                        opt);
  1587.         }
  1588.  
  1589.       /* Emit the Reply-To header only if it differs from the From header.
  1590.          (we just compare the `address' part.)
  1591.        */
  1592.       else if (!strcasecomp(name, HEADER_REPLY_TO))
  1593.         {
  1594.           char *reply_to = MimeHeaders_get (hdrs, HEADER_REPLY_TO,
  1595.                                             FALSE, FALSE);
  1596.           if (reply_to)
  1597.             {
  1598.               char *from = MimeHeaders_get (hdrs, HEADER_FROM, FALSE, FALSE);
  1599.               char *froma = (from
  1600.                              ? MSG_ExtractRFC822AddressMailboxes(from)
  1601.                              : 0);
  1602.               char *repa  = ((reply_to && froma)
  1603.                              ? MSG_ExtractRFC822AddressMailboxes(reply_to)
  1604.                              : 0);
  1605.  
  1606.               FREEIF(reply_to);
  1607.               if (!froma || !repa || strcasecomp (froma, repa))
  1608.                 {
  1609.                   FREEIF(froma);
  1610.                   FREEIF(repa);
  1611.                   status = MimeHeaders_write_address_header (hdrs,
  1612.                                                              HEADER_REPLY_TO,
  1613.                                                              opt);
  1614.                 }
  1615.               FREEIF(repa);
  1616.               FREEIF(froma);
  1617.               FREEIF(from);
  1618.             }
  1619.           FREEIF(reply_to);
  1620.         }
  1621.  
  1622.       /* Random other address headers.
  1623.          These headers combine all occurences: that is, if there is more than
  1624.          one CC field, all of them will be combined and presented as one.
  1625.        */
  1626.       else if (!strcasecomp(name, HEADER_RESENT_TO) ||
  1627.                !strcasecomp(name, HEADER_RESENT_CC) ||
  1628.                !strcasecomp(name, HEADER_TO) ||
  1629.                !strcasecomp(name, HEADER_CC) ||
  1630.                !strcasecomp(name, HEADER_BCC))
  1631.         {
  1632.           status = MimeHeaders_write_address_header (hdrs, name, opt);
  1633.         }
  1634.  
  1635.       /* Random other newsgroup headers.
  1636.          These headers combine all occurences, as with address headers.
  1637.        */
  1638.       else if (!strcasecomp(name, HEADER_NEWSGROUPS) ||
  1639.                !strcasecomp(name, HEADER_FOLLOWUP_TO))
  1640.         {
  1641.           status = MimeHeaders_write_news_header (hdrs, name, opt);
  1642.         }
  1643.  
  1644.       /* Everything else.
  1645.          These headers don't combine: only the first instance of the header
  1646.          will be shown, if there is more than one.
  1647.        */
  1648.       else
  1649.         {
  1650.           status = MimeHeaders_write_random_header (hdrs, name, FALSE, opt);
  1651.         }
  1652.  
  1653.       if (status < 0) break;
  1654.       wrote_any_p |= (status > 0);
  1655.     }
  1656.  
  1657.   return (status < 0 ? status : (wrote_any_p ? 1 : 0));
  1658. }
  1659.  
  1660.  
  1661. static int
  1662. MimeHeaders_write_all_headers (MimeHeaders *hdrs, MimeDisplayOptions *opt)
  1663. {
  1664.   int status;
  1665.   int i;
  1666.   XP_Bool wrote_any_p = FALSE;
  1667.  
  1668.   /* One shouldn't be trying to read headers when one hasn't finished
  1669.      parsing them yet... but this can happen if the message ended
  1670.      prematurely, and has no body at all (as opposed to a null body,
  1671.      which is more normal.)   So, if we try to read from the headers,
  1672.      let's assume that the headers are now finished.  If they aren't
  1673.      in fact finished, then a later attempt to write to them will assert.
  1674.    */
  1675.   if (!hdrs->done_p)
  1676.     {
  1677.       hdrs->done_p = TRUE;
  1678.       status = MimeHeaders_build_heads_list(hdrs);
  1679.       if (status < 0) return 0;
  1680.     }
  1681.  
  1682.   for (i = 0; i < hdrs->heads_size; i++)
  1683.     {
  1684.       char *head = hdrs->heads[i];
  1685.       char *end = (i == hdrs->heads_size-1
  1686.                    ? hdrs->all_headers + hdrs->all_headers_fp
  1687.                    : hdrs->heads[i+1]);
  1688.       char *colon, *ocolon;
  1689.       char *contents;
  1690.       char *name = 0;
  1691.       char *c2 = 0;
  1692.  
  1693.       /* Hack for BSD Mailbox delimiter. */
  1694.       if (i == 0 && head[0] == 'F' && !XP_STRNCMP(head, "From ", 5))
  1695.         {
  1696.           colon = head + 4;
  1697.           contents = colon + 1;
  1698.         }
  1699.       else
  1700.         {
  1701.           /* Find the colon. */
  1702.           for (colon = head; colon < end; colon++)
  1703.             if (*colon == ':') break;
  1704.  
  1705.           if (colon >= end) continue;   /* junk */
  1706.  
  1707.           /* Back up over whitespace before the colon. */
  1708.           ocolon = colon;
  1709.           for (; colon > head && XP_IS_SPACE(colon[-1]); colon--)
  1710.             ;
  1711.  
  1712.           contents = ocolon + 1;
  1713.         }
  1714.  
  1715.       /* Skip over whitespace after colon. */
  1716.       while (contents <= end && XP_IS_SPACE(*contents))
  1717.         contents++;
  1718.  
  1719.       /* Take off trailing whitespace... */
  1720.       while (end > contents && XP_IS_SPACE(end[-1]))
  1721.         end--;
  1722.  
  1723.       name = XP_ALLOC(colon - head + 1);
  1724.       if (!name) return MK_OUT_OF_MEMORY;
  1725.       XP_MEMCPY(name, head, colon - head);
  1726.       name[colon - head] = 0;
  1727.  
  1728.       c2 = XP_ALLOC(end - contents + 1);
  1729.       if (!c2)
  1730.         {
  1731.           XP_FREE(name);
  1732.           return MK_OUT_OF_MEMORY;
  1733.         }
  1734.       XP_MEMCPY(c2, contents, end - contents);
  1735.       c2[end - contents] = 0;
  1736.  
  1737.       if (!strcasecomp(name, HEADER_CC) ||
  1738.           !strcasecomp(name, HEADER_FROM) ||
  1739.           !strcasecomp(name, HEADER_REPLY_TO) ||
  1740.           !strcasecomp(name, HEADER_RESENT_CC) ||
  1741.           !strcasecomp(name, HEADER_RESENT_FROM) ||
  1742.           !strcasecomp(name, HEADER_RESENT_SENDER) ||
  1743.           !strcasecomp(name, HEADER_RESENT_TO) ||
  1744.           !strcasecomp(name, HEADER_SENDER) ||
  1745.           !strcasecomp(name, HEADER_TO))
  1746.         status = MimeHeaders_write_address_header_1(hdrs, name, c2, opt);
  1747.       else if (!strcasecomp(name, HEADER_FOLLOWUP_TO) ||
  1748.                !strcasecomp(name, HEADER_NEWSGROUPS))
  1749.         status = MimeHeaders_write_news_header_1(hdrs, name, c2, opt);
  1750.       else if (!strcasecomp(name, HEADER_MESSAGE_ID) ||
  1751.                !strcasecomp(name, HEADER_RESENT_MESSAGE_ID) ||
  1752.                !strcasecomp(name, HEADER_REFERENCES))
  1753.         status = MimeHeaders_write_id_header_1(hdrs, name, c2, TRUE, opt);
  1754.       else if (!strcasecomp(name, HEADER_SUBJECT))
  1755.         status = MimeHeaders_write_subject_header_1(hdrs, name, c2, opt);
  1756.  
  1757.       else
  1758.         status = MimeHeaders_write_random_header_1(hdrs, name, c2, opt, FALSE);
  1759.       XP_FREE(name);
  1760.       XP_FREE(c2);
  1761.  
  1762.       if (status < 0) return status;
  1763.       if (!wrote_any_p) wrote_any_p = (status > 0);
  1764.     }
  1765.  
  1766.   return (wrote_any_p ? 1 : 0);
  1767. }
  1768.  
  1769.  
  1770. #ifndef MOZILLA_30
  1771. # define EGREGIOUS_HEADERS
  1772. #endif
  1773.  
  1774. static int
  1775. MimeHeaders_write_microscopic_headers (MimeHeaders *hdrs,
  1776.                                        MimeDisplayOptions *opt)
  1777. {
  1778.   int status = 1;
  1779.   char *subj  = MimeHeaders_get (hdrs, HEADER_SUBJECT, FALSE, FALSE);
  1780.   char *from  = MimeHeaders_get (hdrs, HEADER_FROM, FALSE, TRUE);
  1781.   char *date  = MimeHeaders_get (hdrs, HEADER_DATE, FALSE, TRUE);
  1782.   char *out;
  1783.  
  1784.   if (!from)
  1785.     from = MimeHeaders_get (hdrs, HEADER_SENDER, FALSE, TRUE);
  1786.   if (!date)
  1787.     date = MimeHeaders_get (hdrs, HEADER_RESENT_DATE, FALSE, TRUE);
  1788.  
  1789.   if (date && opt->reformat_date_fn)
  1790.     {
  1791.       char *d2 = opt->reformat_date_fn(date, opt->stream_closure);
  1792.       if (d2)
  1793.         {
  1794.           XP_FREE(date);
  1795.           date = d2;
  1796.         }
  1797.     }
  1798.  
  1799.  
  1800.   /* Convert MIME-2 data.
  1801.    */
  1802. #define FROB(VAR) do {                                                        \
  1803.   if (VAR)                                                                    \
  1804.     {                                                                        \
  1805.       char *converted = 0;                                                    \
  1806.       int32 converted_length = 0;                                            \
  1807.       status = MimeHeaders_convert_rfc1522(opt, VAR, XP_STRLEN(VAR),        \
  1808.                                            &converted, &converted_length);    \
  1809.       if (status < 0) goto FAIL;                                            \
  1810.       if (converted)                                                        \
  1811.         {                                                                    \
  1812.           XP_ASSERT(converted_length == (int32) XP_STRLEN(converted));        \
  1813.           VAR = converted;                                                    \
  1814.         }                                                                    \
  1815.     } } while(0)
  1816.   FROB(from);
  1817.   FROB(subj);
  1818.   FROB(date);
  1819. #undef FROB
  1820.  
  1821. #ifndef EGREGIOUS_HEADERS
  1822. # define THLHMAAMS 0
  1823. #else
  1824. # define THLHMAAMS 900
  1825. #endif
  1826.  
  1827.   status = MimeHeaders_grow_obuffer (hdrs,
  1828.                                      (subj ? XP_STRLEN(subj)*2 : 0) +
  1829.                                      (from ? XP_STRLEN(from)   : 0) +
  1830.                                      (date ? XP_STRLEN(date)   : 0) +
  1831.                                      100
  1832.                                  + THLHMAAMS
  1833.                                      );
  1834.   if (status < 0) goto FAIL;
  1835.  
  1836.   if (subj) {
  1837.     XP_ASSERT(!hdrs->munged_subject);
  1838.     hdrs->munged_subject = XP_STRDUP(subj);
  1839.   }
  1840.  
  1841.   out = hdrs->obuffer;
  1842.  
  1843. #ifndef EGREGIOUS_HEADERS
  1844.   XP_STRCPY(out, "<B>");
  1845.   out += XP_STRLEN(out);
  1846.   /* Quotify the subject... */
  1847.   if (subj)
  1848.     status = NET_ScanForURLs (
  1849. # ifndef MOZILLA_30
  1850.                               NULL,
  1851. # endif /* !MOZILLA_30 */
  1852.                               subj, XP_STRLEN(subj), out,
  1853.                               hdrs->obuffer_size - (out - hdrs->obuffer) - 10,
  1854.                               TRUE);
  1855.     if (status < 0) goto FAIL;
  1856.  
  1857.   out += XP_STRLEN(out);
  1858.   XP_STRCPY(out, "</B> (");
  1859.   out += XP_STRLEN(out);
  1860.  
  1861.   /* Quotify the sender... */
  1862.   if (from)
  1863.     status = NET_ScanForURLs (
  1864. # ifndef MOZILLA_30
  1865.                               NULL,
  1866. # endif /* !MOZILLA_30 */
  1867.                               from, XP_STRLEN(from), out,
  1868.                               hdrs->obuffer_size - (out - hdrs->obuffer) - 10,
  1869.                               TRUE);
  1870.   if (status < 0) goto FAIL;
  1871.  
  1872.   out += XP_STRLEN(out);
  1873.   XP_STRCPY(out, ", <NOBR>");
  1874.   out += XP_STRLEN(out);
  1875.  
  1876.   /* Quotify the date (just in case...) */
  1877.   if (date)
  1878.     status = NET_ScanForURLs (
  1879. # ifndef MOZILLA_30
  1880.                               NULL,
  1881. # endif /* !MOZILLA_30 */
  1882.                               date, XP_STRLEN(date), out,
  1883.                               hdrs->obuffer_size - (out - hdrs->obuffer) - 10,
  1884.                               TRUE);
  1885.   if (status < 0) goto FAIL;
  1886.  
  1887.   out += XP_STRLEN(out);
  1888.   XP_STRCPY(out, ")</NOBR><BR>");
  1889.   out += XP_STRLEN(out);
  1890.  
  1891.   status = MimeHeaders_write(opt, hdrs->obuffer, XP_STRLEN(hdrs->obuffer));
  1892.   if (status < 0) goto FAIL;
  1893.  
  1894. #else  /* EGREGIOUS_HEADERS */
  1895.   
  1896.   XP_SPRINTF(out, "\
  1897. <TR><TD VALIGN=TOP BGCOLOR=\"#CCCCCC\" ALIGN=RIGHT><B>%s: </B></TD>\
  1898. <TD VALIGN=TOP BGCOLOR=\"#CCCCCC\" width=100%%>\
  1899. <table border=0 cellspacing=0 cellpadding=0 width=100%%><tr>\
  1900. <td valign=top>", XP_GetString(MK_MIMEHTML_DISP_FROM));
  1901.   out += XP_STRLEN(out);
  1902.  
  1903.   if (from) {
  1904.     status = MimeHeaders_write(opt, hdrs->obuffer, XP_STRLEN(hdrs->obuffer));
  1905.     if (status < 0) goto FAIL;
  1906.     status = MimeHeaders_write_grouped_header_1(hdrs, NULL, from, opt, TRUE);
  1907.     if (status < 0) goto FAIL;
  1908.     out = hdrs->obuffer;
  1909.   }
  1910.  
  1911.   XP_STRCPY(out, "</td><td valign=top align=right nowrap>");
  1912.   out += XP_STRLEN(out);
  1913.   if (date) XP_STRCPY(out, date);
  1914.   out += XP_STRLEN(out);
  1915.   XP_STRCPY(out, "</td></tr></table></TD>");
  1916.   out += XP_STRLEN(out);
  1917.  
  1918.   /* ### This is where to insert something like <TD VALIGN=TOP align=right bgcolor=\"#CCCCCC\" ROWSPAN=2><IMG SRC=\"http://gooey/projects/dogbert/mail/M_both.gif\"></TD> if we want to do an image. */
  1919.   
  1920.   XP_STRCPY(out,
  1921.             "</TR><TR><TD VALIGN=TOP BGCOLOR=\"#CCCCCC\" ALIGN=RIGHT><B>");
  1922.   out += XP_STRLEN(out);
  1923.   XP_STRCPY(out, XP_GetString(MK_MIMEHTML_DISP_SUBJECT));
  1924.   out += XP_STRLEN(out);
  1925.   XP_STRCPY(out, ": </B></TD><TD VALIGN=TOP BGCOLOR=\"#CCCCCC\">");
  1926.   out += XP_STRLEN(out);
  1927.   if (subj) {
  1928.       status = NET_ScanForURLs(
  1929. #ifndef MOZILLA_30
  1930.                                NULL,
  1931. #endif /* !MOZILLA_30 */
  1932.                                subj, XP_STRLEN(subj), out,
  1933.                                hdrs->obuffer_size - (out - hdrs->obuffer) - 10,
  1934.                                TRUE);
  1935.       if (status < 0) goto FAIL;
  1936.   } else {
  1937.       XP_STRCPY(out, "<BR>");
  1938.   }
  1939.   out += XP_STRLEN(out);
  1940.   XP_STRCPY(out, "</TR>");
  1941.   out += XP_STRLEN(out);
  1942.  
  1943.   status = MimeHeaders_write(opt, hdrs->obuffer, XP_STRLEN(hdrs->obuffer));
  1944.   if (status < 0) goto FAIL;
  1945.  
  1946. #endif /* EGREGIOUS_HEADERS */
  1947.  
  1948.  
  1949.   /* At this point, we've written out the one-line summary.
  1950.      But we might want to write out some additional header lines, too...
  1951.    */
  1952.  
  1953.  
  1954.   /* If this was a redirected message, also show the Resent-From or
  1955.      Resent-Sender field, to avoid nasty surprises.
  1956.    */
  1957.   status = MimeHeaders_write_address_header(hdrs, HEADER_RESENT_FROM, opt);
  1958.   if (status < 0) goto FAIL;
  1959.   if (status == 0)   /* meaning "nothing written" */
  1960.     status = MimeHeaders_write_address_header(hdrs, HEADER_RESENT_SENDER, opt);
  1961.   if (status < 0) goto FAIL;
  1962.  
  1963.  
  1964.   /* If this is the mail reader, show the full recipient list.
  1965.    */
  1966.   if (opt->headers == MimeHeadersMicroPlus)
  1967.     {
  1968.       status = MimeHeaders_write_address_header (hdrs, HEADER_TO, opt);
  1969.       if (status < 0) goto FAIL;
  1970.       status = MimeHeaders_write_address_header (hdrs, HEADER_CC, opt);
  1971.       if (status < 0) goto FAIL;
  1972.       status = MimeHeaders_write_news_header (hdrs, HEADER_NEWSGROUPS, opt);
  1973.       if (status < 0) goto FAIL;
  1974.     }
  1975.  
  1976.  FAIL:
  1977.  
  1978.   FREEIF(subj);
  1979.   FREEIF(from);
  1980.   FREEIF(date);
  1981.  
  1982.   return (status < 0 ? status : 1);
  1983. }
  1984.  
  1985.  
  1986. static int
  1987. MimeHeaders_write_citation_headers (MimeHeaders *hdrs, MimeDisplayOptions *opt)
  1988. {
  1989.   int status;
  1990.   char *from = 0, *name = 0, *id = 0;
  1991.   const char *fmt = 0;
  1992.   char *converted = 0;
  1993.   int32 converted_length = 0;
  1994.  
  1995.   if (!opt || !opt->output_fn)
  1996.     return 0;
  1997.  
  1998.   from = MimeHeaders_get(hdrs, HEADER_FROM, FALSE, FALSE);
  1999.   if (!from)
  2000.     from = MimeHeaders_get(hdrs, HEADER_SENDER, FALSE, FALSE);
  2001.   if (!from)
  2002.     from = XP_STRDUP("Unknown");
  2003.   if (!from)
  2004.     {
  2005.       status = MK_OUT_OF_MEMORY;
  2006.       goto FAIL;
  2007.     }
  2008.  
  2009. #if 0
  2010.   id = MimeHeaders_get(hdrs, HEADER_MESSAGE_ID, FALSE, FALSE);
  2011. #endif
  2012.  
  2013.   name = MSG_ExtractRFC822AddressNames (from);
  2014.   if (!name)
  2015.     {
  2016.       name = from;
  2017.       from = 0;
  2018.     }
  2019.   FREEIF(from);
  2020.  
  2021.   fmt = (id
  2022.          ? XP_GetString(MK_MSG_IN_MSG_X_USER_WROTE)
  2023.          : XP_GetString(MK_MSG_USER_WROTE));
  2024.  
  2025.   status = MimeHeaders_grow_obuffer (hdrs,
  2026.                                      XP_STRLEN(fmt) + XP_STRLEN(name) +
  2027.                                      (id ? XP_STRLEN(id) : 0) + 58);
  2028.   if (status < 0) return status;
  2029.  
  2030. #ifndef MOZILLA_30
  2031.   if (opt->nice_html_only_p) {
  2032.     int32 nReplyWithExtraLines = 0, eReplyOnTop = 0;
  2033.     PREF_GetIntPref("mailnews.reply_with_extra_lines", &nReplyWithExtraLines);
  2034.     PREF_GetIntPref("mailnews.reply_on_top", &eReplyOnTop);
  2035.     if (nReplyWithExtraLines && eReplyOnTop == 1) {
  2036.       for (; nReplyWithExtraLines > 0; nReplyWithExtraLines--) {
  2037.         status = MimeHeaders_write(opt, "<BR>", 4);
  2038.         if (status < 0) return status;
  2039.       }
  2040.     }
  2041.   }
  2042. #endif
  2043.  
  2044.   if (id)
  2045.     XP_SPRINTF(hdrs->obuffer, fmt, id, name);
  2046.   else
  2047.     XP_SPRINTF(hdrs->obuffer, fmt, name);
  2048.  
  2049.   status = MimeHeaders_convert_rfc1522(opt, hdrs->obuffer,
  2050.                                        XP_STRLEN(hdrs->obuffer),
  2051.                                        &converted, &converted_length);
  2052.   if (status < 0) return status;
  2053.  
  2054.   if (converted)
  2055.     {
  2056.       status = MimeHeaders_write(opt, converted, converted_length);
  2057.       if (status < 0) goto FAIL;
  2058.     }
  2059.   else
  2060.     {
  2061.       status = MimeHeaders_write(opt, hdrs->obuffer, XP_STRLEN(hdrs->obuffer));
  2062.       if (status < 0) goto FAIL;
  2063.     }
  2064.  
  2065. #ifndef MOZILLA_30
  2066.   if (opt->nice_html_only_p) {
  2067.       char ptr[] = "<BLOCKQUOTE TYPE=CITE>";
  2068.       MimeHeaders_write(opt, ptr, XP_STRLEN(ptr));
  2069.   }
  2070. #endif /* !MOZILLA_30 */
  2071.  
  2072.  FAIL:
  2073.  
  2074.   FREEIF(from);
  2075.   FREEIF(name);
  2076.   FREEIF(id);
  2077.   return (status < 0 ? status : 1);
  2078. }
  2079.  
  2080.  
  2081. /* Writes the headers as text/plain.
  2082.    This writes out a blank line after the headers, unless
  2083.    dont_write_content_type is true, in which case the header-block
  2084.    is not closed off, and none of the Content- headers are written.
  2085.  */
  2086. int
  2087. MimeHeaders_write_raw_headers (MimeHeaders *hdrs, MimeDisplayOptions *opt,
  2088.                                XP_Bool dont_write_content_type)
  2089. {
  2090.   int status;
  2091.  
  2092.   if (hdrs && !hdrs->done_p)
  2093.     {
  2094.       hdrs->done_p = TRUE;
  2095.       status = MimeHeaders_build_heads_list(hdrs);
  2096.       if (status < 0) return 0;
  2097.     }
  2098.  
  2099.   if (!dont_write_content_type)
  2100.     {
  2101.       char nl[] = LINEBREAK;
  2102.       if (hdrs)
  2103.         {
  2104.           status = MimeHeaders_write(opt, hdrs->all_headers,
  2105.                                      hdrs->all_headers_fp);
  2106.           if (status < 0) return status;
  2107.         }
  2108.       status = MimeHeaders_write(opt, nl, XP_STRLEN(nl));
  2109.       if (status < 0) return status;
  2110.     }
  2111.   else if (hdrs)
  2112.     {
  2113.       int32 i;
  2114.       for (i = 0; i < hdrs->heads_size; i++)
  2115.         {
  2116.           char *head = hdrs->heads[i];
  2117.           char *end = (i == hdrs->heads_size-1
  2118.                        ? hdrs->all_headers + hdrs->all_headers_fp
  2119.                        : hdrs->heads[i+1]);
  2120.  
  2121.           XP_ASSERT(head);
  2122.           if (!head) continue;
  2123.  
  2124.           /* Don't write out any Content- header. */
  2125.           if (!strncasecomp(head, "Content-", 8))
  2126.             continue;
  2127.  
  2128.           /* Write out this (possibly multi-line) header. */
  2129.           status = MimeHeaders_write(opt, head, end - head);
  2130.           if (status < 0) return status;
  2131.         }
  2132.     }
  2133.  
  2134.   if (hdrs)
  2135.     MimeHeaders_compact (hdrs);
  2136.  
  2137.   return 0;
  2138. }
  2139.  
  2140.  
  2141. int
  2142. MimeHeaders_write_headers_html (MimeHeaders *hdrs, MimeDisplayOptions *opt)
  2143. {
  2144.   int status = 0;
  2145.   XP_Bool wrote_any_p = FALSE;
  2146.  
  2147.   if (!opt || !opt->output_fn) return 0;
  2148.  
  2149.   FREEIF(hdrs->munged_subject);
  2150.  
  2151.   status = MimeHeaders_grow_obuffer (hdrs, 210);
  2152.   if (status < 0) return status;
  2153.  
  2154.   if (opt->fancy_headers_p) {
  2155. #ifdef JS_ATTACHMENT_MUMBO_JUMBO
  2156.     /* First, insert a table for the way-magic javascript appearing attachment
  2157.        indicator.  The ending code for this is way below. */
  2158.     XP_STRCPY(hdrs->obuffer, "<TABLE><TR><TD>" MIME_HEADER_TABLE);
  2159. #else
  2160.     XP_STRCPY(hdrs->obuffer, MIME_HEADER_TABLE);
  2161. #endif
  2162.   } else
  2163.     XP_STRCPY (hdrs->obuffer, "<P>");
  2164.  
  2165.   status = MimeHeaders_write(opt, hdrs->obuffer, XP_STRLEN(hdrs->obuffer));
  2166.   if (status < 0) return status;
  2167.  
  2168.   if (opt->headers == MimeHeadersAll)
  2169.     status = MimeHeaders_write_all_headers (hdrs, opt);
  2170.   else if (opt->headers == MimeHeadersMicro ||
  2171.            opt->headers == MimeHeadersMicroPlus)
  2172.     status = MimeHeaders_write_microscopic_headers (hdrs, opt);
  2173.   else if (opt->headers == MimeHeadersCitation)
  2174.     status = MimeHeaders_write_citation_headers (hdrs, opt);
  2175.   else
  2176.     status = MimeHeaders_write_interesting_headers (hdrs, opt);
  2177.  
  2178.   wrote_any_p = (status > 0);
  2179.  
  2180.   if (!wrote_any_p && opt->fancy_headers_p)
  2181.     {
  2182.       const char *msg = XP_GetString(MK_MSG_NO_HEADERS);
  2183.       XP_STRCPY (hdrs->obuffer, "<TR><TD><B><I>");
  2184.       XP_STRCAT (hdrs->obuffer, msg);
  2185.       XP_STRCAT (hdrs->obuffer, "</I></B></TD></TR>");
  2186.       status = MimeHeaders_write(opt, hdrs->obuffer, XP_STRLEN(hdrs->obuffer));
  2187.       if (status < 0) goto FAIL;
  2188.     }
  2189.  
  2190.   if (opt->fancy_headers_p) {
  2191.     XP_STRCPY (hdrs->obuffer, "</TABLE>");
  2192. #ifdef JS_ATTACHMENT_MUMBO_JUMBO
  2193.     /* OK, here's the ending code for the way magic javascript indicator. */
  2194.     if (!opt->nice_html_only_p && opt->fancy_links_p) {
  2195.         if (opt->attachment_icon_layer_id == 0) {
  2196.             static int32 randomid = 1; /* Not very random. ### */
  2197.             opt->attachment_icon_layer_id = randomid;
  2198.             XP_SPRINTF(hdrs->obuffer + XP_STRLEN(hdrs->obuffer),
  2199.                        "</TD><TD VALIGN=TOP><LAYER LOCKED name=noattach-%ld>"
  2200.                        "</LAYER><ILAYER LOCKED name=attach-%ld visibility=hide>"
  2201.                        "<a href=mailbox:displayattachments>"
  2202.                        "<IMG SRC=internal-attachment-icon BORDER=0>"
  2203.                        "</a></ilayer>",
  2204.                        (long) randomid, (long) randomid);
  2205.             randomid++;
  2206.         }
  2207.     }
  2208.     XP_STRCAT(hdrs->obuffer, "</td></tr></table>");
  2209. #endif
  2210.   } else
  2211.     XP_STRCPY (hdrs->obuffer, "<P>");
  2212.  
  2213.   status = MimeHeaders_write(opt, hdrs->obuffer, XP_STRLEN(hdrs->obuffer));
  2214.   if (status < 0) goto FAIL;
  2215.   if (hdrs->munged_subject) {
  2216.     char* t2 = NET_EscapeHTML(hdrs->munged_subject);
  2217.     FREEIF(hdrs->munged_subject);
  2218.     if (t2) {
  2219.       status = MimeHeaders_grow_obuffer(hdrs, XP_STRLEN(t2) + 20);
  2220.       if (status >= 0) {
  2221.         XP_SPRINTF(hdrs->obuffer, "<TITLE>%s</TITLE>\n", t2);
  2222.         status = MimeHeaders_write(opt, hdrs->obuffer,
  2223.                                    XP_STRLEN(hdrs->obuffer));
  2224.       }
  2225.     }
  2226.     FREEIF(t2);
  2227.     if (status < 0) goto FAIL;
  2228.   }
  2229.  
  2230.  
  2231.  
  2232.  FAIL:
  2233.   MimeHeaders_compact (hdrs);
  2234.  
  2235.   return status;
  2236. }
  2237.  
  2238.  
  2239.  
  2240. /* For drawing the tables that represent objects that can't be displayed
  2241.    inline.
  2242.  */
  2243.  
  2244. int
  2245. MimeHeaders_write_attachment_box(MimeHeaders *hdrs,
  2246.                                  MimeDisplayOptions *opt,
  2247.                                  const char *content_type,
  2248.                                  const char *encoding,
  2249.                                  const char *lname,
  2250.                                  const char *lname_url,
  2251.                                  const char *body)
  2252. {
  2253.   int status = 0;
  2254.   char *type = 0, *desc = 0, *enc = 0, *icon = 0, *type_desc = 0;
  2255.  
  2256.   type = (content_type
  2257.           ? XP_STRDUP(content_type)
  2258.           : MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, TRUE, FALSE));
  2259.  
  2260.   if (type && *type && opt)
  2261.     {
  2262.       if (opt->type_icon_name_fn)
  2263.         icon = opt->type_icon_name_fn(type, opt->stream_closure);
  2264.  
  2265.       if (!strcasecomp(type, APPLICATION_OCTET_STREAM))
  2266.         type_desc = XP_STRDUP(XP_GetString(MK_MSG_UNSPECIFIED_TYPE));
  2267.       else if (opt->type_description_fn)
  2268.         type_desc = opt->type_description_fn(type, opt->stream_closure);
  2269.     }
  2270.  
  2271.   /* Kludge to use a prettier name for AppleDouble and AppleSingle objects.
  2272.    */
  2273.   if (type && (!strcasecomp(type, MULTIPART_APPLEDOUBLE) ||
  2274.                !strcasecomp(type, MULTIPART_HEADER_SET) ||
  2275.                !strcasecomp(type, APPLICATION_APPLEFILE)))
  2276.     {
  2277.       XP_FREE(type);
  2278.       type = XP_STRDUP(XP_GetString(MK_MSG_MIME_MAC_FILE));
  2279.       FREEIF(icon);
  2280.       icon = XP_STRDUP("internal-gopher-binary");
  2281.     }
  2282.  
  2283. # define PUT_STRING(S) do { \
  2284.          status = MimeHeaders_grow_obuffer(hdrs, XP_STRLEN(S)+1); \
  2285.          if (status < 0) goto FAIL; \
  2286.          XP_STRCPY (hdrs->obuffer, S); \
  2287.          status = MimeHeaders_write(opt, hdrs->obuffer, \
  2288.                                     XP_STRLEN(hdrs->obuffer)); \
  2289.          if (status < 0) goto FAIL; } while (0)
  2290.  
  2291.   PUT_STRING ("<TABLE CELLPADDING=8 CELLSPACING=1 BORDER=1>"
  2292.               "<TR><TD NOWRAP>");
  2293.  
  2294.   if (icon)
  2295.     {
  2296.       PUT_STRING("<TABLE BORDER=0 CELLPADDING=0 CELLSPACING=0>"
  2297.                  "<TR><TD NOWRAP VALIGN=CENTER>");
  2298.  
  2299.       if (lname_url)
  2300.         {
  2301.           PUT_STRING("<A HREF=\"");
  2302.           PUT_STRING(lname_url);
  2303.           PUT_STRING("\">");
  2304.         }
  2305.       PUT_STRING("<IMG SRC=\"");
  2306.       PUT_STRING(icon);
  2307.       PUT_STRING("\" BORDER=0 ALIGN=MIDDLE ALT=\"\">");
  2308.  
  2309.       if (lname_url)
  2310.         PUT_STRING("</A>");
  2311.  
  2312.       PUT_STRING("</TD><TD VALIGN=CENTER>");
  2313.     }
  2314.  
  2315.   if (lname_url)
  2316.     {
  2317.       PUT_STRING("<A HREF=\"");
  2318.       PUT_STRING(lname_url);
  2319.       PUT_STRING("\">");
  2320.     }
  2321.   if (lname)
  2322.     PUT_STRING (lname);
  2323.   if (lname_url)
  2324.     PUT_STRING("</A>");
  2325.  
  2326.   if (icon)
  2327.     PUT_STRING("</TD></TR></TABLE>");
  2328.  
  2329.   PUT_STRING ("</TD><TD>");
  2330.  
  2331.   if (opt->headers == MimeHeadersAll)
  2332.     {
  2333.       status = MimeHeaders_write_headers_html (hdrs, opt);
  2334.       if (status < 0) return status;
  2335.     }
  2336.   else
  2337.     {
  2338.       char *name = MimeHeaders_get_name(hdrs);
  2339.       PUT_STRING (MIME_HEADER_TABLE);
  2340.  
  2341.       if (name)
  2342.         {
  2343.           const char *name_hdr = MimeHeaders_localize_header_name("Name", opt);
  2344.           PUT_STRING(HEADER_START_JUNK);
  2345.           PUT_STRING(name_hdr);
  2346.           PUT_STRING(HEADER_MIDDLE_JUNK);
  2347.           PUT_STRING(name);
  2348.           FREEIF(name);
  2349.           PUT_STRING(HEADER_END_JUNK);
  2350.         }
  2351.  
  2352.       if (type)
  2353.         {
  2354.           const char *type_hdr = MimeHeaders_localize_header_name("Type", opt);
  2355.           PUT_STRING(HEADER_START_JUNK);
  2356.           PUT_STRING(type_hdr);
  2357.           PUT_STRING(HEADER_MIDDLE_JUNK);
  2358.           if (type_desc)
  2359.             {
  2360.               PUT_STRING(type_desc);
  2361.               PUT_STRING(" (");
  2362.             }
  2363.           PUT_STRING(type);
  2364.           if (type_desc)
  2365.             PUT_STRING(")");
  2366.           FREEIF(type);
  2367.           FREEIF(type_desc);
  2368.           PUT_STRING(HEADER_END_JUNK);
  2369.         }
  2370.  
  2371.       enc = (encoding
  2372.              ? XP_STRDUP(encoding)
  2373.              : MimeHeaders_get(hdrs, HEADER_CONTENT_TRANSFER_ENCODING,
  2374.                                TRUE, FALSE));
  2375.       if (enc)
  2376.         {
  2377.           const char *enc_hdr = MimeHeaders_localize_header_name("Encoding",
  2378.                                                                  opt);
  2379.           PUT_STRING(HEADER_START_JUNK);
  2380.           PUT_STRING(enc_hdr);
  2381.           PUT_STRING(HEADER_MIDDLE_JUNK);
  2382.           PUT_STRING(enc);
  2383.           FREEIF(enc);
  2384.           PUT_STRING(HEADER_END_JUNK);
  2385.         }
  2386.  
  2387.       desc = MimeHeaders_get(hdrs, HEADER_CONTENT_DESCRIPTION, FALSE, FALSE);
  2388.       if (!desc)
  2389.         {
  2390.           desc = MimeHeaders_get(hdrs, HEADER_X_SUN_DATA_DESCRIPTION,
  2391.                                  FALSE, FALSE);
  2392.  
  2393.           /* If there's an X-Sun-Data-Description, but it's the same as the
  2394.              X-Sun-Data-Type, don't show it.
  2395.            */
  2396.           if (desc)
  2397.             {
  2398.               char *loser = MimeHeaders_get(hdrs, HEADER_X_SUN_DATA_TYPE,
  2399.                                             FALSE, FALSE);
  2400.               if (loser && !strcasecomp(loser, desc))
  2401.                 FREEIF(desc);
  2402.               FREEIF(loser);
  2403.             }
  2404.         }
  2405.  
  2406.       if (desc)
  2407.         {
  2408.           const char *desc_hdr= MimeHeaders_localize_header_name("Description",
  2409.                                                                  opt);
  2410.           PUT_STRING(HEADER_START_JUNK);
  2411.           PUT_STRING(desc_hdr);
  2412.           PUT_STRING(HEADER_MIDDLE_JUNK);
  2413.           PUT_STRING(desc);
  2414.           FREEIF(desc);
  2415.           PUT_STRING(HEADER_END_JUNK);
  2416.         }
  2417.       PUT_STRING ("</TABLE>");
  2418.     }
  2419.   if (body) PUT_STRING(body);
  2420.   PUT_STRING ("</TD></TR></TABLE></CENTER>");
  2421. # undef PUT_STRING
  2422.  
  2423.  FAIL:
  2424.   FREEIF(type);
  2425.   FREEIF(desc);
  2426.   FREEIF(enc);
  2427.   FREEIF(icon);
  2428.   FREEIF(type_desc);
  2429.   MimeHeaders_compact (hdrs);
  2430.   return status;
  2431. }
  2432.  
  2433.  
  2434. /* Some crypto-related HTML-generated utility routines.
  2435.  */
  2436.  
  2437.  
  2438. char *
  2439. MimeHeaders_open_crypto_stamp(void)
  2440. {
  2441.   return XP_STRDUP("<TABLE CELLPADDING=0 CELLSPACING=0"
  2442.                           " WIDTH=\"100%\" BORDER=0>"
  2443.                    "<TR VALIGN=TOP><TD WIDTH=\"100%\">");
  2444. }
  2445.  
  2446. char *
  2447. MimeHeaders_finish_open_crypto_stamp(void)
  2448. {
  2449.   return XP_STRDUP("</TD><TD ALIGN=RIGHT VALIGN=TOP>");
  2450. }
  2451.  
  2452. char *
  2453. MimeHeaders_close_crypto_stamp(void)
  2454. {
  2455.   return XP_STRDUP("</TD></TR></TABLE><P>");
  2456. }
  2457.  
  2458.  
  2459. char *
  2460. MimeHeaders_make_crypto_stamp(XP_Bool encrypted_p,
  2461.                               XP_Bool signed_p,
  2462.                               XP_Bool good_p,
  2463.                               XP_Bool close_parent_stamp_p,
  2464.                               const char *stamp_url)
  2465. {
  2466.   const char *open = ("%s"
  2467.                       "<P>"
  2468.                       "<CENTER>"
  2469.                         "<TABLE CELLPADDING=3 CELLSPACING=1 BORDER=1>"
  2470.                           "<TR>"
  2471.                             "<TD ALIGN=RIGHT VALIGN=BOTTOM BGCOLOR=\"white\">"
  2472.                               "%s<IMG SRC=\"%s\" BORDER=0 ALT=\"S/MIME\">%s"
  2473.                               "<B>"
  2474.                                 "<FONT COLOR=\"red\" SIZE=\"-1\">"
  2475.                                   "<BR>"
  2476.                       "%s%s%s");
  2477.   int16 middle_key = 0;
  2478.   char *href_open = 0;
  2479.   const char *img_src = "";
  2480.   const char *href_close = 0;
  2481.   const char *middle = 0;
  2482.   const char *close = (         "</FONT>"
  2483.                               "</B>"
  2484.                             "</TD>"
  2485.                           "</TR>"
  2486.                         "</TABLE>"
  2487.                       "</CENTER>"
  2488.                       );
  2489.   char *parent_close = 0;
  2490.   const char *parent_close_early = 0;
  2491.   const char *parent_close_late = 0;
  2492.   char *result = 0;
  2493.  
  2494.   /* Neither encrypted nor signed means "certs only". */
  2495.  
  2496.   if (encrypted_p && signed_p && good_p)                /* 111 */
  2497.     {
  2498.       middle_key = MK_MIMEHTML_ENC_AND_SIGNED;
  2499.       img_src = "internal-smime-encrypted-signed";
  2500.     }
  2501.   else if (!encrypted_p && signed_p && good_p)            /* 011 */
  2502.     {
  2503.       middle_key = MK_MIMEHTML_SIGNED;
  2504.       img_src = "internal-smime-signed";
  2505.     }
  2506.   else if (encrypted_p && !signed_p && good_p)            /* 101 */
  2507.     {
  2508.       middle_key = MK_MIMEHTML_ENCRYPTED;
  2509.       img_src = "internal-smime-encrypted";
  2510.     }
  2511.   else if (!encrypted_p && !signed_p && good_p)            /* 001 */
  2512.     {
  2513.       middle_key = MK_MIMEHTML_CERTIFICATES;
  2514.       img_src = "internal-smime-attached";
  2515.     }
  2516.  
  2517.   else if (encrypted_p && signed_p && !good_p)            /* 110 */
  2518.     {
  2519.       middle_key = MK_MIMEHTML_ENC_SIGNED_BAD;
  2520.       img_src = "internal-smime-encrypted-signed-bad";
  2521.     }
  2522.   else if (!encrypted_p && signed_p && !good_p)            /* 010 */
  2523.     {
  2524.       middle_key = MK_MIMEHTML_SIGNED_BAD;
  2525.       img_src = "internal-smime-signed-bad";
  2526.     }
  2527.   else if (encrypted_p && !signed_p && !good_p)            /* 100 */
  2528.     {
  2529.       middle_key = MK_MIMEHTML_ENCRYPTED_BAD;
  2530.       img_src = "internal-smime-encrypted-bad";
  2531.     }
  2532.   else /* (!encrypted_p && !signed_p && !good_p) */        /* 000 */
  2533.     {
  2534.       middle_key = MK_MIMEHTML_CERTIFICATES_BAD;
  2535.       img_src = "internal-smime-signed-bad";
  2536.     }
  2537.  
  2538.   if (middle_key)
  2539.     {
  2540.       middle = XP_GetString(middle_key);
  2541.  
  2542.       /* #### Don't have access to output_csid from here...
  2543.          middle = XP_GetStringForHTML(middle_key, output_csid, (char*)middle);
  2544.        */
  2545.     }
  2546.  
  2547.   if (close_parent_stamp_p)
  2548.     {
  2549.       parent_close = MimeHeaders_close_crypto_stamp();
  2550.       if (!parent_close) goto FAIL; /* MK_OUT_OF_MEMORY */
  2551.     }
  2552.  
  2553.   parent_close_early = 0;
  2554.   parent_close_late = parent_close;
  2555.   if (!encrypted_p && !signed_p)
  2556.     {
  2557.       /* Kludge for certs-only messages -- close off the parent early
  2558.          so that the stamp goes in the body, not next to the headers. */
  2559.       parent_close_early = parent_close;
  2560.       parent_close_late  = 0;
  2561.     }
  2562.  
  2563.   if (stamp_url)
  2564.     {
  2565.       const char *stamp_text = XP_GetString(MK_MIMEHTML_SHOW_SECURITY_ADVISOR);
  2566.       href_open = PR_smprintf("<A HREF=\"%s\""
  2567.                               " onMouseOver=\"window.status='%s';"
  2568.                               " return true\">",
  2569.                               stamp_url,
  2570.                               stamp_text);
  2571.       href_close = "</A>";
  2572.       if (!href_open) goto FAIL; /* MK_OUT_OF_MEMORY */
  2573.     }
  2574.  
  2575.   result = PR_smprintf(open,
  2576.                        (parent_close_early ? parent_close_early : ""),
  2577.                         (href_open ? href_open : ""),
  2578.                          img_src,
  2579.                         (href_close ? href_close : ""),
  2580.                         (middle ? middle : ""),
  2581.                        close,
  2582.                        (parent_close_late ? parent_close_late : ""));
  2583.  FAIL:
  2584.   FREEIF(parent_close);
  2585.   FREEIF(href_open);
  2586.   return result;
  2587. }
  2588.  
  2589. /* Strip CR+LF+<whitespace> runs within (original).
  2590.    Since the string at (original) can only shrink,
  2591.    this conversion is done in place. (original)
  2592.    is returned. */
  2593. char *
  2594. strip_continuations(char *original)
  2595. {
  2596.     char *p1, *p2;
  2597.  
  2598.     /* If we were given a null string, return it as is */
  2599.     if (!original) return NULL;
  2600.  
  2601.     /* Start source and dest pointers at the beginning */
  2602.     p1 = p2 = original;
  2603.  
  2604.     while(*p2)
  2605.     {
  2606.         /* p2 runs ahead at (CR and/or LF) + <space> */
  2607.         if ((p2[0] == CR) || (p2[0] == LF))
  2608.         {
  2609.             /* move past (CR and/or LF) + whitespace following */    
  2610.             do
  2611.             {
  2612.                 p2++;
  2613.             }
  2614.             while((*p2 == CR) || (*p2 == LF) || XP_IS_SPACE(*p2));
  2615.  
  2616.             if (*p2 == '\0') continue; /* drop out of loop at end of string*/
  2617.         }
  2618.  
  2619.         /* Copy the next non-linebreaking char */
  2620.         *p1 = *p2;
  2621.         p1++; p2++;
  2622.     }
  2623.     *p1 = '\0';
  2624.  
  2625.     return original;
  2626. }
  2627.  
  2628. extern int16 INTL_DefaultMailToWinCharSetID(int16 csid);
  2629.  
  2630. /* Given text purporting to be a qtext header value, strip backslashes that
  2631.     may be escaping other chars in the string. */
  2632. char *
  2633. mime_decode_filename(char *name)
  2634. {
  2635.     char *s = name, *d = name;
  2636.     char *cvt, *returnVal = NULL;
  2637.     int16 mail_csid = CS_DEFAULT, win_csid = CS_DEFAULT;
  2638.     
  2639.     while (*s)
  2640.     {
  2641.         /* Remove backslashes when they are used to escape special characters. */
  2642.         if ((*s == '\\') &&
  2643.             ((*(s+1) == CR) || (*(s+1) == LF) || (*(s+1) == '"') || (*(s+1) == '\\')))
  2644.             s++; /* take whatever char follows the backslash */
  2645.         if (*s)
  2646.             *d++ = *s++;
  2647.     }
  2648.     *d = 0;
  2649.     returnVal = name;
  2650.     
  2651.     /* If there is a MIME-2 encoded-word in the string, 
  2652.         get the charset of the first one and decode to that charset. */
  2653.     s = XP_STRSTR(returnVal, "=?");
  2654.     if (s)
  2655.     {
  2656.         s += 2;
  2657.         d = XP_STRCHR(s, '?');
  2658.         if (d) *d = '\0';
  2659.         mail_csid = INTL_CharSetNameToID(s);
  2660.         if (d) *d = '?';
  2661.         win_csid = INTL_DocToWinCharSetID(mail_csid);
  2662.         
  2663.         cvt = INTL_DecodeMimePartIIStr(returnVal, win_csid, FALSE);
  2664.         if (cvt && cvt != returnVal)
  2665.             returnVal = cvt;
  2666.     }
  2667.  
  2668.  
  2669.     /* Seriously ugly hack. If the first three characters of the filename 
  2670.        are <ESC>$B, then we know the filename is in JIS and should be 
  2671.        converted to either SJIS or EUC-JP. */ 
  2672.     if ((XP_STRLEN(returnVal) > 3) && 
  2673.         (returnVal[0] == 0x1B) && (returnVal[1] == '$') && (returnVal[2] == 'B')) 
  2674.       { 
  2675.         int16 dest_csid = INTL_DocToWinCharSetID(CS_JIS); 
  2676.         
  2677.         cvt = (char *) INTL_ConvertLineWithoutAutoDetect(CS_JIS, dest_csid, (unsigned char *)returnVal, XP_STRLEN(returnVal)); 
  2678.         if (cvt && cvt != returnVal) 
  2679.           { 
  2680.             if (returnVal != name) XP_FREE(returnVal); 
  2681.             returnVal = cvt; 
  2682.           } 
  2683.       } 
  2684.     
  2685.     return returnVal;
  2686. }
  2687.  
  2688.  
  2689. /* Pull the name out of some header or another.  Order is:
  2690.    Content-Disposition: XXX; filename=NAME (RFC 1521/1806)
  2691.    Content-Type: XXX; name=NAME (RFC 1341)
  2692.    Content-Name: NAME (no RFC, but seen to occur)
  2693.    X-Sun-Data-Name: NAME (no RFC, but used by MailTool)
  2694.  */
  2695. char *
  2696. MimeHeaders_get_name(MimeHeaders *hdrs)
  2697. {
  2698.   char *s = 0, *name = 0, *cvt = 0;
  2699.  
  2700.   s = MimeHeaders_get(hdrs, HEADER_CONTENT_DISPOSITION, FALSE, FALSE);
  2701.   if (s)
  2702.     {
  2703.       name = MimeHeaders_get_parameter(s, HEADER_PARM_FILENAME);
  2704.       XP_FREE(s);
  2705.     }
  2706.  
  2707.   if (! name)
  2708.   {
  2709.       s = MimeHeaders_get(hdrs, HEADER_CONTENT_TYPE, FALSE, FALSE);
  2710.       if (s)
  2711.       {
  2712.           name = MimeHeaders_get_parameter(s, HEADER_PARM_NAME);
  2713.           XP_FREE(s);
  2714.       }
  2715.   }
  2716.  
  2717.   if (! name)
  2718.       name = MimeHeaders_get (hdrs, HEADER_CONTENT_NAME, FALSE, FALSE);
  2719.   
  2720.   if (! name)
  2721.       name = MimeHeaders_get (hdrs, HEADER_X_SUN_DATA_NAME, FALSE, FALSE);
  2722.  
  2723.   if (name)
  2724.   {
  2725.         /*    First remove continuation delimiters (CR+LF+space), then
  2726.             remove escape ('\\') characters, then attempt to decode
  2727.             mime-2 encoded-words. The latter two are done in 
  2728.             mime_decode_filename. 
  2729.             */
  2730.         strip_continuations(name);
  2731.  
  2732.         /*    Argh. What we should do if we want to be robust is to decode qtext
  2733.             in all appropriate headers. Unfortunately, that would be too scary
  2734.             at this juncture. So just decode qtext/mime2 here. */
  2735.            cvt = mime_decode_filename(name);
  2736.            if (cvt && cvt != name)
  2737.            {
  2738.                XP_FREE(name);
  2739.                name = cvt;
  2740.            }
  2741.   }
  2742.  
  2743.   return name;
  2744. }
  2745.  
  2746.  
  2747.  
  2748. #ifdef XP_UNIX
  2749. /* This piece of junk is so that I can use BBDB with Mozilla.
  2750.    = Put bbdb-srv.perl on your path.
  2751.    = Put bbdb-srv.el on your lisp path.
  2752.    = Make sure gnudoit (comes with xemacs) is on your path.
  2753.    = Put (gnuserv-start) in ~/.emacs
  2754.    = setenv NS_MSG_DISPLAY_HOOK bbdb-srv.perl
  2755.  */
  2756. void
  2757. MimeHeaders_do_unix_display_hook_hack(MimeHeaders *hdrs)
  2758. {
  2759.   static char *cmd = 0;
  2760.   if (!cmd)
  2761.     {
  2762.       /* The first time we're invoked, look up the command in the
  2763.          environment.  Use "" as the `no command' tag. */
  2764.       cmd = getenv("NS_MSG_DISPLAY_HOOK");
  2765.       if (!cmd)
  2766.         cmd = "";
  2767.       else
  2768.         cmd = XP_STRDUP(cmd);
  2769.     }
  2770.  
  2771.   /* Invoke "cmd" at the end of a pipe, and give it the headers on stdin.
  2772.      The command is expected to be safe from hostile input!!
  2773.    */
  2774.   if (cmd && *cmd)
  2775.     {
  2776.       FILE *fp = popen(cmd, "w");
  2777.       if (fp)
  2778.         {
  2779.           fwrite(hdrs->all_headers, 1, hdrs->all_headers_fp, fp);
  2780.           pclose(fp);
  2781.         }
  2782.     }
  2783. }
  2784. #endif /* XP_UNIX */
  2785.