Back to index

enigmail  1.4.3
mimeenc2.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is mozilla.org code.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1998
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  * Ramalingam Saravanan <sarava@sarava.net>
00023  * Patrick Brunschwig <patrick@mozilla-enigmail.org>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include <stdio.h>
00040 
00041 #define MOZILLA_INTERNAL_API
00042 
00043 #include "modmimee.h"
00044 #include "mimei.h"
00045 #include "prmem.h"
00046 #include "plstr.h"
00047 #include "prlog.h"
00048 #include "prprf.h"
00049 #include "mimeobj.h"
00050 #include "enigmail.h"
00051 
00052 typedef enum mime_encoding {
00053   mime_Base64, mime_QuotedPrintable, mime_uuencode, mime_yencode
00054 } mime_encoding;
00055 
00056 typedef enum mime_decoder_state {
00057   DS_BEGIN, DS_BODY, DS_END
00058 } mime_decoder_state;
00059 
00060 struct MimeDecoderData {
00061   mime_encoding encoding;    /* Which encoding to use */
00062 
00063   /* A read-buffer used for QP and B64. */
00064   char token[4];
00065   int token_size;
00066 
00067   /* State and read-buffer used for uudecode and yencode. */
00068   mime_decoder_state ds_state;
00069   char *line_buffer;
00070   int line_buffer_size;
00071 
00072   MimeObject *objectToDecode; // might be null, only used for QP currently
00073   /* Where to write the decoded data */
00074   nsresult (*write_buffer) (const char *buf, PRInt32 size, void *closure);
00075   void *closure;
00076 };
00077 
00078 
00079 static int
00080 mime_decode_qp_buffer (MimeDecoderData *data, const char *buffer, PRInt32 length)
00081 {
00082   /* Warning, we are overwriting the buffer which was passed in.
00083    This is ok, because decoding these formats will never result
00084    in larger data than the input, only smaller. */
00085   const char *in  = buffer;
00086   char *out = (char *) buffer;
00087   char token [3];
00088   int i;
00089 
00090   NS_ASSERTION(data->encoding == mime_QuotedPrintable, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00091   if (data->encoding != mime_QuotedPrintable) return -1;
00092 
00093   /* For the first pass, initialize the token from the unread-buffer. */
00094   i = 0;
00095   while (i < 3 && data->token_size > 0)
00096   {
00097     token [i] = data->token[i];
00098     data->token_size--;
00099     i++;
00100   }
00101 
00102   /* #### BUG: when decoding quoted-printable, we are required to
00103    strip trailing whitespace from lines -- since when encoding in
00104    qp, one is required to quote such trailing whitespace, any
00105    trailing whitespace which remains must have been introduced
00106    by a stupid gateway. */
00107 
00108   /* Treat null bytes as spaces when format_out is
00109    nsMimeOutput::nsMimeMessageBodyDisplay (see bug 243199 comment 7) */
00110   EMBool treatNullAsSpace = data->objectToDecode &&
00111                             data->objectToDecode->options->format_out == nsMimeOutput::nsMimeMessageBodyDisplay;
00112 
00113   while (length > 0 || i != 0)
00114   {
00115     while (i < 3 && length > 0)
00116     {
00117       token [i++] = *in;
00118       in++;
00119       length--;
00120     }
00121 
00122     if (i < 3)
00123     {
00124       /* Didn't get enough for a complete token.
00125        If it might be a token, unread it.
00126        Otherwise, just dump it.
00127        */
00128       memcpy (data->token, token, i);
00129       data->token_size = i;
00130       i = 0;
00131       length = 0;
00132       break;
00133     }
00134     i = 0;
00135 
00136     if (token [0] == '=')
00137     {
00138       unsigned char c = 0;
00139       if (token[1] >= '0' && token[1] <= '9')
00140       c = token[1] - '0';
00141       else if (token[1] >= 'A' && token[1] <= 'F')
00142       c = token[1] - ('A' - 10);
00143       else if (token[1] >= 'a' && token[1] <= 'f')
00144       c = token[1] - ('a' - 10);
00145       else if (token[1] == '\r' || token[1] == '\n')
00146       {
00147         /* =\n means ignore the newline. */
00148         if (token[1] == '\r' && token[2] == '\n')
00149         ;    /* swallow all three chars */
00150         else
00151         {
00152           in--;  /* put the third char back */
00153           length++;
00154         }
00155         continue;
00156       }
00157       else
00158       {
00159         /* = followed by something other than hex or newline -
00160          pass it through unaltered, I guess.  (But, if
00161          this bogus token happened to occur over a buffer
00162          boundary, we can't do this, since we don't have
00163          space for it.  Oh well.  Screw it.)  */
00164         if (in > out) *out++ = token[0];
00165         if (in > out) *out++ = token[1];
00166         if (in > out) *out++ = token[2];
00167         continue;
00168       }
00169 
00170       /* Second hex digit */
00171       c = (c << 4);
00172       if (token[2] >= '0' && token[2] <= '9')
00173       c += token[2] - '0';
00174       else if (token[2] >= 'A' && token[2] <= 'F')
00175       c += token[2] - ('A' - 10);
00176       else if (token[2] >= 'a' && token[2] <= 'f')
00177       c += token[2] - ('a' - 10);
00178       else
00179       {
00180         /* We got =xy where "x" was hex and "y" was not, so
00181          treat that as a literal "=", x, and y.  (But, if
00182          this bogus token happened to occur over a buffer
00183          boundary, we can't do this, since we don't have
00184          space for it.  Oh well.  Screw it.) */
00185         if (in > out) *out++ = token[0];
00186         if (in > out) *out++ = token[1];
00187         if (in > out) *out++ = token[2];
00188         continue;
00189       }
00190 
00191       *out++ = c ? (char) c : ((treatNullAsSpace) ? ' ' : (char) c);
00192     }
00193     else
00194     {
00195       *out++ = token[0];
00196 
00197       token[0] = token[1];
00198       token[1] = token[2];
00199       i = 2;
00200     }
00201   }
00202 
00203   /* Now that we've altered the data in place, write it. */
00204   if (out > buffer)
00205   return data->write_buffer (buffer, (out - buffer), data->closure);
00206   else
00207   return 1;
00208 }
00209 
00210 
00211 static int
00212 mime_decode_base64_token (const char *in, char *out)
00213 {
00214   /* reads 4, writes 0-3.  Returns bytes written.
00215    (Writes less than 3 only at EOF.) */
00216   int j;
00217   int eq_count = 0;
00218   unsigned long num = 0;
00219 
00220   for (j = 0; j < 4; j++)
00221   {
00222     unsigned char c = 0;
00223     if (in[j] >= 'A' && in[j] <= 'Z')     c = in[j] - 'A';
00224     else if (in[j] >= 'a' && in[j] <= 'z') c = in[j] - ('a' - 26);
00225     else if (in[j] >= '0' && in[j] <= '9') c = in[j] - ('0' - 52);
00226     else if (in[j] == '+')         c = 62;
00227     else if (in[j] == '/')         c = 63;
00228     else if (in[j] == '=')         c = 0, eq_count++;
00229     else
00230     NS_ASSERTION(0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00231     num = (num << 6) | c;
00232   }
00233 
00234   *out++ = (char) (num >> 16);
00235   *out++ = (char) ((num >> 8) & 0xFF);
00236   *out++ = (char) (num & 0xFF);
00237 
00238   if (eq_count == 0)
00239   return 3;        /* No "=" padding means 4 bytes mapped to 3. */
00240   else if (eq_count == 1)
00241   return 2;        /* "xxx=" means 3 bytes mapped to 2. */
00242   else if (eq_count == 2)
00243   return 1;        /* "xx==" means 2 bytes mapped to 1. */
00244   else
00245   {            /* "x===" can't happen, because "x" would then */
00246     NS_ASSERTION(0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");      /* be encoding only 6 bits, not the min of 8. */
00247     return 1;
00248   }
00249 }
00250 
00251 
00252 static int
00253 mime_decode_base64_buffer (MimeDecoderData *data,
00254                const char *buffer, PRInt32 length)
00255 {
00256   /* Warning, we are overwriting the buffer which was passed in.
00257    This is ok, because decoding these formats will never result
00258    in larger data than the input, only smaller. */
00259   const char *in  = buffer;
00260   char *out = (char *) buffer;
00261   char token [4];
00262   int i;
00263   EMBool leftover = (data->token_size > 0);
00264 
00265   NS_ASSERTION(data->encoding == mime_Base64, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00266 
00267   /* For the first pass, initialize the token from the unread-buffer. */
00268   i = 0;
00269   while (i < 4 && data->token_size > 0)
00270   {
00271     token [i] = data->token[i];
00272     data->token_size--;
00273     i++;
00274   }
00275 
00276   while (length > 0)
00277   {
00278     while (i < 4 && length > 0)
00279     {
00280       if ((*in >= 'A' && *in <= 'Z') ||
00281         (*in >= 'a' && *in <= 'z') ||
00282         (*in >= '0' && *in <= '9') ||
00283         *in == '+' || *in == '/' || *in == '=')
00284       token [i++] = *in;
00285       in++;
00286       length--;
00287     }
00288 
00289     if (i < 4)
00290     {
00291       /* Didn't get enough for a complete token. */
00292       memcpy (data->token, token, i);
00293       data->token_size = i;
00294       length = 0;
00295       break;
00296     }
00297     i = 0;
00298 
00299     if (leftover)
00300     {
00301       /* If there are characters left over from the last time around,
00302        we might not have space in the buffer to do our dirty work
00303        (if there were 2 or 3 left over, then there is only room for
00304        1 or 2 in the buffer right now, and we need 3.)  This is only
00305        a problem for the first chunk in each buffer, so in that
00306        case, just write prematurely. */
00307       int n;
00308       n = mime_decode_base64_token (token, token);
00309       n = data->write_buffer (token, n, data->closure);
00310       if (n < 0) /* abort */
00311       return n;
00312 
00313       /* increment buffer so that we don't write the 1 or 2 unused
00314        characters now at the front. */
00315       buffer = in;
00316       out = (char *) buffer;
00317 
00318       leftover = PR_FALSE;
00319     }
00320     else
00321     {
00322       int n = mime_decode_base64_token (token, out);
00323       /* Advance "out" by the number of bytes just written to it. */
00324       out += n;
00325     }
00326   }
00327 
00328   /* Now that we've altered the data in place, write it. */
00329   if (out > buffer)
00330   return data->write_buffer (buffer, (out - buffer), data->closure);
00331   else
00332   return 1;
00333 }
00334 
00335 
00336 static int
00337 mime_decode_uue_buffer (MimeDecoderData *data,
00338             const char *input_buffer, PRInt32 input_length)
00339 {
00340   /* First, copy input_buffer into state->line_buffer until we have
00341    a complete line.
00342 
00343    Then decode that line in place (in the line_buffer) and write
00344    it out.
00345 
00346    Then pull the next line into line_buffer and continue.
00347    */
00348   if (!data->line_buffer)
00349   {
00350     data->line_buffer_size = 128;
00351     data->line_buffer = (char *)PR_MALLOC(data->line_buffer_size);
00352     if (!data->line_buffer)
00353       return -1;
00354     data->line_buffer[0] = 0;
00355   }
00356 
00357   int status = 0;
00358   char *line = data->line_buffer;
00359   char *line_end = data->line_buffer + data->line_buffer_size - 1;
00360 
00361   NS_ASSERTION(data->encoding == mime_uuencode, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00362   if (data->encoding != mime_uuencode) return -1;
00363 
00364   if (data->ds_state == DS_END)
00365   {
00366     status = 0;
00367     goto DONE;
00368   }
00369 
00370   while (input_length > 0)
00371   {
00372     /* Copy data from input_buffer to `line' until we have a complete line,
00373      or until we've run out of input.
00374 
00375      (line may have data in it already if the last time we were called,
00376      we weren't called with a buffer that ended on a line boundary.)
00377      */
00378     {
00379     char *out = line + strlen(line);
00380     while (input_length > 0 &&
00381          out < line_end)
00382       {
00383       *out++ = *input_buffer++;
00384       input_length--;
00385 
00386       if (out[-1] == '\r' || out[-1] == '\n')
00387         {
00388         /* If we just copied a CR, and an LF is waiting, grab it too.
00389          */
00390         if (out[-1] == '\r' &&
00391           input_length > 0 &&
00392           *input_buffer == '\n')
00393           input_buffer++, input_length--;
00394 
00395         /* We have a line. */
00396         break;
00397         }
00398       }
00399     *out = 0;
00400 
00401     /* Ignore blank lines.
00402      */
00403     if (*line == '\r' || *line == '\n')
00404       {
00405       *line = 0;
00406       continue;
00407       }
00408 
00409     /* If this line was bigger than our buffer, truncate it.
00410        (This means the data was way corrupted, and there's basically
00411        no chance of decoding it properly, but give it a shot anyway.)
00412      */
00413     if (out == line_end)
00414       {
00415       out--;
00416       out[-1] = '\r';
00417       out[0] = 0;
00418       }
00419 
00420     /* If we didn't get a complete line, simply return; we'll be called
00421        with the rest of this line next time.
00422      */
00423     if (out[-1] != '\r' && out[-1] != '\n')
00424       {
00425       NS_ASSERTION (input_length == 0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00426       break;
00427       }
00428     }
00429 
00430 
00431     /* Now we have a complete line.  Deal with it.
00432      */
00433 
00434 
00435     if (data->ds_state == DS_BODY &&
00436       line[0] == 'e' &&
00437       line[1] == 'n' &&
00438       line[2] == 'd' &&
00439       (line[3] == '\r' ||
00440        line[3] == '\n'))
00441     {
00442       /* done! */
00443       data->ds_state = DS_END;
00444       *line = 0;
00445       break;
00446     }
00447     else if (data->ds_state == DS_BEGIN)
00448     {
00449       if (!strncmp (line, "begin ", 6))
00450       data->ds_state = DS_BODY;
00451       *line = 0;
00452       continue;
00453     }
00454     else
00455     {
00456       /* We're in DS_BODY.  Decode the line. */
00457       char *in, *out;
00458       PRInt32 i;
00459       long lost;
00460 
00461       NS_ASSERTION (data->ds_state == DS_BODY, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00462 
00463       /* We map down `line', reading four bytes and writing three.
00464        That means that `out' always stays safely behind `in'.
00465        */
00466       in = line;
00467       out = line;
00468 
00469 # undef DEC
00470 # define DEC(c) (((c) - ' ') & 077)
00471       i = DEC (*in); /* get length */
00472 
00473       /* all the parens and casts are because gcc was doing something evil.
00474        */
00475       lost = ((long) i) - (((((long) strlen (in)) - 2L) * 3L) / 4L);
00476 
00477       if (lost > 0) /* Short line!! */
00478       {
00479         /* If we get here, then the line is shorter than the length byte
00480          at the beginning says it should be.  However, the case where
00481          the line is short because it was at the end of the buffer and
00482          we didn't get the whole line was handled earlier (up by the
00483          "didn't get a complete line" comment.)  So if we've gotten
00484          here, then this is a complete line which is internally
00485          inconsistent.  We will parse from it what we can...
00486 
00487          This probably happened because some gateway stripped trailing
00488          whitespace from the end of the line -- so pretend the line
00489          was padded with spaces (which map to \000.)
00490          */
00491         i -= lost;
00492       }
00493 
00494       for (++in; i > 0; in += 4, i -= 3)
00495       {
00496         char ch;
00497         NS_ASSERTION(out <= in, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00498 
00499         if (i >= 3)
00500         {
00501           /* We read four; write three. */
00502           ch = DEC (in[0]) << 2 | DEC (in[1]) >> 4;
00503           *out++ = ch;
00504 
00505           NS_ASSERTION(out <= in+1, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00506 
00507           ch = DEC (in[1]) << 4 | DEC (in[2]) >> 2;
00508           *out++ = ch;
00509 
00510           NS_ASSERTION(out <= in+2, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00511 
00512           ch = DEC (in[2]) << 6 | DEC (in[3]);
00513           *out++ = ch;
00514 
00515           NS_ASSERTION(out <= in+3, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00516         }
00517         else
00518         {
00519           /* Handle a line that isn't a multiple of 4 long.
00520            (We read 1, 2, or 3, and will write 1 or 2.)
00521            */
00522           NS_ASSERTION (i > 0 && i < 3, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00523 
00524           ch = DEC (in[0]) << 2 | DEC (in[1]) >> 4;
00525           *out++ = ch;
00526 
00527           NS_ASSERTION(out <= in+1, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00528 
00529           if (i == 2)
00530           {
00531             ch = DEC (in[1]) << 4 | DEC (in[2]) >> 2;
00532             *out++ = ch;
00533 
00534             NS_ASSERTION(out <= in+2, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00535           }
00536         }
00537       }
00538 
00539       /* If the line was truncated, pad the missing bytes with 0 (SPC). */
00540       while (lost > 0)
00541       {
00542         *out++ = 0;
00543         lost--;
00544         in = out+1; /* just to prevent the assert, below. */
00545       }
00546 # undef DEC
00547 
00548       /* Now write out what we decoded for this line.
00549        */
00550       NS_ASSERTION(out >= line && out < in, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00551       if (out > line)
00552       status = data->write_buffer (line, (out - line), data->closure);
00553 
00554       /* Reset the line so that we don't think it's partial next time. */
00555       *line = 0;
00556 
00557       if (status < 0) /* abort */
00558       goto DONE;
00559     }
00560   }
00561 
00562   status = 1;
00563 
00564  DONE:
00565 
00566   return status;
00567 }
00568 
00569 static int
00570 mime_decode_yenc_buffer (MimeDecoderData *data,
00571             const char *input_buffer, PRInt32 input_length)
00572 {
00573   /* First, copy input_buffer into state->line_buffer until we have
00574    a complete line.
00575 
00576    Then decode that line in place (in the line_buffer) and write
00577    it out.
00578 
00579    Then pull the next line into line_buffer and continue.
00580    */
00581   if (!data->line_buffer)
00582   {
00583     data->line_buffer_size = 1000; // let make sure we have plenty of space for the header line
00584     data->line_buffer = (char *)PR_MALLOC(data->line_buffer_size);
00585     if (!data->line_buffer)
00586       return -1;
00587     data->line_buffer[0] = 0;
00588   }
00589 
00590   int status = 0;
00591   char *line = data->line_buffer;
00592   char *line_end = data->line_buffer + data->line_buffer_size - 1;
00593 
00594   NS_ASSERTION(data->encoding == mime_yencode, "wrong decoder!");
00595   if (data->encoding != mime_yencode) return -1;
00596 
00597   if (data->ds_state == DS_END)
00598     return 0;
00599 
00600   while (input_length > 0)
00601   {
00602     /* Copy data from input_buffer to `line' until we have a complete line,
00603        or until we've run out of input.
00604 
00605        (line may have data in it already if the last time we were called,
00606        we weren't called with a buffer that ended on a line boundary.)
00607     */
00608     {
00609       char *out = line + strlen(line);
00610       while (input_length > 0 && out < line_end)
00611       {
00612         *out++ = *input_buffer++;
00613         input_length--;
00614 
00615         if (out[-1] == '\r' || out[-1] == '\n')
00616         {
00617           /* If we just copied a CR, and an LF is waiting, grab it too. */
00618           if (out[-1] == '\r' &&
00619                   input_length > 0 &&
00620                   *input_buffer == '\n')
00621             input_buffer++, input_length--;
00622 
00623            /* We have a line. */
00624            break;
00625         }
00626       }
00627       *out = 0;
00628 
00629       /* Ignore blank lines. */
00630       if (*line == '\r' || *line == '\n')
00631       {
00632         *line = 0;
00633         continue;
00634       }
00635 
00636       /* If this line was bigger than our buffer, truncate it.
00637          (This means the data was way corrupted, and there's basically
00638          no chance of decoding it properly, but give it a shot anyway.)
00639       */
00640       if (out == line_end)
00641       {
00642         out--;
00643         out[-1] = '\r';
00644         out[0] = 0;
00645       }
00646 
00647       /* If we didn't get a complete line, simply return; we'll be called
00648          with the rest of this line next time.
00649       */
00650       if (out[-1] != '\r' && out[-1] != '\n')
00651       {
00652         NS_ASSERTION (input_length == 0, "empty buffer!");
00653         break;
00654       }
00655     }
00656 
00657 
00658     /* Now we have a complete line.  Deal with it.
00659      */
00660     const char * endOfLine = line + strlen(line);
00661 
00662     if (data->ds_state == DS_BEGIN)
00663     {
00664       int new_line_size = 0;
00665       /* this yenc decoder does not support yenc v2 or multipart yenc.
00666          Therefore, we are looking first for "=ybegin line="
00667       */
00668       if ((endOfLine - line) >= 13 && !strncmp (line, "=ybegin line=", 13))
00669       {
00670         /* ...then couple digits. */
00671         for (line += 13; line < endOfLine; line ++)
00672         {
00673           if (*line < '0' || *line > '9')
00674             break;
00675           new_line_size = (new_line_size * 10) + *line - '0';
00676         }
00677 
00678         /* ...next, look for <space>size= */
00679         if ((endOfLine - line) >= 6 && !strncmp (line, " size=", 6))
00680         {
00681           /* ...then couple digits. */
00682           for (line += 6; line < endOfLine; line ++)
00683             if (*line < '0' || *line > '9')
00684               break;
00685 
00686           /* ...next, look for <space>name= */
00687           if ((endOfLine - line) >= 6 && !strncmp (line, " name=", 6))
00688           {
00689             /* we have found the yenc header line.
00690                Now check if we need to grow our buffer line
00691             */
00692             data->ds_state = DS_BODY;
00693             if (new_line_size > data->line_buffer_size && new_line_size <= 997) /* don't let bad value hurt us! */
00694             {
00695               PR_Free(data->line_buffer);
00696               data->line_buffer_size = new_line_size + 4; //extra chars for line ending and potential escape char
00697               data->line_buffer = (char *)PR_MALLOC(data->line_buffer_size);
00698               if (!data->line_buffer)
00699                 return -1;
00700             }
00701           }
00702         }
00703 
00704       }
00705       *data->line_buffer = 0;
00706       continue;
00707     }
00708 
00709     if (data->ds_state == DS_BODY && line[0] == '=')
00710     {
00711       /* look if this this the final line */
00712       if (!strncmp (line, "=yend size=", 11))
00713       {
00714         /* done! */
00715         data->ds_state = DS_END;
00716         *line = 0;
00717         break;
00718       }
00719     }
00720 
00721     /* We're in DS_BODY.  Decode the line in place. */
00722     {
00723       char *src = line;
00724       char *dest = src;
00725       char c;
00726       for (; src < line_end; src ++)
00727       {
00728         c = *src;
00729         if (!c || c == '\r' || c == '\n')
00730           break;
00731 
00732         if (c == '=')
00733         {
00734           src++;
00735           c = *src;
00736           if (c == 0)
00737             return -1;  /* last character cannot be escape char */
00738           c -= 64;
00739         }
00740         c -= 42;
00741         *dest = c;
00742         dest ++;
00743       }
00744 
00745       /* Now write out what we decoded for this line. */
00746       NS_ASSERTION(dest >= line && dest < src, "nothing to write!");
00747       if (dest > line)
00748       {
00749         status = data->write_buffer (line, dest - line, data->closure);
00750         if (status < 0) /* abort */
00751           return status;
00752       }
00753 
00754       /* Reset the line so that we don't think it's partial next time. */
00755       *line = 0;
00756     }
00757   }
00758 
00759   return 1;
00760 }
00761 
00762 int
00763 MimeDecoderDestroy (MimeDecoderData *data, EMBool abort_p)
00764 {
00765   int status = 0;
00766   /* Flush out the last few buffered characters. */
00767   if (!abort_p &&
00768     data->token_size > 0 &&
00769     data->token[0] != '=')
00770   {
00771     if (data->encoding == mime_Base64)
00772     while ((unsigned int)data->token_size < sizeof (data->token))
00773       data->token [data->token_size++] = '=';
00774 
00775     status = data->write_buffer (data->token, data->token_size,
00776                    data->closure);
00777   }
00778 
00779   if (data->line_buffer)
00780     PR_Free(data->line_buffer);
00781   PR_Free (data);
00782   return status;
00783 }
00784 
00785 
00786 static MimeDecoderData *
00787 mime_decoder_init (mime_encoding which,
00788            nsresult (*output_fn) (const char *, PRInt32, void *),
00789            void *closure)
00790 {
00791   MimeDecoderData *data = PR_NEW(MimeDecoderData);
00792   if (!data) return 0;
00793   memset(data, 0, sizeof(*data));
00794   data->encoding = which;
00795   data->write_buffer = output_fn;
00796   data->closure = closure;
00797   data->line_buffer_size = 0;
00798   data->line_buffer = nsnull;
00799 
00800   return data;
00801 }
00802 
00803 MimeDecoderData *
00804 MimeB64DecoderInit (nsresult (*output_fn) (const char *, PRInt32, void *),
00805           void *closure)
00806 {
00807   return mime_decoder_init (mime_Base64, output_fn, closure);
00808 }
00809 
00810 MimeDecoderData *
00811 MimeQPDecoderInit (nsresult (*output_fn) (const char *, PRInt32, void *),
00812            void *closure, MimeObject *object)
00813 {
00814   MimeDecoderData *retData = mime_decoder_init (mime_QuotedPrintable, output_fn, closure);
00815   if (retData)
00816     retData->objectToDecode = object;
00817   return retData;
00818 }
00819 
00820 MimeDecoderData *
00821 MimeUUDecoderInit (nsresult (*output_fn) (const char *, PRInt32, void *),
00822            void *closure)
00823 {
00824   return mime_decoder_init (mime_uuencode, output_fn, closure);
00825 }
00826 
00827 MimeDecoderData *
00828 MimeYDecoderInit (nsresult (*output_fn) (const char *, PRInt32, void *),
00829            void *closure)
00830 {
00831   return mime_decoder_init (mime_yencode, output_fn, closure);
00832 }
00833 
00834 int
00835 MimeDecoderWrite (MimeDecoderData *data, const char *buffer, PRInt32 size)
00836 {
00837   NS_ASSERTION(data, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00838   if (!data) return -1;
00839   switch(data->encoding)
00840   {
00841   case mime_Base64:
00842     return mime_decode_base64_buffer (data, buffer, size);
00843   case mime_QuotedPrintable:
00844     return mime_decode_qp_buffer (data, buffer, size);
00845   case mime_uuencode:
00846     return mime_decode_uue_buffer (data, buffer, size);
00847   case mime_yencode:
00848     return mime_decode_yenc_buffer (data, buffer, size);
00849   default:
00850     NS_ASSERTION(0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
00851     return -1;
00852   }
00853 }
00854 
00855 
00856 
00857 /* ================== Encoders.
00858  */
00859 
00860 struct MimeEncoderData {
00861   mime_encoding encoding;    /* Which encoding to use */
00862 
00863   /* Buffer for the base64 encoder. */
00864   unsigned char in_buffer[3];
00865   PRInt32 in_buffer_count;
00866 
00867   /* Buffer for uuencoded data. (Need a line because of the length byte.) */
00868   unsigned char uue_line_buf[128];
00869   EMBool uue_wrote_begin;
00870 
00871   PRInt32 current_column, line_byte_count;
00872 
00873   char *filename; /* filename for use with uuencoding */
00874 
00875   /* Where to write the encoded data */
00876   nsresult (*write_buffer) (const char *buf, PRInt32 size, void *closure);
00877   void *closure;
00878 };
00879 
00880 /* Use what looks like a nice, safe value for a standard uue line length */
00881 #define UUENCODE_LINE_LIMIT 60
00882 
00883 #undef ENC
00884 #define ENC(c) ((c & 0x3F) + ' ')
00885 
00886 void
00887 mime_uuencode_write_line(MimeEncoderData *data)
00888 {
00889   /* Set the length byte at the beginning:
00890      encoded (data->line_byte_count). */
00891   data->uue_line_buf[0] = ENC(data->line_byte_count);
00892 
00893   /* Tack a CRLF onto the end. */
00894   data->uue_line_buf[data->current_column++] = '\r';
00895   data->uue_line_buf[data->current_column++] = '\n';
00896 
00897   /* Write the line to output. */
00898   data->write_buffer((const char*)data->uue_line_buf, data->current_column,
00899              data->closure);
00900 
00901   /* Reset data based on having just written a complete line. */
00902   data->in_buffer_count = 0;
00903   data->line_byte_count = 0;
00904   data->current_column = 1;
00905 }
00906 
00907 void
00908 mime_uuencode_convert_triplet(MimeEncoderData *data)
00909 {
00910   /*
00911      If we have 3 bytes, encode them and add them to the current
00912      line. The way we want to encode them is like this
00913      (each digit corresponds to a bit in the binary source):
00914      11111111 -> 00111111 + ' ' (six highest bits of 1)
00915      22222222    00112222 + ' ' (low 2 of 1, high 4 of 2)
00916      33333333    00222233 + ' ' (low 4 of 2, high 2 of 3)
00917                  00333333 + ' ' (low 6 of 3)
00918   */
00919   char outData[4];
00920   int i;
00921 
00922   outData[0] = data->in_buffer[0] >> 2;
00923 
00924   outData[1] = ((data->in_buffer[0] << 4) & 0x30);
00925   outData[1] |= data->in_buffer[1] >> 4;
00926 
00927   outData[2] = ((data->in_buffer[1] << 2) & 0x3C);
00928   outData[2] |= data->in_buffer[2] >> 6;
00929 
00930   outData[3] = data->in_buffer[2] & 0x3F;
00931 
00932   for(i=0;i<4;i++)
00933     data->uue_line_buf[data->current_column++] = ENC(outData[i]);
00934 
00935   data->in_buffer_count = 0;
00936 }
00937 
00938 int
00939 mime_uuencode_buffer(MimeEncoderData *data,
00940              const char *buffer, PRInt32 size)
00941 {
00942   /* If this is the first time through, write a begin statement. */
00943   if (!(data->uue_wrote_begin))
00944   {
00945     char firstLine[256];
00946     PR_snprintf(firstLine, sizeof(firstLine), "begin 644 %s\015\012", data->filename ? data->filename : "");
00947     data->write_buffer(firstLine, strlen(firstLine), data->closure);
00948     data->uue_wrote_begin = PR_TRUE;
00949     data->current_column = 1; /* initialization unique to uuencode */
00950   }
00951 
00952   /* Pick up where we left off. */
00953   while(size > 0)
00954   {
00955     /* If we've reached the end of a line, write the line out. */
00956     if (data->current_column >= UUENCODE_LINE_LIMIT)
00957     {
00958       /* End of a line. Write the line out. */
00959       mime_uuencode_write_line(data);
00960     }
00961 
00962     /* Get the next 3 bytes if we have them, or whatever we can get. */
00963     while(size > 0 && data->in_buffer_count < 3)
00964     {
00965       data->in_buffer[data->in_buffer_count++] = *(buffer++);
00966       size--; data->line_byte_count++;
00967     }
00968 
00969     if (data->in_buffer_count == 3)
00970     {
00971       mime_uuencode_convert_triplet(data);
00972     }
00973   }
00974   return 0;
00975 }
00976 
00977 int
00978 mime_uuencode_finish(MimeEncoderData *data)
00979 {
00980   int i;
00981   static const char *endStr = "end\015\012";
00982 
00983   /* If we have converted binary data to write to output, do it now. */
00984   if (data->line_byte_count > 0)
00985   {
00986     /* If we have binary data yet to be converted,
00987        pad and convert it. */
00988     if (data->in_buffer_count > 0)
00989     {
00990       for(i=data->in_buffer_count;i<3;i++)
00991         data->in_buffer[i] = '\0'; /* pad with zeroes */
00992 
00993       mime_uuencode_convert_triplet(data);
00994     }
00995 
00996     mime_uuencode_write_line(data);
00997   }
00998 
00999   /* Write 'end' on a line by itself. */
01000   return data->write_buffer(endStr, strlen(endStr), data->closure);
01001 }
01002 
01003 #undef ENC
01004 
01005 int
01006 mime_encode_base64_buffer (MimeEncoderData *data,
01007                const char *buffer, PRInt32 size)
01008 {
01009   int status = 0;
01010   const unsigned char *in = (unsigned char *) buffer;
01011   const unsigned char *end = in + size;
01012   char out_buffer[80];
01013   char *out = out_buffer;
01014   PRUint32 i = 0, n = 0;
01015   PRUint32 off;
01016 
01017   if (size == 0)
01018   return 0;
01019   else if (size < 0)
01020   {
01021     NS_ASSERTION(0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
01022     return -1;
01023   }
01024 
01025 
01026   /* If this input buffer is too small, wait until next time. */
01027   if (size < (3 - data->in_buffer_count))
01028   {
01029     NS_ASSERTION(size < 3 && size > 0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
01030     data->in_buffer[data->in_buffer_count++] = buffer[0];
01031     if (size > 1)
01032     data->in_buffer[data->in_buffer_count++] = buffer[1];
01033     NS_ASSERTION(data->in_buffer_count < 3, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
01034     return 0;
01035   }
01036 
01037 
01038   /* If there are bytes that were put back last time, take them now.
01039    */
01040   i = 0;
01041   if (data->in_buffer_count > 0) n = data->in_buffer[0];
01042   if (data->in_buffer_count > 1) n = (n << 8) + data->in_buffer[1];
01043   i = data->in_buffer_count;
01044   data->in_buffer_count = 0;
01045 
01046   /* If this buffer is not a multiple of three, put one or two bytes back.
01047    */
01048   off = ((size + i) % 3);
01049   if (off)
01050   {
01051     data->in_buffer[0] = buffer [size - off];
01052     if (off > 1)
01053     data->in_buffer [1] = buffer [size - off + 1];
01054     data->in_buffer_count = off;
01055     size -= off;
01056     NS_ASSERTION (! ((size + i) % 3), "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
01057     end = (unsigned char *) (buffer + size);
01058   }
01059 
01060   /* Populate the out_buffer with base64 data, one line at a time.
01061    */
01062   while (in < end)
01063   {
01064     PRInt32 j;
01065 
01066     while (i < 3)
01067     {
01068       n = (n << 8) | *in++;
01069       i++;
01070     }
01071     i = 0;
01072 
01073     for (j = 18; j >= 0; j -= 6)
01074     {
01075       unsigned int k = (n >> j) & 0x3F;
01076       if (k < 26)       *out++ = k      + 'A';
01077       else if (k < 52)  *out++ = k - 26 + 'a';
01078       else if (k < 62)  *out++ = k - 52 + '0';
01079       else if (k == 62) *out++ = '+';
01080       else if (k == 63) *out++ = '/';
01081       else abort ();
01082     }
01083 
01084     data->current_column += 4;
01085     if (data->current_column >= 72)
01086     {
01087       /* Do a linebreak before column 76.  Flush out the line buffer. */
01088       data->current_column = 0;
01089       *out++ = '\015';
01090       *out++ = '\012';
01091       status = data->write_buffer (out_buffer, (out - out_buffer),
01092                      data->closure);
01093       out = out_buffer;
01094       if (status < 0) return status;
01095     }
01096   }
01097 
01098   /* Write out the unwritten portion of the last line buffer. */
01099   if (out > out_buffer)
01100   {
01101     status = data->write_buffer (out_buffer, (out - out_buffer),
01102                    data->closure);
01103     if (status < 0) return status;
01104   }
01105 
01106   return 0;
01107 }
01108 
01109 
01110 int
01111 mime_encode_qp_buffer (MimeEncoderData *data, const char *buffer, PRInt32 size)
01112 {
01113   int status = 0;
01114   static const char hexdigits[] = "0123456789ABCDEF";
01115   const unsigned char *in = (unsigned char *) buffer;
01116   const unsigned char *end = in + size;
01117   char out_buffer[80];
01118   char *out = out_buffer;
01119   EMBool white = PR_FALSE;
01120   EMBool mb_p = PR_FALSE;
01121 
01122   /*
01123   #### I don't know how to hook this back up:
01124   ####  mb_p = INTL_DefaultWinCharSetID(state->context) & 0x300 ;
01125   */
01126 
01127 
01128   NS_ASSERTION(data->in_buffer_count == 0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
01129 
01130   /* Populate the out_buffer with quoted-printable data, one line at a time.
01131   */
01132   for (; in < end; in++)
01133   {
01134     if (*in == '\r' || *in == '\n')
01135     {
01136 
01137     /* Whitespace cannot be allowed to occur at the end of
01138     the line, so we backup and replace the whitespace with
01139     its code.
01140       */
01141       if (white)
01142       {
01143         out--;
01144         char whitespace_char = *out;
01145         *out++ = '=';
01146         *out++ = hexdigits[whitespace_char >> 4];
01147         *out++ = hexdigits[whitespace_char & 0xF];
01148       }
01149 
01150       /* Now write out the newline. */
01151       *out++ = '\r';
01152       *out++ = '\n';
01153       white = PR_FALSE;
01154 
01155       status = data->write_buffer (out_buffer, (out - out_buffer),
01156         data->closure);
01157       if (status < 0) return status;
01158       out = out_buffer;
01159 
01160       /* If it's CRLF, swallow two chars instead of one. */
01161       if (in[0] == '\r' && in[1] == '\n')
01162         in++;
01163 
01164       out = out_buffer;
01165       white = PR_FALSE;
01166       data->current_column = 0;
01167     }
01168     else if (data->current_column == 0 && *in == '.')
01169     {
01170       /* Just to be SMTP-safe, if "." appears in column 0, encode it.
01171                   (mmencode does this too.)
01172       */
01173       goto HEX;
01174     }
01175     else if (data->current_column == 0 && *in == 'F'
01176          && (in >= end-1 || in[1] == 'r')
01177                            && (in >= end-2 || in[2] == 'o')
01178                            && (in >= end-3 || in[3] == 'm')
01179                            && (in >= end-4 || in[4] == ' '))
01180     {
01181       /* If this line begins with 'F' and we cannot determine that
01182                   this line does not begin with "From " then do the safe thing
01183                   and assume that it does, and encode the 'F' in hex to avoid
01184                   BSD mailbox lossage.  (We might not be able to tell that it
01185                   is really "From " if the end of the buffer was early.  So
01186                   this means that "\nFoot" will have the F encoded if the end of
01187                   the buffer happens to fall just after the F; but will not have
01188                   it encoded if it's after the first "o" or later.  Oh well.
01189                   It's a little inconsistent, but it errs on the safe side.)
01190       */
01191       goto HEX;
01192     }
01193     else if ((*in >= 33 && *in <= 60) ||    /* safe printing chars */
01194          (*in >= 62 && *in <= 126) ||
01195                            (mb_p && (*in == 61 || *in == 127 || *in == 0x1B)))
01196     {
01197       white = PR_FALSE;
01198       *out++ = *in;
01199       data->current_column++;
01200     }
01201     else if (*in == ' ' || *in == '\t')    /* whitespace */
01202     {
01203       white = PR_TRUE;
01204       *out++ = *in;
01205       data->current_column++;
01206     }
01207     else                    /* print as =FF */
01208     {
01209 HEX:
01210       white = PR_FALSE;
01211                   *out++ = '=';
01212                   *out++ = hexdigits[*in >> 4];
01213                   *out++ = hexdigits[*in & 0xF];
01214                   data->current_column += 3;
01215     }
01216 
01217     NS_ASSERTION (data->current_column <= 76, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00"); /* Hard limit required by spec */
01218 
01219     if (data->current_column >= 73)    /* soft line break: "=\r\n" */
01220     {
01221       *out++ = '=';
01222       *out++ = '\r';
01223       *out++ = '\n';
01224 
01225       status = data->write_buffer (out_buffer, (out - out_buffer),
01226         data->closure);
01227       if (status < 0) return status;
01228       out = out_buffer;
01229       white = PR_FALSE;
01230       data->current_column = 0;
01231     }
01232   }
01233 
01234   /* Write out the unwritten portion of the last line buffer. */
01235   if (out > out_buffer)
01236   {
01237     status = data->write_buffer (out_buffer, (out - out_buffer),
01238       data->closure);
01239     if (status < 0) return status;
01240   }
01241 
01242   return 0;
01243 }
01244 
01245 
01246 
01247 int
01248 MimeEncoderDestroy (MimeEncoderData *data, EMBool abort_p)
01249 {
01250   int status = 0;
01251 
01252   /* If we're uuencoding, we have our own finishing routine. */
01253   if (data->encoding == mime_uuencode)
01254    mime_uuencode_finish(data);
01255 
01256   /* Since Base64 (and uuencode) output needs to do some buffering to get
01257    a multiple of three bytes on each block, there may be a few bytes
01258    left in the buffer after the last block has been written.  We need to
01259    flush those out now.
01260    */
01261 
01262   NS_ASSERTION (data->encoding == mime_Base64 ||
01263        data->in_buffer_count == 0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
01264 
01265   if (!abort_p &&
01266     data->in_buffer_count > 0)
01267   {
01268     char buf2 [6];
01269     char *buf = buf2 + 2;
01270     char *out = buf;
01271     int j;
01272     /* fixed bug 55998, 61302, 61866
01273      * type casting to PRUint32 before shifting
01274      */
01275     PRUint32 n = ((PRUint32) data->in_buffer[0]) << 16;
01276     if (data->in_buffer_count > 1)
01277     n = n | (((PRUint32) data->in_buffer[1]) << 8);
01278 
01279     buf2[0] = '\r';
01280     buf2[1] = '\n';
01281 
01282     for (j = 18; j >= 0; j -= 6)
01283     {
01284       unsigned int k = (n >> j) & 0x3F;
01285       if (k < 26)       *out++ = k      + 'A';
01286       else if (k < 52)  *out++ = k - 26 + 'a';
01287       else if (k < 62)  *out++ = k - 52 + '0';
01288       else if (k == 62) *out++ = '+';
01289       else if (k == 63) *out++ = '/';
01290       else abort ();
01291     }
01292 
01293     /* Pad with equal-signs. */
01294     if (data->in_buffer_count == 1)
01295     buf[2] = '=';
01296     buf[3] = '=';
01297 
01298     if (data->current_column >= 72)
01299     status = data->write_buffer (buf2, 6, data->closure);
01300     else
01301     status = data->write_buffer (buf,  4, data->closure);
01302   }
01303 
01304   PR_FREEIF(data->filename);
01305   PR_Free (data);
01306   return status;
01307 }
01308 
01309 
01310 static MimeEncoderData *
01311 mime_encoder_init (mime_encoding which,
01312            nsresult (*output_fn) (const char *, PRInt32, void *),
01313            void *closure)
01314 {
01315   MimeEncoderData *data = PR_NEW(MimeEncoderData);
01316   if (!data) return 0;
01317   memset(data, 0, sizeof(*data));
01318   data->encoding = which;
01319   data->write_buffer = output_fn;
01320   data->closure = closure;
01321   return data;
01322 }
01323 
01324 MimeEncoderData *
01325 MimeB64EncoderInit (nsresult (*output_fn) (const char *, PRInt32, void *),
01326           void *closure)
01327 {
01328   return mime_encoder_init (mime_Base64, output_fn, closure);
01329 }
01330 
01331 MimeEncoderData *
01332 MimeQPEncoderInit (nsresult (*output_fn) (const char *, PRInt32, void *),
01333            void *closure)
01334 {
01335   return mime_encoder_init (mime_QuotedPrintable, output_fn, closure);
01336 }
01337 
01338 MimeEncoderData *
01339 MimeUUEncoderInit (char *filename,
01340           nsresult (*output_fn) (const char *, PRInt32, void *),
01341           void *closure)
01342 {
01343   MimeEncoderData *enc = mime_encoder_init (mime_uuencode, output_fn, closure);
01344 
01345   if (filename)
01346     enc->filename = strdup(filename);
01347 
01348   return enc;
01349 }
01350 
01351 int
01352 MimeEncoderWrite (MimeEncoderData *data, const char *buffer, PRInt32 size)
01353 {
01354   NS_ASSERTION(data, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
01355   if (!data) return -1;
01356   switch(data->encoding)
01357   {
01358   case mime_Base64:
01359     return mime_encode_base64_buffer (data, buffer, size);
01360   case mime_QuotedPrintable:
01361     return mime_encode_qp_buffer (data, buffer, size);
01362   case mime_uuencode:
01363     return mime_uuencode_buffer(data, buffer, size);
01364   default:
01365     NS_ASSERTION(0, "1.1 <rhp@netscape.com> 19 Mar 1999 12:00");
01366     return -1;
01367   }
01368 }