Back to index

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