home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libmime / mimeenc.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  28.3 KB  |  1,117 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. /* mimeenc.c --- MIME encoders and decoders, version 2.
  20.    Created: Jamie Zawinski <jwz@netscape.com>, 15-May-96.
  21.  */
  22.  
  23.  
  24. #include "mimeenc.h"
  25. #include "mimei.h"
  26.  
  27. typedef enum mime_encoding {
  28.   mime_Base64, mime_QuotedPrintable, mime_uuencode
  29. } mime_encoding;
  30.  
  31. typedef enum mime_uue_state {
  32.   UUE_BEGIN, UUE_BODY, UUE_END
  33. } mime_uue_state;
  34.  
  35. struct MimeDecoderData {
  36.   mime_encoding encoding;        /* Which encoding to use */
  37.  
  38.   /* A read-buffer used for QP and B64. */
  39.   char token[4];
  40.   int token_size;
  41.  
  42.   /* State and read-buffer used for uudecode. */
  43.   mime_uue_state uue_state;
  44.   char uue_line_buffer [128];
  45.  
  46.   /* Where to write the decoded data */
  47.   int (*write_buffer) (const char *buf, int32 size, void *closure);
  48.   void *closure;
  49. };
  50.  
  51.  
  52. static int
  53. mime_decode_qp_buffer (MimeDecoderData *data, const char *buffer, int32 length)
  54. {
  55.   /* Warning, we are overwriting the buffer which was passed in.
  56.      This is ok, because decoding these formats will never result
  57.      in larger data than the input, only smaller. */
  58.   const char *in  = buffer;
  59.   char *out = (char *) buffer;
  60.   char token [3];
  61.   int i;
  62.  
  63.   XP_ASSERT(data->encoding == mime_QuotedPrintable);
  64.   if (data->encoding != mime_QuotedPrintable) return -1;
  65.  
  66.   /* For the first pass, initialize the token from the unread-buffer. */
  67.   i = 0;
  68.   while (i < 3 && data->token_size > 0)
  69.     {
  70.       token [i] = data->token[i];
  71.       data->token_size--;
  72.       i++;
  73.     }
  74.  
  75.   /* #### BUG: when decoding quoted-printable, we are required to
  76.      strip trailing whitespace from lines -- since when encoding in
  77.      qp, one is required to quote such trailing whitespace, any
  78.      trailing whitespace which remains must have been introduced
  79.      by a stupid gateway. */
  80.  
  81.   while (length > 0 || i != 0)
  82.     {
  83.       while (i < 3 && length > 0)
  84.         {
  85.           token [i++] = *in;
  86.           in++;
  87.           length--;
  88.         }
  89.  
  90.       if (i < 3)
  91.         {
  92.           /* Didn't get enough for a complete token.
  93.              If it might be a token, unread it.
  94.              Otherwise, just dump it.
  95.              */
  96.           XP_MEMCPY (data->token, token, i);
  97.           data->token_size = i;
  98.           i = 0;
  99.           length = 0;
  100.           break;
  101.         }
  102.       i = 0;
  103.  
  104.       if (token [0] == '=')
  105.         {
  106.           unsigned char c = 0;
  107.           if (token[1] >= '0' && token[1] <= '9')
  108.             c = token[1] - '0';
  109.           else if (token[1] >= 'A' && token[1] <= 'F')
  110.             c = token[1] - ('A' - 10);
  111.           else if (token[1] >= 'a' && token[1] <= 'f')
  112.             c = token[1] - ('a' - 10);
  113.           else if (token[1] == CR || token[1] == LF)
  114.             {
  115.               /* =\n means ignore the newline. */
  116.               if (token[1] == CR && token[2] == LF)
  117.                 ;        /* swallow all three chars */
  118.               else
  119.                 {
  120.                   in--;    /* put the third char back */
  121.                   length++;
  122.                 }
  123.               continue;
  124.             }
  125.           else
  126.             {
  127.               /* = followed by something other than hex or newline -
  128.                  pass it through unaltered, I guess.  (But, if
  129.                  this bogus token happened to occur over a buffer
  130.                  boundary, we can't do this, since we don't have
  131.                  space for it.  Oh well.  Screw it.)  */
  132.               if (in > out) *out++ = token[0];
  133.               if (in > out) *out++ = token[1];
  134.               if (in > out) *out++ = token[2];
  135.               continue;
  136.             }
  137.  
  138.           /* Second hex digit */
  139.           c = (c << 4);
  140.           if (token[2] >= '0' && token[2] <= '9')
  141.             c += token[2] - '0';
  142.           else if (token[2] >= 'A' && token[2] <= 'F')
  143.             c += token[2] - ('A' - 10);
  144.           else if (token[2] >= 'a' && token[2] <= 'f')
  145.             c += token[2] - ('a' - 10);
  146.           else
  147.             {
  148.               /* We got =xy where "x" was hex and "y" was not, so
  149.                  treat that as a literal "=", x, and y.  (But, if
  150.                  this bogus token happened to occur over a buffer
  151.                  boundary, we can't do this, since we don't have
  152.                  space for it.  Oh well.  Screw it.) */
  153.               if (in > out) *out++ = token[0];
  154.               if (in > out) *out++ = token[1];
  155.               if (in > out) *out++ = token[2];
  156.               continue;
  157.             }
  158.  
  159.           *out++ = (char) c;
  160.         }
  161.       else
  162.         {
  163.           *out++ = token[0];
  164.  
  165.           token[0] = token[1];
  166.           token[1] = token[2];
  167.           i = 2;
  168.         }
  169.     }
  170.  
  171.   /* Now that we've altered the data in place, write it. */
  172.   if (out > buffer)
  173.     return data->write_buffer (buffer, (out - buffer), data->closure);
  174.   else
  175.     return 1;
  176. }
  177.  
  178.  
  179. static int
  180. mime_decode_base64_token (const char *in, char *out)
  181. {
  182.   /* reads 4, writes 0-3.  Returns bytes written.
  183.      (Writes less than 3 only at EOF.) */
  184.   int j;
  185.   int eq_count = 0;
  186.   unsigned long num = 0;
  187.  
  188.   for (j = 0; j < 4; j++)
  189.     {
  190.       unsigned char c = 0;
  191.       if (in[j] >= 'A' && in[j] <= 'Z')         c = in[j] - 'A';
  192.       else if (in[j] >= 'a' && in[j] <= 'z') c = in[j] - ('a' - 26);
  193.       else if (in[j] >= '0' && in[j] <= '9') c = in[j] - ('0' - 52);
  194.       else if (in[j] == '+')                 c = 62;
  195.       else if (in[j] == '/')                 c = 63;
  196.       else if (in[j] == '=')                 c = 0, eq_count++;
  197.       else
  198.         XP_ASSERT(0);
  199.       num = (num << 6) | c;
  200.     }
  201.  
  202.   *out++ = (char) (num >> 16);
  203.   *out++ = (char) ((num >> 8) & 0xFF);
  204.   *out++ = (char) (num & 0xFF);
  205.  
  206.   if (eq_count == 0)
  207.     return 3;                /* No "=" padding means 4 bytes mapped to 3. */
  208.   else if (eq_count == 1)
  209.     return 2;                /* "xxx=" means 3 bytes mapped to 2. */
  210.   else if (eq_count == 2)
  211.     return 1;                /* "xx==" means 2 bytes mapped to 1. */
  212.   else
  213.     {                        /* "x===" can't happen, because "x" would then */
  214.       XP_ASSERT(0);            /* be encoding only 6 bits, not the min of 8. */
  215.       return 1;
  216.     }
  217. }
  218.  
  219.  
  220. static int
  221. mime_decode_base64_buffer (MimeDecoderData *data,
  222.                            const char *buffer, int32 length)
  223. {
  224.   /* Warning, we are overwriting the buffer which was passed in.
  225.      This is ok, because decoding these formats will never result
  226.      in larger data than the input, only smaller. */
  227.   const char *in  = buffer;
  228.   char *out = (char *) buffer;
  229.   char token [4];
  230.   int i;
  231.   XP_Bool leftover = (data->token_size > 0);
  232.  
  233.   XP_ASSERT(data->encoding == mime_Base64);
  234.  
  235.   /* For the first pass, initialize the token from the unread-buffer. */
  236.   i = 0;
  237.   while (i < 4 && data->token_size > 0)
  238.     {
  239.       token [i] = data->token[i];
  240.       data->token_size--;
  241.       i++;
  242.     }
  243.  
  244.   while (length > 0)
  245.     {
  246.       while (i < 4 && length > 0)
  247.         {
  248.           if ((*in >= 'A' && *in <= 'Z') ||
  249.               (*in >= 'a' && *in <= 'z') ||
  250.               (*in >= '0' && *in <= '9') ||
  251.               *in == '+' || *in == '/' || *in == '=')
  252.             token [i++] = *in;
  253.           in++;
  254.           length--;
  255.         }
  256.  
  257.       if (i < 4)
  258.         {
  259.           /* Didn't get enough for a complete token. */
  260.           XP_MEMCPY (data->token, token, i);
  261.           data->token_size = i;
  262.           length = 0;
  263.           break;
  264.         }
  265.       i = 0;
  266.  
  267.       if (leftover)
  268.         {
  269.           /* If there are characters left over from the last time around,
  270.              we might not have space in the buffer to do our dirty work
  271.              (if there were 2 or 3 left over, then there is only room for
  272.              1 or 2 in the buffer right now, and we need 3.)  This is only
  273.              a problem for the first chunk in each buffer, so in that
  274.              case, just write prematurely. */
  275.           int n;
  276.           n = mime_decode_base64_token (token, token);
  277.           n = data->write_buffer (token, n, data->closure);
  278.           if (n < 0) /* abort */
  279.             return n;
  280.  
  281.           /* increment buffer so that we don't write the 1 or 2 unused
  282.              characters now at the front. */
  283.           buffer = in;
  284.           out = (char *) buffer;
  285.  
  286.           leftover = FALSE;
  287.         }
  288.       else
  289.         {
  290.           int n = mime_decode_base64_token (token, out);
  291.           /* Advance "out" by the number of bytes just written to it. */
  292.           out += n;
  293.         }
  294.     }
  295.  
  296.   /* Now that we've altered the data in place, write it. */
  297.   if (out > buffer)
  298.     return data->write_buffer (buffer, (out - buffer), data->closure);
  299.   else
  300.     return 1;
  301. }
  302.  
  303.  
  304. static int
  305. mime_decode_uue_buffer (MimeDecoderData *data,
  306.                         const char *input_buffer, int32 input_length)
  307. {
  308.   /* First, copy input_buffer into state->uue_line_buffer until we have
  309.      a complete line.
  310.  
  311.      Then decode that line in place (in the uue_line_buffer) and write
  312.      it out.
  313.  
  314.      Then pull the next line into uue_line_buffer and continue.
  315.    */
  316.   int status = 0;
  317.   char *line = data->uue_line_buffer;
  318.   char *line_end = data->uue_line_buffer + sizeof (data->uue_line_buffer) - 1;
  319.  
  320.   XP_ASSERT(data->encoding == mime_uuencode);
  321.   if (data->encoding != mime_uuencode) return -1;
  322.  
  323.   if (data->uue_state == UUE_END)
  324.     {
  325.       status = 0;
  326.       goto DONE;
  327.     }
  328.  
  329.   while (input_length > 0)
  330.     {
  331.       /* Copy data from input_buffer to `line' until we have a complete line,
  332.          or until we've run out of input.
  333.  
  334.          (line may have data in it already if the last time we were called,
  335.          we weren't called with a buffer that ended on a line boundary.)
  336.        */
  337.       {
  338.         char *out = line + XP_STRLEN(line);
  339.         while (input_length > 0 &&
  340.                out < line_end)
  341.           {
  342.             *out++ = *input_buffer++;
  343.             input_length--;
  344.  
  345.             if (out[-1] == CR || out[-1] == LF)
  346.               {
  347.                 /* If we just copied a CR, and an LF is waiting, grab it too.
  348.                  */
  349.                 if (out[-1] == CR &&
  350.                     input_length > 0 &&
  351.                     *input_buffer == LF)
  352.                   input_buffer++, input_length--;
  353.  
  354.                 /* We have a line. */
  355.                 break;
  356.               }
  357.           }
  358.         *out = 0;
  359.  
  360.         /* Ignore blank lines.
  361.          */
  362.         if (*line == CR || *line == LF)
  363.           {
  364.             *line = 0;
  365.             continue;
  366.           }
  367.  
  368.         /* If this line was bigger than our buffer, truncate it.
  369.            (This means the data was way corrupted, and there's basically
  370.            no chance of decoding it properly, but give it a shot anyway.)
  371.          */
  372.         if (out == line_end)
  373.           {
  374.             out--;
  375.             out[-1] = CR;
  376.             out[0] = 0;
  377.           }
  378.  
  379.         /* If we didn't get a complete line, simply return; we'll be called
  380.            with the rest of this line next time.
  381.          */
  382.         if (out[-1] != CR && out[-1] != LF)
  383.           {
  384.             XP_ASSERT (input_length == 0);
  385.             break;
  386.           }
  387.       }
  388.  
  389.  
  390.       /* Now we have a complete line.  Deal with it.
  391.        */
  392.  
  393.  
  394.       if (data->uue_state == UUE_BODY &&
  395.           line[0] == 'e' &&
  396.           line[1] == 'n' &&
  397.           line[2] == 'd' &&
  398.           (line[3] == CR ||
  399.            line[3] == LF))
  400.         {
  401.           /* done! */
  402.           data->uue_state = UUE_END;
  403.           *line = 0;
  404.           break;
  405.         }
  406.       else if (data->uue_state == UUE_BEGIN)
  407.         {
  408.           if (!XP_STRNCMP (line, "begin ", 6))
  409.             data->uue_state = UUE_BODY;
  410.           *line = 0;
  411.           continue;
  412.         }
  413.       else
  414.         {
  415.           /* We're in UUE_BODY.  Decode the line. */
  416.           char *in, *out;
  417.           int32 i;
  418.           long lost;
  419.  
  420.           XP_ASSERT (data->uue_state == UUE_BODY);
  421.  
  422.           /* We map down `line', reading four bytes and writing three.
  423.              That means that `out' always stays safely behind `in'.
  424.            */
  425.           in = line;
  426.           out = line;
  427.  
  428. # undef DEC
  429. # define DEC(c) (((c) - ' ') & 077)
  430.           i = DEC (*in); /* get length */
  431.  
  432.           /* all the parens and casts are because gcc was doing something evil.
  433.            */
  434.           lost = ((long) i) - (((((long) XP_STRLEN (in)) - 2L) * 3L) / 4L);
  435.  
  436.           if (lost > 0) /* Short line!! */
  437.             {
  438.               /* If we get here, then the line is shorter than the length byte
  439.                  at the beginning says it should be.  However, the case where
  440.                  the line is short because it was at the end of the buffer and
  441.                  we didn't get the whole line was handled earlier (up by the
  442.                  "didn't get a complete line" comment.)  So if we've gotten
  443.                  here, then this is a complete line which is internally
  444.                  inconsistent.  We will parse from it what we can...
  445.  
  446.                  This probably happened because some gateway stripped trailing
  447.                  whitespace from the end of the line -- so pretend the line
  448.                  was padded with spaces (which map to \000.)
  449.                */
  450.               i -= lost;
  451.             }
  452.  
  453.           for (++in; i > 0; in += 4, i -= 3)
  454.             {
  455.               char ch;
  456.               XP_ASSERT(out <= in);
  457.  
  458.               if (i >= 3)
  459.                 {
  460.                   /* We read four; write three. */
  461.                   ch = DEC (in[0]) << 2 | DEC (in[1]) >> 4;
  462.                   *out++ = ch;
  463.  
  464.                   XP_ASSERT(out <= in+1);
  465.  
  466.                   ch = DEC (in[1]) << 4 | DEC (in[2]) >> 2;
  467.                   *out++ = ch;
  468.  
  469.                   XP_ASSERT(out <= in+2);
  470.  
  471.                   ch = DEC (in[2]) << 6 | DEC (in[3]);
  472.                   *out++ = ch;
  473.  
  474.                   XP_ASSERT(out <= in+3);
  475.                 }
  476.               else
  477.                 {
  478.                   /* Handle a line that isn't a multiple of 4 long.
  479.                      (We read 1, 2, or 3, and will write 1 or 2.)
  480.                    */
  481.                   XP_ASSERT (i > 0 && i < 3);
  482.  
  483.                   ch = DEC (in[0]) << 2 | DEC (in[1]) >> 4;
  484.                   *out++ = ch;
  485.  
  486.                   XP_ASSERT(out <= in+1);
  487.  
  488.                   if (i == 2)
  489.                     {
  490.                       ch = DEC (in[1]) << 4 | DEC (in[2]) >> 2;
  491.                       *out++ = ch;
  492.  
  493.                       XP_ASSERT(out <= in+2);
  494.                     }
  495.                 }
  496.             }
  497.  
  498.           /* If the line was truncated, pad the missing bytes with 0 (SPC). */
  499.           while (lost > 0)
  500.             {
  501.               *out++ = 0;
  502.               lost--;
  503.               in = out+1; /* just to prevent the assert, below. */
  504.             }
  505. # undef DEC
  506.  
  507.           /* Now write out what we decoded for this line.
  508.            */
  509.           XP_ASSERT(out >= line && out < in);
  510.           if (out > line)
  511.             status = data->write_buffer (line, (out - line), data->closure);
  512.  
  513.           /* Reset the line so that we don't think it's partial next time. */
  514.           *line = 0;
  515.  
  516.           if (status < 0) /* abort */
  517.             goto DONE;
  518.         }
  519.     }
  520.  
  521.   status = 1;
  522.  
  523.  DONE:
  524.  
  525.   return status;
  526. }
  527.  
  528.  
  529. int
  530. MimeDecoderDestroy (MimeDecoderData *data, XP_Bool abort_p)
  531. {
  532.   int status = 0;
  533.   /* Flush out the last few buffered characters. */
  534.   if (!abort_p &&
  535.       data->token_size > 0 &&
  536.       data->token[0] != '=')
  537.     {
  538.       if (data->encoding == mime_Base64)
  539.         while (data->token_size < sizeof (data->token))
  540.           data->token [data->token_size++] = '=';
  541.  
  542.       status = data->write_buffer (data->token, data->token_size,
  543.                                    data->closure);
  544.     }
  545.  
  546.   XP_FREE (data);
  547.   return status;
  548. }
  549.  
  550.  
  551. static MimeDecoderData *
  552. mime_decoder_init (enum mime_encoding which,
  553.                    int (*output_fn) (const char *, int32, void *),
  554.                    void *closure)
  555. {
  556.   MimeDecoderData *data = XP_NEW(MimeDecoderData);
  557.   if (!data) return 0;
  558.   XP_MEMSET(data, 0, sizeof(*data));
  559.   data->encoding = which;
  560.   data->write_buffer = output_fn;
  561.   data->closure = closure;
  562.   return data;
  563. }
  564.  
  565. MimeDecoderData *
  566. MimeB64DecoderInit (int (*output_fn) (const char *, int32, void *),
  567.                     void *closure)
  568. {
  569.   return mime_decoder_init (mime_Base64, output_fn, closure);
  570. }
  571.  
  572. MimeDecoderData *
  573. MimeQPDecoderInit (int (*output_fn) (const char *, int32, void *),
  574.                    void *closure)
  575. {
  576.   return mime_decoder_init (mime_QuotedPrintable, output_fn, closure);
  577. }
  578.  
  579. MimeDecoderData *
  580. MimeUUDecoderInit (int (*output_fn) (const char *, int32, void *),
  581.                    void *closure)
  582. {
  583.   return mime_decoder_init (mime_uuencode, output_fn, closure);
  584. }
  585.  
  586. int
  587. MimeDecoderWrite (MimeDecoderData *data, const char *buffer, int32 size)
  588. {
  589.   XP_ASSERT(data);
  590.   if (!data) return -1;
  591.   switch(data->encoding)
  592.     {
  593.     case mime_Base64:
  594.       return mime_decode_base64_buffer (data, buffer, size);
  595.     case mime_QuotedPrintable:
  596.       return mime_decode_qp_buffer (data, buffer, size);
  597.     case mime_uuencode:
  598.       return mime_decode_uue_buffer (data, buffer, size);
  599.     default:
  600.       XP_ASSERT(0);
  601.       return -1;
  602.     }
  603. }
  604.  
  605.  
  606.  
  607. /* ================== Encoders.
  608.  */
  609.  
  610. struct MimeEncoderData {
  611.   mime_encoding encoding;        /* Which encoding to use */
  612.  
  613.   /* Buffer for the base64 encoder. */
  614.   unsigned char in_buffer[3];
  615.   int32 in_buffer_count;
  616.  
  617.     /* Buffer for uuencoded data. (Need a line because of the length byte.) */
  618.     unsigned char uue_line_buf[128];
  619.     XP_Bool uue_wrote_begin;
  620.     
  621.   int32 current_column, line_byte_count;
  622.   
  623.   char *filename; /* filename for use with uuencoding */
  624.  
  625.   /* Where to write the encoded data */
  626.   int (*write_buffer) (const char *buf, int32 size, void *closure);
  627.   void *closure;
  628. };
  629.  
  630. /* Use what looks like a nice, safe value for a standard uue line length */
  631. #define UUENCODE_LINE_LIMIT 60
  632.  
  633. #undef ENC
  634. #define ENC(c) ((c & 0x3F) + ' ')
  635.  
  636. void
  637. mime_uuencode_write_line(MimeEncoderData *data)
  638. {
  639.     /* Set the length byte at the beginning: 
  640.        encoded (data->line_byte_count). */
  641.     data->uue_line_buf[0] = ENC(data->line_byte_count);
  642.  
  643.     /* Tack a CRLF onto the end. */
  644.     data->uue_line_buf[data->current_column++] = CR;
  645.     data->uue_line_buf[data->current_column++] = LF;
  646.  
  647.     /* Write the line to output. */
  648.     data->write_buffer((const char*)data->uue_line_buf, data->current_column,
  649.                        data->closure);
  650.  
  651.     /* Reset data based on having just written a complete line. */
  652.     data->in_buffer_count = 0;
  653.     data->line_byte_count = 0;
  654.     data->current_column = 1;
  655. }
  656.  
  657. void
  658. mime_uuencode_convert_triplet(MimeEncoderData *data)
  659. {
  660.     /* 
  661.        If we have 3 bytes, encode them and add them to the current 
  662.        line. The way we want to encode them is like this 
  663.        (each digit corresponds to a bit in the binary source):
  664.        11111111 -> 00111111 + ' ' (six highest bits of 1)
  665.        22222222    00112222 + ' ' (low 2 of 1, high 4 of 2)
  666.        33333333    00222233 + ' ' (low 4 of 2, high 2 of 3)
  667.                    00333333 + ' ' (low 6 of 3)
  668.     */
  669.     char outData[4];
  670.     int i;
  671.     
  672.     outData[0] = data->in_buffer[0] >> 2;
  673.     
  674.     outData[1] = ((data->in_buffer[0] << 4) & 0x30);
  675.     outData[1] |= data->in_buffer[1] >> 4;
  676.  
  677.     outData[2] = ((data->in_buffer[1] << 2) & 0x3C);
  678.     outData[2] |= data->in_buffer[2] >> 6;
  679.  
  680.     outData[3] = data->in_buffer[2] & 0x3F;
  681.  
  682.     for(i=0;i<4;i++)
  683.         data->uue_line_buf[data->current_column++] = ENC(outData[i]);
  684.  
  685.     data->in_buffer_count = 0;
  686. }
  687.  
  688. int
  689. mime_uuencode_buffer(MimeEncoderData *data,
  690.                        const char *buffer, int32 size)
  691. {
  692.     /* If this is the first time through, write a begin statement. */
  693.     if (!(data->uue_wrote_begin))
  694.     {
  695.         char firstLine[256];
  696.         XP_SPRINTF(firstLine, "begin 644 %s\015\012", data->filename ? data->filename : "");
  697.         data->write_buffer(firstLine, strlen(firstLine), data->closure);
  698.         data->uue_wrote_begin = TRUE;
  699.         data->current_column = 1; /* initialization unique to uuencode */
  700.     }
  701.  
  702.     /* Pick up where we left off. */
  703.     while(size > 0)
  704.     {
  705.         /* If we've reached the end of a line, write the line out. */
  706.         if (data->current_column >= UUENCODE_LINE_LIMIT) 
  707.         {
  708.             /* End of a line. Write the line out. */
  709.             mime_uuencode_write_line(data);
  710.         }
  711.  
  712.         /* Get the next 3 bytes if we have them, or whatever we can get. */
  713.         while(size > 0 && data->in_buffer_count < 3)
  714.         {
  715.             data->in_buffer[data->in_buffer_count++] = *(buffer++);
  716.             size--; data->line_byte_count++;
  717.         }
  718.         
  719.         if (data->in_buffer_count == 3)
  720.         {
  721.             mime_uuencode_convert_triplet(data);
  722.         }
  723.     }
  724.     return 0;
  725. }
  726.  
  727. int
  728. mime_uuencode_finish(MimeEncoderData *data)
  729. {
  730.     int i;
  731.     static const char *endStr = "end\015\012";
  732.  
  733.     /* If we have converted binary data to write to output, do it now. */
  734.     if (data->line_byte_count > 0)
  735.     {
  736.         /* If we have binary data yet to be converted, 
  737.            pad and convert it. */
  738.         if (data->in_buffer_count > 0)
  739.         {
  740.             for(i=data->in_buffer_count;i<3;i++)
  741.                 data->in_buffer[i] = '\0'; /* pad with zeroes */
  742.  
  743.             mime_uuencode_convert_triplet(data);
  744.         }
  745.  
  746.         mime_uuencode_write_line(data);
  747.     }
  748.  
  749.     /* Write 'end' on a line by itself. */
  750.     return data->write_buffer(endStr, strlen(endStr), data->closure);
  751. }
  752.  
  753. #undef ENC
  754.  
  755. int
  756. mime_encode_base64_buffer (MimeEncoderData *data,
  757.                            const char *buffer, int32 size)
  758. {
  759.   int status = 0;
  760.   const unsigned char *in = (unsigned char *) buffer;
  761.   const unsigned char *end = in + size;
  762.   char out_buffer[80];
  763.   char *out = out_buffer;
  764.   uint32 i = 0, n = 0;
  765.   uint32 off;
  766.  
  767.   if (size == 0)
  768.     return 0;
  769.   else if (size < 0)
  770.     {
  771.       XP_ASSERT(0);
  772.       return -1;
  773.     }
  774.  
  775.  
  776.   /* If this input buffer is too small, wait until next time. */
  777.   if (size < (3 - data->in_buffer_count))
  778.     {
  779.       XP_ASSERT(size < 3 && size > 0);
  780.       data->in_buffer[data->in_buffer_count++] = buffer[0];
  781.       if (size > 1)
  782.         data->in_buffer[data->in_buffer_count++] = buffer[1];
  783.       XP_ASSERT(data->in_buffer_count < 3);
  784.       return 0;
  785.     }
  786.  
  787.  
  788.   /* If there are bytes that were put back last time, take them now.
  789.    */
  790.   i = 0;
  791.   if (data->in_buffer_count > 0) n = data->in_buffer[0];
  792.   if (data->in_buffer_count > 1) n = (n << 8) + data->in_buffer[1];
  793.   i = data->in_buffer_count;
  794.   data->in_buffer_count = 0;
  795.  
  796.   /* If this buffer is not a multiple of three, put one or two bytes back.
  797.    */
  798.   off = ((size + i) % 3);
  799.   if (off)
  800.     {
  801.       data->in_buffer[0] = buffer [size - off];
  802.       if (off > 1)
  803.         data->in_buffer [1] = buffer [size - off + 1];
  804.       data->in_buffer_count = off;
  805.       size -= off;
  806.       XP_ASSERT (! ((size + i) % 3));
  807.       end = (unsigned char *) (buffer + size);
  808.     }
  809.  
  810.   /* Populate the out_buffer with base64 data, one line at a time.
  811.    */
  812.   while (in < end)
  813.     {
  814.       int32 j;
  815.  
  816.       while (i < 3)
  817.         {
  818.           n = (n << 8) | *in++;
  819.           i++;
  820.         }
  821.       i = 0;
  822.  
  823.       for (j = 18; j >= 0; j -= 6)
  824.         {
  825.           unsigned int k = (n >> j) & 0x3F;
  826.           if (k < 26)       *out++ = k      + 'A';
  827.           else if (k < 52)  *out++ = k - 26 + 'a';
  828.           else if (k < 62)  *out++ = k - 52 + '0';
  829.           else if (k == 62) *out++ = '+';
  830.           else if (k == 63) *out++ = '/';
  831.           else abort ();
  832.         }
  833.  
  834.       data->current_column += 4;
  835.       if (data->current_column >= 72)
  836.         {
  837.           /* Do a linebreak before column 76.  Flush out the line buffer. */
  838.           data->current_column = 0;
  839.           *out++ = '\015';
  840.           *out++ = '\012';
  841.           status = data->write_buffer (out_buffer, (out - out_buffer),
  842.                                        data->closure);
  843.           out = out_buffer;
  844.           if (status < 0) return status;
  845.         }
  846.     }
  847.  
  848.   /* Write out the unwritten portion of the last line buffer. */
  849.   if (out > out_buffer)
  850.     {
  851.       status = data->write_buffer (out_buffer, (out - out_buffer),
  852.                                    data->closure);
  853.       if (status < 0) return status;
  854.     }
  855.  
  856.   return 0;
  857. }
  858.  
  859.  
  860. int
  861. mime_encode_qp_buffer (MimeEncoderData *data, const char *buffer, int32 size)
  862. {
  863.   int status = 0;
  864.   static const char hexdigits[] = "0123456789ABCDEF";
  865.   const unsigned char *in = (unsigned char *) buffer;
  866.   const unsigned char *end = in + size;
  867.   char out_buffer[80];
  868.   char *out = out_buffer;
  869.   uint32 i = 0, n = 0;
  870.   XP_Bool white = FALSE;
  871.   XP_Bool mb_p = FALSE;
  872.  
  873. /*
  874.   #### I don't know how to hook this back up:
  875.   ####  mb_p = INTL_DefaultWinCharSetID(state->context) & 0x300 ;    
  876.  */
  877.  
  878.  
  879.   XP_ASSERT(data->in_buffer_count == 0);
  880.  
  881.   /* Populate the out_buffer with quoted-printable data, one line at a time.
  882.    */
  883.   for (; in < end; in++)
  884.     {
  885.       if (*in == CR || *in == LF)
  886.         {
  887.           /* Whitespace cannot be allowed to occur at the end of the line.
  888.              So we encode " \n" as " =\n\n", that is, the whitespace, a
  889.              soft line break, and then a hard line break.
  890.            */
  891.           if (white)
  892.             {
  893.               *out++ = '=';
  894.               *out++ = CR;
  895.               *out++ = LF;
  896.             }
  897.  
  898.           /* Now write out the newline. */
  899.           *out++ = CR;
  900.           *out++ = LF;
  901.           white = FALSE;
  902.  
  903.           status = data->write_buffer (out_buffer, (out - out_buffer),
  904.                                        data->closure);
  905.           if (status < 0) return status;
  906.           out = out_buffer;
  907.  
  908.           /* If its CRLF, swallow two chars instead of one. */
  909.           if (in[0] == CR && in[1] == LF)
  910.             in++;
  911.  
  912.           out = out_buffer;
  913.           white = FALSE;
  914.           data->current_column = 0;
  915.         }
  916.       else if (data->current_column == 0 && *in == '.')
  917.         {
  918.           /* Just to be SMTP-safe, if "." appears in column 0, encode it.
  919.              (mmencode does this too.)
  920.            */
  921.           goto HEX;
  922.         }
  923.       else if (data->current_column == 0 && *in == 'F'
  924.                && (in >= end-1 || in[1] == 'r')
  925.                && (in >= end-2 || in[2] == 'o')
  926.                && (in >= end-3 || in[3] == 'm')
  927.                && (in >= end-4 || in[4] == ' '))
  928.         {
  929.           /* If this line begins with 'F' and we cannot determine that
  930.              this line does not begin with "From " then do the safe thing
  931.              and assume that it does, and encode the 'F' in hex to avoid
  932.              BSD mailbox lossage.  (We might not be able to tell that it
  933.              is really "From " if the end of the buffer was early.  So
  934.              this means that "\nFoot" will have the F encoded if the end of
  935.              the buffer happens to fall just after the F; but will not have
  936.              it encoded if it's after the first "o" or later.  Oh well.
  937.              It's a little inconsistent, but it errs on the safe side.)
  938.            */
  939.           goto HEX;
  940.         }
  941.       else if ((*in >= 33 && *in <= 60) ||        /* safe printing chars */
  942.                (*in >= 62 && *in <= 126) ||
  943.                (mb_p && (*in == 61 || *in == 127 || *in == 0x1B)))
  944.         {
  945.           white = FALSE;
  946.           *out++ = *in;
  947.           data->current_column++;
  948.         }
  949.       else if (*in == ' ' || *in == '\t')        /* whitespace */
  950.         {
  951.           white = TRUE;
  952.           *out++ = *in;
  953.           data->current_column++;
  954.         }
  955.       else                                        /* print as =FF */
  956.         {
  957.         HEX:
  958.           white = FALSE;
  959.           *out++ = '=';
  960.           *out++ = hexdigits[*in >> 4];
  961.           *out++ = hexdigits[*in & 0xF];
  962.           data->current_column += 3;
  963.         }
  964.  
  965.       XP_ASSERT (data->current_column <= 76); /* Hard limit required by spec */
  966.  
  967.       if (data->current_column >= 73)        /* soft line break: "=\r\n" */
  968.         {
  969.           *out++ = '=';
  970.           *out++ = CR;
  971.           *out++ = LF;
  972.  
  973.           status = data->write_buffer (out_buffer, (out - out_buffer),
  974.                                        data->closure);
  975.           if (status < 0) return status;
  976.           out = out_buffer;
  977.           white = FALSE;
  978.           data->current_column = 0;
  979.         }
  980.     }
  981.  
  982.   /* Write out the unwritten portion of the last line buffer. */
  983.   if (out > out_buffer)
  984.     {
  985.       status = data->write_buffer (out_buffer, (out - out_buffer),
  986.                                    data->closure);
  987.       if (status < 0) return status;
  988.     }
  989.  
  990.   return 0;
  991. }
  992.  
  993.  
  994.  
  995. int
  996. MimeEncoderDestroy (MimeEncoderData *data, XP_Bool abort_p)
  997. {
  998.   int status = 0;
  999.  
  1000.   /* If we're uuencoding, we have our own finishing routine. */
  1001.   if (data->encoding == mime_uuencode)
  1002.      mime_uuencode_finish(data);
  1003.  
  1004.   /* Since Base64 (and uuencode) output needs to do some buffering to get 
  1005.      a multiple of three bytes on each block, there may be a few bytes 
  1006.      left in the buffer after the last block has been written.  We need to
  1007.      flush those out now.
  1008.    */
  1009.  
  1010.   XP_ASSERT (data->encoding == mime_Base64 ||
  1011.              data->in_buffer_count == 0);
  1012.  
  1013.   if (!abort_p &&
  1014.       data->in_buffer_count > 0)
  1015.     {
  1016.       char buf2 [6];
  1017.       char *buf = buf2 + 2;
  1018.       char *out = buf;
  1019.       int j;
  1020.       /* fixed bug 55998, 61302, 61866
  1021.        * type casting to uint32 before shifting
  1022.        */
  1023.       uint32 n = ((uint32) data->in_buffer[0]) << 16;
  1024.       if (data->in_buffer_count > 1)
  1025.         n = n | (((uint32) data->in_buffer[1]) << 8);
  1026.  
  1027.       buf2[0] = CR;
  1028.       buf2[1] = LF;
  1029.  
  1030.       for (j = 18; j >= 0; j -= 6)
  1031.         {
  1032.           unsigned int k = (n >> j) & 0x3F;
  1033.           if (k < 26)       *out++ = k      + 'A';
  1034.           else if (k < 52)  *out++ = k - 26 + 'a';
  1035.           else if (k < 62)  *out++ = k - 52 + '0';
  1036.           else if (k == 62) *out++ = '+';
  1037.           else if (k == 63) *out++ = '/';
  1038.           else abort ();
  1039.         }
  1040.  
  1041.       /* Pad with equal-signs. */
  1042.       if (data->in_buffer_count == 1)
  1043.         buf[2] = '=';
  1044.       buf[3] = '=';
  1045.  
  1046.       if (data->current_column >= 72)
  1047.         status = data->write_buffer (buf2, 6, data->closure);
  1048.       else
  1049.         status = data->write_buffer (buf,  4, data->closure);
  1050.     }
  1051.  
  1052.   XP_FREEIF(data->filename);
  1053.   XP_FREE (data);
  1054.   return status;
  1055. }
  1056.  
  1057.  
  1058. static MimeEncoderData *
  1059. mime_encoder_init (enum mime_encoding which,
  1060.                    int (*output_fn) (const char *, int32, void *),
  1061.                    void *closure)
  1062. {
  1063.   MimeEncoderData *data = XP_NEW(MimeEncoderData);
  1064.   if (!data) return 0;
  1065.   XP_MEMSET(data, 0, sizeof(*data));
  1066.   data->encoding = which;
  1067.   data->write_buffer = output_fn;
  1068.   data->closure = closure;
  1069.   return data;
  1070. }
  1071.  
  1072. MimeEncoderData *
  1073. MimeB64EncoderInit (int (*output_fn) (const char *, int32, void *),
  1074.                     void *closure)
  1075. {
  1076.   return mime_encoder_init (mime_Base64, output_fn, closure);
  1077. }
  1078.  
  1079. MimeEncoderData *
  1080. MimeQPEncoderInit (int (*output_fn) (const char *, int32, void *),
  1081.                    void *closure)
  1082. {
  1083.   return mime_encoder_init (mime_QuotedPrintable, output_fn, closure);
  1084. }
  1085.  
  1086. MimeEncoderData *
  1087. MimeUUEncoderInit (char *filename,
  1088.                     int (*output_fn) (const char *, int32, void *),
  1089.                     void *closure)
  1090. {
  1091.   MimeEncoderData *enc = mime_encoder_init (mime_uuencode, output_fn, closure);
  1092.   
  1093.   if (filename)
  1094.       enc->filename = XP_STRDUP(filename);
  1095.       
  1096.   return enc;
  1097. }
  1098.  
  1099. int
  1100. MimeEncoderWrite (MimeEncoderData *data, const char *buffer, int32 size)
  1101. {
  1102.   XP_ASSERT(data);
  1103.   if (!data) return -1;
  1104.   switch(data->encoding)
  1105.     {
  1106.     case mime_Base64:
  1107.       return mime_encode_base64_buffer (data, buffer, size);
  1108.     case mime_QuotedPrintable:
  1109.       return mime_encode_qp_buffer (data, buffer, size);
  1110.     case mime_uuencode:
  1111.       return mime_uuencode_buffer(data, buffer, size);
  1112.     default:
  1113.       XP_ASSERT(0);
  1114.       return -1;
  1115.     }
  1116. }
  1117.