Back to index

lightning-sunbird  0.9+nobinonly
mimemult.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 
00038 #include "msgCore.h"
00039 #include "mimemult.h"
00040 #include "mimemoz2.h"
00041 #include "mimeeobj.h"
00042 
00043 #include "prlog.h"
00044 #include "prmem.h"
00045 #include "plstr.h"
00046 #include "prio.h"
00047 #include "nsMimeStringResources.h"
00048 #include "nsMimeTypes.h"
00049 
00050 #if defined(XP_MAC) || defined(XP_MACOSX)
00051   extern MimeObjectClass mimeMultipartAppleDoubleClass;
00052 #endif
00053 
00054 #define MIME_SUPERCLASS mimeContainerClass
00055 MimeDefClass(MimeMultipart, MimeMultipartClass,
00056                       mimeMultipartClass, &MIME_SUPERCLASS);
00057 
00058 static int MimeMultipart_initialize (MimeObject *);
00059 static void MimeMultipart_finalize (MimeObject *);
00060 static int MimeMultipart_parse_line (char *line, PRInt32 length, MimeObject *);
00061 static int MimeMultipart_parse_eof (MimeObject *object, PRBool abort_p);
00062 
00063 static MimeMultipartBoundaryType MimeMultipart_check_boundary(MimeObject *,
00064                                                                                                            const char *,
00065                                                                                                            PRInt32);
00066 static int MimeMultipart_create_child(MimeObject *);
00067 static PRBool MimeMultipart_output_child_p(MimeObject *, MimeObject *);
00068 static int MimeMultipart_parse_child_line (MimeObject *, char *, PRInt32,
00069                                                                          PRBool);
00070 static int MimeMultipart_close_child(MimeObject *);
00071 
00072 extern "C" MimeObjectClass mimeMultipartAlternativeClass;
00073 extern "C" MimeObjectClass mimeMultipartRelatedClass;
00074 extern "C" MimeObjectClass mimeMultipartSignedClass;
00075 extern "C" MimeObjectClass mimeInlineTextVCardClass;
00076 extern "C" MimeExternalObjectClass mimeExternalObjectClass;
00077 
00078 #if defined(DEBUG) && defined(XP_UNIX)
00079 static int MimeMultipart_debug_print (MimeObject *, PRFileDesc *, PRInt32);
00080 #endif
00081 
00082 static int
00083 MimeMultipartClassInitialize(MimeMultipartClass *clazz)
00084 {
00085   MimeObjectClass    *oclass = (MimeObjectClass *)    clazz;
00086   MimeMultipartClass *mclass = (MimeMultipartClass *) clazz;
00087 
00088   PR_ASSERT(!oclass->class_initialized);
00089   oclass->initialize  = MimeMultipart_initialize;
00090   oclass->finalize    = MimeMultipart_finalize;
00091   oclass->parse_line  = MimeMultipart_parse_line;
00092   oclass->parse_eof   = MimeMultipart_parse_eof;
00093 
00094   mclass->check_boundary   = MimeMultipart_check_boundary;
00095   mclass->create_child     = MimeMultipart_create_child;
00096   mclass->output_child_p   = MimeMultipart_output_child_p;
00097   mclass->parse_child_line = MimeMultipart_parse_child_line;
00098   mclass->close_child      = MimeMultipart_close_child;
00099 
00100 #if defined(DEBUG) && defined(XP_UNIX)
00101   oclass->debug_print = MimeMultipart_debug_print;
00102 #endif
00103 
00104   return 0;
00105 }
00106 
00107 
00108 static int
00109 MimeMultipart_initialize (MimeObject *object)
00110 {
00111   MimeMultipart *mult = (MimeMultipart *) object;
00112   char *ct;
00113 
00114   /* This is an abstract class; it shouldn't be directly instantiated. */
00115   PR_ASSERT(object->clazz != (MimeObjectClass *) &mimeMultipartClass);
00116 
00117   ct = MimeHeaders_get (object->headers, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE);
00118   mult->boundary = (ct
00119                                    ? MimeHeaders_get_parameter (ct, HEADER_PARM_BOUNDARY, NULL, NULL)
00120                                    : 0);
00121   PR_FREEIF(ct);
00122   mult->state = MimeMultipartPreamble;
00123   return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
00124 }
00125 
00126 
00127 static void
00128 MimeMultipart_finalize (MimeObject *object)
00129 {
00130   MimeMultipart *mult = (MimeMultipart *) object;
00131 
00132   object->clazz->parse_eof(object, PR_FALSE);
00133 
00134   PR_FREEIF(mult->boundary);
00135   if (mult->hdrs)
00136        MimeHeaders_free(mult->hdrs);
00137   mult->hdrs = 0;
00138   ((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(object);
00139 }
00140 
00141 int MimeWriteAString(MimeObject *obj, const nsACString &string)
00142 {
00143   const nsCString &flatString = PromiseFlatCString(string);
00144   return MimeObject_write(obj, flatString.get(), flatString.Length(), PR_TRUE);
00145 }
00146 
00147 static int
00148 MimeMultipart_parse_line (char *line, PRInt32 length, MimeObject *obj)
00149 {
00150   MimeMultipart *mult = (MimeMultipart *) obj;
00151   int status = 0;
00152   MimeMultipartBoundaryType boundary;
00153 
00154   PR_ASSERT(line && *line);
00155   if (!line || !*line) return -1;
00156 
00157   PR_ASSERT(!obj->closed_p);
00158   if (obj->closed_p) return -1;
00159 
00160   /* If we're supposed to write this object, but aren't supposed to convert
00161      it to HTML, simply pass it through unaltered. */
00162   if (obj->output_p &&
00163          obj->options &&
00164          !obj->options->write_html_p &&
00165          obj->options->output_fn
00166           && obj->options->format_out != nsMimeOutput::nsMimeMessageAttach)
00167        return MimeObject_write(obj, line, length, PR_TRUE);
00168 
00169 
00170   if (mult->state == MimeMultipartEpilogue)  /* already done */
00171     boundary = MimeMultipartBoundaryTypeNone;
00172   else
00173     boundary = ((MimeMultipartClass *)obj->clazz)->check_boundary(obj, line,
00174                                                                   length);
00175 
00176   if (boundary == MimeMultipartBoundaryTypeTerminator ||
00177     boundary == MimeMultipartBoundaryTypeSeparator)
00178   {
00179   /* Match!  Close the currently-open part, move on to the next
00180      state, and discard this line.
00181    */
00182     PRBool endOfPart = (mult->state != MimeMultipartPreamble);
00183     if (endOfPart)
00184       status = ((MimeMultipartClass *)obj->clazz)->close_child(obj);
00185     if (status < 0) return status;
00186     
00187     if (boundary == MimeMultipartBoundaryTypeTerminator)
00188       mult->state = MimeMultipartEpilogue;
00189     else
00190     {
00191       mult->state = MimeMultipartHeaders;
00192       
00193       /* Reset the header parser for this upcoming part. */
00194       PR_ASSERT(!mult->hdrs);
00195       if (mult->hdrs)
00196         MimeHeaders_free(mult->hdrs);
00197       mult->hdrs = MimeHeaders_new();
00198       if (!mult->hdrs)
00199         return MIME_OUT_OF_MEMORY;
00200       if (obj->options->state->partsToStrip.Count() > 0)
00201       {
00202         nsCAutoString newPart(mime_part_address(obj));
00203         MimeContainer *container = (MimeContainer*) obj; 
00204         newPart.Append('.');
00205         newPart.AppendInt(container->nchildren + 1);
00206         obj->options->state->strippingPart = PR_FALSE;
00207         // check if this is a sub-part of a part we're stripping.
00208         for (PRInt32 partIndex = 0; partIndex < obj->options->state->partsToStrip.Count(); partIndex++)
00209         {
00210           nsCString *curPartToStrip = obj->options->state->partsToStrip.CStringAt(partIndex);
00211           if (newPart.Find(*curPartToStrip) == 0 && (newPart.Length() == curPartToStrip->Length() || newPart.CharAt(curPartToStrip->Length()) == '.'))
00212           {
00213             obj->options->state->strippingPart = PR_TRUE;
00214             if (partIndex < obj->options->state->detachToFiles.Count())
00215               obj->options->state->detachedFilePath = *obj->options->state->detachToFiles.CStringAt(partIndex);
00216             break;
00217           }
00218         }
00219       }
00220     }
00221     
00222     // if stripping out attachments, write the boundary line. Otherwise, return
00223     // to ignore it.
00224     if (obj->options->format_out == nsMimeOutput::nsMimeMessageAttach)
00225     {
00226       // Because MimeMultipart_parse_child_line strips out the 
00227       // the CRLF of the last line before the end of a part, we need to add that
00228       // back in here.
00229       if (endOfPart)
00230         MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK));
00231 
00232       status = MimeObject_write(obj, line, length, PR_TRUE);
00233     }
00234     return 0;
00235   }
00236 
00237   /* Otherwise, this isn't a boundary string.  So do whatever it is we
00238         should do with this line (parse it as a header, feed it to the
00239         child part, ignore it, etc.) */
00240 
00241   switch (mult->state)
00242   {
00243     case MimeMultipartPreamble:
00244     case MimeMultipartEpilogue:
00245       /* Ignore this line. */
00246       break;
00247 
00248     case MimeMultipartHeaders:
00249     /* Parse this line as a header for the sub-part. */
00250     {
00251       status = MimeHeaders_parse_line(line, length, mult->hdrs);
00252       if (status < 0) return status;
00253       
00254       // If this line is blank, we're now done parsing headers, and should
00255       // now examine the content-type to create this "body" part.
00256       //
00257       if (*line == nsCRT::CR || *line == nsCRT::LF)
00258       {
00259         if (obj->options->state->strippingPart)
00260         {
00261           PRBool detachingPart = obj->options->state->detachedFilePath.Length() > 0;
00262 
00263           nsCAutoString fileName;
00264           fileName.Adopt(MimeHeaders_get_name(mult->hdrs, obj->options));
00265           if (detachingPart)
00266           {
00267             char *contentType = MimeHeaders_get(mult->hdrs, "Content-Type", PR_FALSE, PR_FALSE);
00268             if (contentType)
00269             {
00270               MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Type: "));
00271               MimeWriteAString(obj, nsDependentCString(contentType));
00272               PR_Free(contentType);
00273             }
00274             MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK));
00275             MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Disposition: attachment; filename=\""));
00276             MimeWriteAString(obj, fileName);
00277             MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK));
00278             MimeWriteAString(obj, NS_LITERAL_CSTRING("X-Mozilla-External-Attachment-URL: "));
00279             MimeWriteAString(obj, obj->options->state->detachedFilePath);
00280             MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK));
00281             MimeWriteAString(obj, NS_LITERAL_CSTRING("X-Mozilla-Altered: AttachmentDetached; date=\""));
00282           }
00283           else
00284           {
00285             nsCAutoString header("Content-Type: text/x-moz-deleted; name=\"Deleted: ");
00286             header.Append(fileName);
00287             status = MimeWriteAString(obj, header);
00288             if (status < 0) 
00289               return status;
00290             status = MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK"Content-Transfer-Encoding: 8bit"MSG_LINEBREAK));
00291             MimeWriteAString(obj, NS_LITERAL_CSTRING("Content-Disposition: inline; filename=\"Deleted:"));
00292             MimeWriteAString(obj, fileName);
00293             MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK"X-Mozilla-Altered: AttachmentDeleted; date=\""));
00294           }
00295           nsCString result;
00296           char timeBuffer[128];
00297           PRExplodedTime now;
00298           PR_ExplodeTime(PR_Now(), PR_LocalTimeParameters, &now);
00299           PR_FormatTimeUSEnglish(timeBuffer, sizeof(timeBuffer),
00300                                  "%a %b %d %H:%M:%S %Y",
00301                                  &now);
00302           MimeWriteAString(obj, nsDependentCString(timeBuffer));
00303           MimeWriteAString(obj, NS_LITERAL_CSTRING("\""MSG_LINEBREAK));
00304           MimeWriteAString(obj, NS_LITERAL_CSTRING(MSG_LINEBREAK"The original MIME headers for this attachment are:"MSG_LINEBREAK));
00305           MimeHeaders_write_raw_headers(mult->hdrs, obj->options, PR_FALSE);
00306         }
00307         status = ((MimeMultipartClass *) obj->clazz)->create_child(obj);
00308         if (status < 0) return status;
00309         PR_ASSERT(mult->state != MimeMultipartHeaders);
00310 
00311         // Ok, at this point, we need to examine the headers and see if there
00312         // is a special charset (i.e. non US-ASCII) for this message. If so, 
00313         // we need to tell the emitter that this is the case for use in in any
00314         // possible reply or forward operation.
00315         //
00316         PRBool isBody = PR_FALSE;
00317         PRBool isAlternative = PR_FALSE;
00318 
00319         MimeContainer *container = (MimeContainer*) obj; 
00320         // check if we're stripping the part of this newly created child.
00321         if (container->children && container->nchildren > 0)
00322         {
00323           MimeObject *kid = container->children[container->nchildren-1];
00324           if (kid->output_p)
00325             kid->output_p = !obj->options->state->strippingPart;
00326         }
00327         if (container->children && container->nchildren == 1)
00328         {
00329           PRBool isAlternativeOrRelated = PR_FALSE;
00330           isBody = MimeObjectChildIsMessageBody(obj, &isAlternativeOrRelated);
00331 
00332           // MimeObjectChildIsMessageBody returns false for "multipart/related"
00333           // but we want to use the first part charset if that's a body.
00334           // I don't want to change the behavior of MimeObjectChildIsMessageBody
00335           // which is used by other places, so do the body check here.
00336           if (!isBody && 
00337               isAlternativeOrRelated &&
00338               mime_subclass_p(obj->clazz, (MimeObjectClass*) &mimeMultipartRelatedClass))
00339           {
00340             MimeObject *firstChild = container->children[0];
00341             char *disposition = MimeHeaders_get (firstChild->headers,
00342                                                  HEADER_CONTENT_DISPOSITION, 
00343                                                  PR_TRUE,
00344                                                  PR_FALSE);
00345             if (!disposition)
00346             {
00347               if (!nsCRT::strcasecmp (firstChild->content_type, TEXT_PLAIN) ||
00348                   !nsCRT::strcasecmp (firstChild->content_type, TEXT_HTML) ||
00349                   !nsCRT::strcasecmp (firstChild->content_type, TEXT_MDL) ||
00350                   !nsCRT::strcasecmp (firstChild->content_type, MULTIPART_ALTERNATIVE) ||
00351                   !nsCRT::strcasecmp (firstChild->content_type, MULTIPART_RELATED) ||
00352                   !nsCRT::strcasecmp (firstChild->content_type, MESSAGE_NEWS) ||
00353                   !nsCRT::strcasecmp (firstChild->content_type, MESSAGE_RFC822))
00354                 isBody = PR_TRUE;
00355             }
00356           }
00357         }
00358         else 
00359           isAlternative = mime_subclass_p(obj->clazz, (MimeObjectClass*) &mimeMultipartAlternativeClass);
00360 
00361         // If "multipart/alternative" or the first part is a message body
00362         // then we should check for a charset and notify the emitter  
00363         // if one exists.                                                     
00364         if (obj->options && ((isAlternative && mult->state != MimeMultipartSkipPartLine) || isBody))
00365         {
00366           {
00367            char *ct = MimeHeaders_get(mult->hdrs, HEADER_CONTENT_TYPE, PR_FALSE, PR_FALSE);
00368            if (ct)
00369            {
00370              char *cset = MimeHeaders_get_parameter (ct, "charset", NULL, NULL);
00371              if (cset)
00372              {
00373                 mimeEmitterUpdateCharacterSet(obj->options, cset);
00374                 if (!(obj->options->override_charset))
00375                   // Also set this charset to msgWindow
00376                   SetMailCharacterSetToMsgWindow(obj, cset);
00377               }
00378 
00379               PR_FREEIF(ct);
00380               PR_FREEIF(cset);
00381             }
00382           }
00383         }
00384       }
00385       break;
00386     }
00387 
00388     case MimeMultipartPartFirstLine:
00389       /* Hand this line off to the sub-part. */
00390       status = (((MimeMultipartClass *) obj->clazz)->parse_child_line(obj,
00391                                                   line, length, PR_TRUE));
00392       if (status < 0) return status;
00393       mult->state = MimeMultipartPartLine;
00394       break;
00395 
00396     case MimeMultipartPartLine:
00397       /* Hand this line off to the sub-part. */
00398       status = (((MimeMultipartClass *) obj->clazz)->parse_child_line(obj,
00399                   line, length, PR_FALSE));
00400       if (status < 0) return status;
00401       break;
00402 
00403     case MimeMultipartSkipPartLine:
00404       /* we are skipping that part, therefore just ignore the line */
00405       break;
00406 
00407     default:
00408       PR_ASSERT(0);
00409       return -1;
00410   }
00411 
00412   if (obj->options->format_out == nsMimeOutput::nsMimeMessageAttach && 
00413       (!obj->options->state->strippingPart && mult->state != MimeMultipartPartLine))
00414       return MimeObject_write(obj, line, length, PR_FALSE);
00415   return 0;
00416 }
00417 
00418 
00419 static MimeMultipartBoundaryType
00420 MimeMultipart_check_boundary(MimeObject *obj, const char *line, PRInt32 length)
00421 {
00422   MimeMultipart *mult = (MimeMultipart *) obj;
00423   PRInt32 blen;
00424   PRBool term_p;
00425 
00426   if (!mult->boundary ||
00427          line[0] != '-' ||
00428          line[1] != '-')
00429        return MimeMultipartBoundaryTypeNone;
00430 
00431   /* This is a candidate line to be a boundary.  Check it out... */
00432   blen = strlen(mult->boundary);
00433   term_p = PR_FALSE;
00434 
00435   /* strip trailing whitespace (including the newline.) */
00436   while(length > 2 && nsCRT::IsAsciiSpace(line[length-1]))
00437        length--;
00438 
00439   /* Could this be a terminating boundary? */
00440   if (length == blen + 4 &&
00441          line[length-1] == '-' &&
00442          line[length-2] == '-')
00443        {
00444          term_p = PR_TRUE;
00445        }
00446 
00447   //looks like we have a separator but first, we need to check it's not for one of the part's children.
00448   MimeContainer *cont = (MimeContainer *) obj;
00449   if (cont->nchildren > 0)
00450   {
00451     MimeObject *kid = cont->children[cont->nchildren-1];
00452     if (kid)
00453       if (mime_typep(kid, (MimeObjectClass*) &mimeMultipartClass))
00454       {
00455         //Don't ask the kid to check the boundary if it has already detected a Teminator
00456         MimeMultipart *mult = (MimeMultipart *) kid;
00457         if (mult->state != MimeMultipartEpilogue)
00458           if (MimeMultipart_check_boundary(kid, line, length) != MimeMultipartBoundaryTypeNone)
00459             return MimeMultipartBoundaryTypeNone;
00460       }
00461   }
00462 
00463   if (term_p)
00464     length -= 2;
00465 
00466   if (blen == length-2 && !strncmp(line+2, mult->boundary, length-2))
00467     return (term_p
00468       ? MimeMultipartBoundaryTypeTerminator
00469       : MimeMultipartBoundaryTypeSeparator);
00470   else
00471     return MimeMultipartBoundaryTypeNone;
00472 }
00473 
00474 
00475 static int
00476 MimeMultipart_create_child(MimeObject *obj)
00477 {
00478   MimeMultipart *mult = (MimeMultipart *) obj;
00479   int           status;
00480   char *ct = (mult->hdrs
00481                        ? MimeHeaders_get (mult->hdrs, HEADER_CONTENT_TYPE,
00482                                                          PR_TRUE, PR_FALSE)
00483                        : 0);
00484   const char *dct = (((MimeMultipartClass *) obj->clazz)->default_part_type);
00485   MimeObject *body = NULL;
00486 
00487   mult->state = MimeMultipartPartFirstLine;
00488   /* Don't pass in NULL as the content-type (this means that the
00489         auto-uudecode-hack won't ever be done for subparts of a
00490         multipart, but only for untyped children of message/rfc822.
00491    */
00492   body = mime_create(((ct && *ct) ? ct : (dct ? dct: TEXT_PLAIN)),
00493                                     mult->hdrs, obj->options);
00494   PR_FREEIF(ct);
00495   if (!body) return MIME_OUT_OF_MEMORY;
00496   status = ((MimeContainerClass *) obj->clazz)->add_child(obj, body);
00497   if (status < 0)
00498        {
00499          mime_free(body);
00500          return status;
00501        }
00502 
00503 #ifdef MIME_DRAFTS
00504   if ( obj->options && 
00505           obj->options->decompose_file_p &&
00506           obj->options->is_multipart_msg &&
00507           obj->options->decompose_file_init_fn )
00508        {
00509          if ( !mime_typep(obj,(MimeObjectClass*)&mimeMultipartRelatedClass) &&
00510            !mime_typep(obj,(MimeObjectClass*)&mimeMultipartAlternativeClass) &&
00511                  !mime_typep(obj,(MimeObjectClass*)&mimeMultipartSignedClass) &&
00512 #ifdef MIME_DETAIL_CHECK
00513                  !mime_typep(body, (MimeObjectClass*)&mimeMultipartRelatedClass) &&
00514                  !mime_typep(body, (MimeObjectClass*)&mimeMultipartAlternativeClass) &&
00515                  !mime_typep(body,(MimeObjectClass*)&mimeMultipartSignedClass) 
00516 #else
00517            /* bug 21869 -- due to the fact that we are not generating the
00518               correct mime class object for content-typ multipart/signed part
00519               the above check failed. to solve the problem in general and not
00520               to cause early temination when parsing message for opening as
00521               draft we can simply make sure that the child is not a multipart
00522               mime object. this way we could have a proper decomposing message
00523               part functions set correctly */
00524            !mime_typep(body, (MimeObjectClass*) &mimeMultipartClass)
00525 #endif
00526     &&        ! (mime_typep(body, (MimeObjectClass*)&mimeExternalObjectClass) && !strcmp(body->content_type, "text/x-vcard"))
00527        )
00528          {
00529               status = obj->options->decompose_file_init_fn ( obj->options->stream_closure, mult->hdrs );
00530               if (status < 0) return status;
00531          }
00532        }
00533 #endif /* MIME_DRAFTS */
00534 
00535 
00536   /* Now that we've added this new object to our list of children,
00537         start its parser going (if we want to display it.)
00538    */
00539   body->output_p = (((MimeMultipartClass *) obj->clazz)->output_child_p(obj, body));
00540   if (body->output_p)
00541        {  
00542          status = body->clazz->parse_begin(body);
00543 
00544 #if defined(XP_MAC) || defined(XP_MACOSX)
00545     /* if we are saving an apple double attachment, we need to set correctly the conten type of the channel */
00546     if (mime_typep(obj, (MimeObjectClass *) &mimeMultipartAppleDoubleClass))
00547     {
00548       struct mime_stream_data *msd = (struct mime_stream_data *)body->options->stream_closure;
00549       if (!body->options->write_html_p && body->content_type && !nsCRT::strcasecmp(body->content_type, APPLICATION_APPLEFILE))
00550       {
00551                             if (msd && msd->channel)
00552               msd->channel->SetContentType(NS_LITERAL_CSTRING(APPLICATION_APPLEFILE));
00553       }
00554     }
00555 #endif
00556 
00557          if (status < 0) return status;
00558        }
00559 
00560   return 0;
00561 }
00562 
00563 
00564 static PRBool
00565 MimeMultipart_output_child_p(MimeObject *obj, MimeObject *child)
00566 {
00567   /* if we are saving an apple double attachment, ignore the appledouble wrapper part */
00568   return obj->options->write_html_p || nsCRT::strcasecmp(child->content_type, MULTIPART_APPLEDOUBLE);
00569 }
00570 
00571 
00572 
00573 static int
00574 MimeMultipart_close_child(MimeObject *object)
00575 {
00576   MimeMultipart *mult = (MimeMultipart *) object;
00577   MimeContainer *cont = (MimeContainer *) object;
00578 
00579   if (!mult->hdrs)
00580        return 0;
00581 
00582   MimeHeaders_free(mult->hdrs);
00583   mult->hdrs = 0;
00584 
00585   NS_ASSERTION(cont->nchildren > 0, "badly formed mime message");
00586   if (cont->nchildren > 0)
00587        {
00588          MimeObject *kid = cont->children[cont->nchildren-1];
00589          if (kid)
00590               {
00591                 int status;
00592                 status = kid->clazz->parse_eof(kid, PR_FALSE);
00593                 if (status < 0) return status;
00594                 status = kid->clazz->parse_end(kid, PR_FALSE);
00595                 if (status < 0) return status;
00596 
00597 #ifdef MIME_DRAFTS
00598                 if ( object->options &&
00599                         object->options->decompose_file_p &&
00600                         object->options->is_multipart_msg &&
00601                         object->options->decompose_file_close_fn ) 
00602                 {
00603                        if ( !mime_typep(object,(MimeObjectClass*)&mimeMultipartRelatedClass) &&
00604                                !mime_typep(object,(MimeObjectClass*)&mimeMultipartAlternativeClass) &&
00605                                !mime_typep(object,(MimeObjectClass*)&mimeMultipartSignedClass) &&
00606 #ifdef MIME_DETAIL_CHECK
00607                                !mime_typep(kid,(MimeObjectClass*)&mimeMultipartRelatedClass) &&
00608                                !mime_typep(kid,(MimeObjectClass*)&mimeMultipartAlternativeClass) &&
00609                                !mime_typep(kid,(MimeObjectClass*)&mimeMultipartSignedClass) 
00610 #else
00611                    /* bug 21869 -- due to the fact that we are not generating the
00612                       correct mime class object for content-typ multipart/signed part
00613                       the above check failed. to solve the problem in general and not
00614                       to cause early temination when parsing message for opening as
00615                       draft we can simply make sure that the child is not a multipart
00616                       mime object. this way we could have a proper decomposing message
00617                       part functions set correctly */
00618                    !mime_typep(kid,(MimeObjectClass*) &mimeMultipartClass)
00619 #endif
00620                                   && !(mime_typep(kid, (MimeObjectClass*)&mimeExternalObjectClass) && !strcmp(kid->content_type, "text/x-vcard"))
00621            )
00622                             {
00623                                    status = object->options->decompose_file_close_fn ( object->options->stream_closure );
00624                                    if (status < 0) return status;
00625                             }
00626                 }
00627 #endif /* MIME_DRAFTS */
00628 
00629               }
00630        }
00631   return 0;
00632 }
00633 
00634 
00635 static int
00636 MimeMultipart_parse_child_line (MimeObject *obj, char *line, PRInt32 length,
00637                                                         PRBool first_line_p)
00638 {
00639   MimeContainer *cont = (MimeContainer *) obj;
00640   int status;
00641   MimeObject *kid;
00642 
00643   PR_ASSERT(cont->nchildren > 0);
00644   if (cont->nchildren <= 0)
00645        return -1;
00646 
00647   kid = cont->children[cont->nchildren-1];
00648   PR_ASSERT(kid);
00649   if (!kid) return -1;
00650 
00651 #ifdef MIME_DRAFTS
00652   if ( obj->options &&
00653           obj->options->decompose_file_p &&
00654           obj->options->is_multipart_msg && 
00655           obj->options->decompose_file_output_fn ) 
00656   {
00657        if (!mime_typep(obj,(MimeObjectClass*)&mimeMultipartAlternativeClass) &&
00658               !mime_typep(obj,(MimeObjectClass*)&mimeMultipartRelatedClass) &&
00659               !mime_typep(obj,(MimeObjectClass*)&mimeMultipartSignedClass) &&
00660 #ifdef MIME_DETAIL_CHECK
00661               !mime_typep(kid,(MimeObjectClass*)&mimeMultipartAlternativeClass) &&
00662               !mime_typep(kid,(MimeObjectClass*)&mimeMultipartRelatedClass) &&
00663               !mime_typep(kid,(MimeObjectClass*)&mimeMultipartSignedClass)
00664 #else
00665         /* bug 21869 -- due to the fact that we are not generating the
00666            correct mime class object for content-typ multipart/signed part
00667            the above check failed. to solve the problem in general and not
00668            to cause early temination when parsing message for opening as
00669            draft we can simply make sure that the child is not a multipart
00670            mime object. this way we could have a proper decomposing message
00671            part functions set correctly */
00672         !mime_typep(kid, (MimeObjectClass*) &mimeMultipartClass)
00673 #endif
00674     && !(mime_typep(kid, (MimeObjectClass*)&mimeExternalObjectClass) && !strcmp(kid->content_type, "text/x-vcard"))
00675     )
00676               return obj->options->decompose_file_output_fn (line, length, obj->options->stream_closure);
00677   }
00678 #endif /* MIME_DRAFTS */
00679 
00680   /* The newline issues here are tricky, since both the newlines before
00681         and after the boundary string are to be considered part of the
00682         boundary: this is so that a part can be specified such that it
00683         does not end in a trailing newline.
00684 
00685         To implement this, we send a newline *before* each line instead
00686         of after, except for the first line, which is not preceeded by a
00687         newline.
00688    */
00689 
00690   /* Remove the trailing newline... */
00691   if (length > 0 && line[length-1] == nsCRT::LF) length--;
00692   if (length > 0 && line[length-1] == nsCRT::CR) length--;
00693 
00694   if (!first_line_p)
00695        {
00696          /* Push out a preceeding newline... */
00697          char nl[] = MSG_LINEBREAK;
00698          status = kid->clazz->parse_buffer (nl, MSG_LINEBREAK_LEN, kid);
00699          if (status < 0) return status;
00700        }
00701 
00702   /* Now push out the line sans trailing newline. */
00703   return kid->clazz->parse_buffer (line, length, kid);
00704 }
00705 
00706 
00707 static int
00708 MimeMultipart_parse_eof (MimeObject *obj, PRBool abort_p)
00709 {
00710   MimeMultipart *mult = (MimeMultipart *) obj;
00711   MimeContainer *cont = (MimeContainer *) obj;
00712 
00713   if (obj->closed_p) return 0;
00714 
00715   /* Push out one last newline if part of the last line is still in the
00716         ibuffer.  If this happens, this object does not end in a trailing newline
00717         (and the parse_line method will be called with a string with no trailing
00718         newline, which isn't the usual case.)
00719    */
00720   if (!abort_p && obj->ibuffer_fp > 0)
00721        {
00722          int status = obj->clazz->parse_buffer (obj->ibuffer, obj->ibuffer_fp,
00723                                                                               obj);
00724          obj->ibuffer_fp = 0;
00725          if (status < 0)
00726               {
00727                 obj->closed_p = PR_TRUE;
00728                 return status;
00729               }
00730        }
00731 
00732   /* Now call parse_eof for our active child, if there is one.
00733    */
00734   if (cont->nchildren > 0 &&
00735          (mult->state == MimeMultipartPartLine ||
00736           mult->state == MimeMultipartPartFirstLine))
00737        {
00738          MimeObject *kid = cont->children[cont->nchildren-1];
00739          PR_ASSERT(kid);
00740          if (kid)
00741               {
00742                 int status = kid->clazz->parse_eof(kid, abort_p);
00743                 if (status < 0) return status;
00744               }
00745        }
00746 
00747   return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
00748 }
00749 
00750 
00751 #if defined(DEBUG) && defined(XP_UNIX)
00752 static int
00753 MimeMultipart_debug_print (MimeObject *obj, PRFileDesc *stream, PRInt32 depth)
00754 {
00755   /*  MimeMultipart *mult = (MimeMultipart *) obj; */
00756   MimeContainer *cont = (MimeContainer *) obj;
00757   char *addr = mime_part_address(obj);
00758   int i;
00759   for (i=0; i < depth; i++)
00760        PR_Write(stream, "  ", 2);
00769   PR_FREEIF(addr);
00770 
00771 /*
00772   if (cont->nchildren > 0)
00773        fprintf(stream, "\n");
00774  */
00775 
00776   for (i = 0; i < cont->nchildren; i++)
00777        {
00778          MimeObject *kid = cont->children[i];
00779          int status = kid->clazz->debug_print (kid, stream, depth+1);
00780          if (status < 0) return status;
00781        }
00782 
00783 /*
00784   if (cont->nchildren > 0)
00785        fprintf(stream, "\n");
00786  */
00787 
00788   return 0;
00789 }
00790 #endif