Back to index

lightning-sunbird  0.9+nobinonly
mimemalt.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  *   Ben Bucksch <mozilla@bucksch.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 "mimemalt.h"
00040 #include "prmem.h"
00041 #include "plstr.h"
00042 #include "prlog.h"
00043 #include "nsMimeTypes.h"
00044 #include "nsMimeStringResources.h"
00045 #include "nsIPrefBranch.h"
00046 #include "mimemoz2.h" // for prefs
00047 #include "nsCRT.h"
00048 
00049 extern "C" MimeObjectClass mimeMultipartRelatedClass;
00050 
00051 #define MIME_SUPERCLASS mimeMultipartClass
00052 MimeDefClass(MimeMultipartAlternative, MimeMultipartAlternativeClass,
00053        mimeMultipartAlternativeClass, &MIME_SUPERCLASS);
00054 
00055 static int MimeMultipartAlternative_initialize (MimeObject *);
00056 static void MimeMultipartAlternative_finalize (MimeObject *);
00057 static int MimeMultipartAlternative_parse_eof (MimeObject *, PRBool);
00058 static int MimeMultipartAlternative_create_child(MimeObject *);
00059 static int MimeMultipartAlternative_parse_child_line (MimeObject *, char *,
00060                             PRInt32, PRBool);
00061 static int MimeMultipartAlternative_close_child(MimeObject *);
00062 
00063 static PRBool MimeMultipartAlternative_display_part_p(MimeObject *self,
00064                              MimeHeaders *sub_hdrs);
00065 static int MimeMultipartAlternative_discard_cached_part(MimeObject *);
00066 static int MimeMultipartAlternative_display_cached_part(MimeObject *);
00067 
00068 static int
00069 MimeMultipartAlternativeClassInitialize(MimeMultipartAlternativeClass *clazz)
00070 {
00071   MimeObjectClass    *oclass = (MimeObjectClass *)    clazz;
00072   MimeMultipartClass *mclass = (MimeMultipartClass *) clazz;
00073   PR_ASSERT(!oclass->class_initialized);
00074   oclass->initialize       = MimeMultipartAlternative_initialize;
00075   oclass->finalize         = MimeMultipartAlternative_finalize;
00076   oclass->parse_eof        = MimeMultipartAlternative_parse_eof;
00077   mclass->create_child     = MimeMultipartAlternative_create_child;
00078   mclass->parse_child_line = MimeMultipartAlternative_parse_child_line;
00079   mclass->close_child      = MimeMultipartAlternative_close_child;
00080   return 0;
00081 }
00082 
00083 
00084 static int
00085 MimeMultipartAlternative_initialize (MimeObject *obj)
00086 {
00087   MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
00088 
00089   PR_ASSERT(!malt->part_buffer);
00090   malt->part_buffer = MimePartBufferCreate();
00091   if (!malt->part_buffer)
00092   return MIME_OUT_OF_MEMORY;
00093 
00094   return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(obj);
00095 }
00096 
00097 static void
00098 MimeMultipartAlternative_cleanup(MimeObject *obj)
00099 {
00100   MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
00101   if (malt->buffered_hdrs)
00102   {
00103     MimeHeaders_free(malt->buffered_hdrs);
00104     malt->buffered_hdrs = 0;
00105   }
00106   if (malt->part_buffer)
00107   {
00108     MimePartBufferDestroy(malt->part_buffer);
00109     malt->part_buffer = 0;
00110   }
00111 }
00112 
00113 
00114 static void
00115 MimeMultipartAlternative_finalize (MimeObject *obj)
00116 {
00117   MimeMultipartAlternative_cleanup(obj);
00118   ((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(obj);
00119 }
00120 
00121 
00122 static int
00123 MimeMultipartAlternative_parse_eof (MimeObject *obj, PRBool abort_p)
00124 {
00125   MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
00126   int status = 0;
00127 
00128   if (obj->closed_p) return 0;
00129 
00130   status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
00131   if (status < 0) return status;
00132 
00133   /* If there's a cached part we haven't written out yet, do it now.
00134    */
00135   if (malt->buffered_hdrs && !abort_p && 
00136       obj->options->format_out != nsMimeOutput::nsMimeMessageAttach)
00137   {
00138     status = MimeMultipartAlternative_display_cached_part(obj);
00139     if (status < 0) return status;
00140   }
00141 
00142   MimeMultipartAlternative_cleanup(obj);
00143 
00144   return status;
00145 }
00146 
00147 
00148 static int
00149 MimeMultipartAlternative_create_child(MimeObject *obj)
00150 {
00151   MimeMultipart *mult = (MimeMultipart *) obj;
00152   MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
00153 
00154   if (MimeMultipartAlternative_display_part_p (obj, mult->hdrs))
00155   {
00156     /* If this part is potentially displayable, begin populating the cache
00157      with it.  If there's something in the cache already, discard it
00158      first.  (Just because this part is displayable doesn't mean we will
00159      display it -- of two consecutive displayable parts, it is the second
00160      one that gets displayed.)
00161      */
00162     int status;
00163     mult->state = MimeMultipartPartFirstLine;
00164 
00165     status = MimeMultipartAlternative_discard_cached_part(obj);
00166     if (status < 0) return status;
00167 
00168     PR_ASSERT(!malt->buffered_hdrs);
00169     malt->buffered_hdrs = MimeHeaders_copy(mult->hdrs);
00170     if (!malt->buffered_hdrs) return MIME_OUT_OF_MEMORY;
00171     return 0;
00172   }
00173   else
00174   {
00175     /* If this part is not displayable, then skip it. maybe the next one will be...
00176      */
00177     mult->state = MimeMultipartSkipPartLine;
00178     return 0;
00179   }
00180 }
00181 
00182 
00183 static int
00184 MimeMultipartAlternative_parse_child_line (MimeObject *obj,
00185                        char *line, PRInt32 length,
00186                        PRBool first_line_p)
00187 {
00188   MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
00189 
00190   PR_ASSERT(malt->part_buffer);
00191   if (!malt->part_buffer) return -1;
00192 
00193   if (!obj->options->state->strippingPart && obj->options->format_out == nsMimeOutput::nsMimeMessageAttach)
00194     MimeObject_write(obj, line, length, PR_FALSE);
00195 
00196   /* Push this line into the buffer for later retrieval. */
00197   return MimePartBufferWrite (malt->part_buffer, line, length);
00198 }
00199 
00200 
00201 static int
00202 MimeMultipartAlternative_close_child(MimeObject *obj)
00203 {
00204   MimeMultipart *mult = (MimeMultipart *) obj;
00205   MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
00206 
00207   /* PR_ASSERT(malt->part_buffer);      Some Mac brokenness trips this...
00208   if (!malt->part_buffer) return -1; */
00209 
00210   if (malt->part_buffer)
00211   MimePartBufferClose(malt->part_buffer);
00212 
00213   /* PR_ASSERT(mult->hdrs);         I expect the Mac trips this too */
00214   if (mult->hdrs)
00215   MimeHeaders_free(mult->hdrs);
00216   mult->hdrs = 0;
00217 
00218   return 0;
00219 }
00220 
00221 
00222 static PRBool
00223 MimeMultipartAlternative_display_part_p(MimeObject *self,
00224                     MimeHeaders *sub_hdrs)
00225 {
00226   char *ct = MimeHeaders_get (sub_hdrs, HEADER_CONTENT_TYPE, PR_TRUE, PR_FALSE);
00227   if (!ct)
00228     return PR_FALSE;
00229 
00230   /* RFC 1521 says:
00231      Receiving user agents should pick and display the last format
00232      they are capable of displaying.  In the case where one of the
00233      alternatives is itself of type "multipart" and contains unrecognized
00234      sub-parts, the user agent may choose either to show that alternative,
00235      an earlier alternative, or both.
00236 
00237    Ugh.  If there is a multipart subtype of alternative, we simply show
00238    that, without descending into it to determine if any of its sub-parts
00239    are themselves unknown.
00240    */
00241 
00242   // prefer_plaintext pref
00243   nsIPrefBranch *prefBranch = GetPrefBranch(self->options); 
00244   PRBool prefer_plaintext = PR_FALSE;
00245   if (prefBranch)
00246     prefBranch->GetBoolPref("mailnews.display.prefer_plaintext",
00247                             &prefer_plaintext);
00248   if (prefer_plaintext
00249       && self->options->format_out != nsMimeOutput::nsMimeMessageSaveAs
00250       && (!nsCRT::strncasecmp(ct, "text/html", 9) ||
00251           !nsCRT::strncasecmp(ct, "text/enriched", 13) ||
00252           !nsCRT::strncasecmp(ct, "text/richtext", 13))
00253      )
00254     // if the user prefers plaintext and this is the "rich" (e.g. HTML) part...
00255   {
00256 #if DEBUG
00257     printf ("Ignoring %s alternative\n", ct);
00258 #endif
00259     return PR_FALSE;
00260   }
00261 #if DEBUG
00262   printf ("Considering %s alternative\n", ct);
00263 #endif
00264 
00265   MimeObjectClass *clazz = mime_find_class (ct, sub_hdrs, self->options, PR_TRUE);
00266   PRBool result = (clazz
00267           ? clazz->displayable_inline_p(clazz, sub_hdrs)
00268           : PR_FALSE);
00269   PR_FREEIF(ct);
00270   return result;
00271 }
00272 
00273 static int 
00274 MimeMultipartAlternative_discard_cached_part(MimeObject *obj)
00275 {
00276   MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
00277 
00278   if (malt->buffered_hdrs)
00279   {
00280     MimeHeaders_free(malt->buffered_hdrs);
00281     malt->buffered_hdrs = 0;
00282   }
00283   if (malt->part_buffer)
00284   MimePartBufferReset (malt->part_buffer);
00285 
00286   return 0;
00287 }
00288 
00289 static int 
00290 MimeMultipartAlternative_display_cached_part(MimeObject *obj)
00291 {
00292   MimeMultipartAlternative *malt = (MimeMultipartAlternative *) obj;
00293   int status;
00294 
00295   char *ct = (malt->buffered_hdrs
00296         ? MimeHeaders_get (malt->buffered_hdrs, HEADER_CONTENT_TYPE,
00297                  PR_TRUE, PR_FALSE)
00298         : 0);
00299   const char *dct = (((MimeMultipartClass *) obj->clazz)->default_part_type);
00300   MimeObject *body;
00301 
00302   /* Don't pass in NULL as the content-type (this means that the
00303    auto-uudecode-hack won't ever be done for subparts of a
00304    multipart, but only for untyped children of message/rfc822.
00305    */
00306   body = mime_create(((ct && *ct) ? ct : (dct ? dct: TEXT_PLAIN)),
00307            malt->buffered_hdrs, obj->options);
00308 
00309   PR_FREEIF(ct);
00310   if (!body) return MIME_OUT_OF_MEMORY;
00311 
00312   status = ((MimeContainerClass *) obj->clazz)->add_child(obj, body);
00313   if (status < 0)
00314   {
00315     mime_free(body);
00316     return status;
00317   }
00318 
00319 #ifdef MIME_DRAFTS
00320   /* if this object is a child of a multipart/related object, the parent is
00321      taking care of decomposing the whole part, don't need to do it at this level.
00322      However, we still have to call decompose_file_init_fn and decompose_file_close_fn
00323      in order to set the correct content-type. But don't call MimePartBufferRead
00324   */
00325   PRBool multipartRelatedChild = mime_typep(obj->parent,(MimeObjectClass*)&mimeMultipartRelatedClass);
00326   PRBool decomposeFile = obj->options &&
00327                   obj->options->decompose_file_p &&
00328                   obj->options->decompose_file_init_fn &&
00329                   !mime_typep(body, (MimeObjectClass *) &mimeMultipartClass);
00330 
00331   if (decomposeFile)
00332   {
00333     status = obj->options->decompose_file_init_fn (
00334                         obj->options->stream_closure,
00335                         malt->buffered_hdrs);
00336     if (status < 0) return status;
00337   }
00338 #endif /* MIME_DRAFTS */
00339 
00340 
00341   /* Now that we've added this new object to our list of children,
00342    start its parser going. */
00343   status = body->clazz->parse_begin(body);
00344   if (status < 0) return status;
00345 
00346 #ifdef MIME_DRAFTS
00347   if (decomposeFile && !multipartRelatedChild)
00348     status = MimePartBufferRead (malt->part_buffer,
00349                   obj->options->decompose_file_output_fn,
00350                   obj->options->stream_closure);
00351   else
00352 #endif /* MIME_DRAFTS */
00353 
00354   status = MimePartBufferRead (malt->part_buffer,
00355                   /* The (nsresult (*) ...) cast is to turn the
00356                    `void' argument into `MimeObject'. */
00357                   ((nsresult (*) (const char *, PRInt32, void *))
00358                   body->clazz->parse_buffer),
00359                   body);
00360 
00361   if (status < 0) return status;
00362 
00363   MimeMultipartAlternative_cleanup(obj);
00364 
00365   /* Done parsing. */
00366   status = body->clazz->parse_eof(body, PR_FALSE);
00367   if (status < 0) return status;
00368   status = body->clazz->parse_end(body, PR_FALSE);
00369   if (status < 0) return status;
00370 
00371 #ifdef MIME_DRAFTS
00372   if (decomposeFile)
00373   {
00374     status = obj->options->decompose_file_close_fn ( obj->options->stream_closure );
00375     if (status < 0) return status;
00376   }
00377 #endif /* MIME_DRAFTS */
00378 
00379   return 0;
00380 }