Back to index

lightning-sunbird  0.9+nobinonly
mimecryp.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; 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 Communicator.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corp..
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s): David Drinan <ddrinan@netscape.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * 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 "mimecryp.h"
00039 #include "mimemoz2.h"
00040 #include "nsMimeTypes.h"
00041 #include "nsMimeStringResources.h"
00042 #include "mimebuf.h"
00043 #include "prmem.h"
00044 #include "plstr.h"
00045 #include "prlog.h"
00046 #include "nsCRT.h"
00047 #include "mimemult.h"
00048 
00049 
00050 #define MIME_SUPERCLASS mimeContainerClass
00051 MimeDefClass(MimeEncrypted, MimeEncryptedClass, mimeEncryptedClass,
00052                       &MIME_SUPERCLASS);
00053 
00054 static int MimeEncrypted_initialize (MimeObject *);
00055 static void MimeEncrypted_finalize (MimeObject *);
00056 static int MimeEncrypted_parse_begin (MimeObject *);
00057 static int MimeEncrypted_parse_buffer (const char *, PRInt32, MimeObject *);
00058 static int MimeEncrypted_parse_line (char *, PRInt32, MimeObject *);
00059 static int MimeEncrypted_parse_decoded_buffer (const char *, PRInt32, MimeObject *);
00060 static int MimeEncrypted_parse_eof (MimeObject *, PRBool);
00061 static int MimeEncrypted_parse_end (MimeObject *, PRBool);
00062 static int MimeEncrypted_add_child (MimeObject *, MimeObject *);
00063 
00064 static int MimeHandleDecryptedOutput (const char *, PRInt32, void *);
00065 static int MimeHandleDecryptedOutputLine (char *, PRInt32, MimeObject *);
00066 static int MimeEncrypted_close_headers (MimeObject *);
00067 static int MimeEncrypted_emit_buffered_child(MimeObject *);
00068 
00069 static int
00070 MimeEncryptedClassInitialize(MimeEncryptedClass *clazz)
00071 {
00072   MimeObjectClass    *oclass = (MimeObjectClass *)    clazz;
00073   MimeContainerClass *cclass = (MimeContainerClass *) clazz;
00074 
00075   NS_ASSERTION(!oclass->class_initialized, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59");
00076   oclass->initialize   = MimeEncrypted_initialize;
00077   oclass->finalize     = MimeEncrypted_finalize;
00078   oclass->parse_begin  = MimeEncrypted_parse_begin;
00079   oclass->parse_buffer = MimeEncrypted_parse_buffer;
00080   oclass->parse_line   = MimeEncrypted_parse_line;
00081   oclass->parse_eof    = MimeEncrypted_parse_eof;
00082   oclass->parse_end    = MimeEncrypted_parse_end;
00083 
00084   cclass->add_child    = MimeEncrypted_add_child;
00085 
00086   clazz->parse_decoded_buffer = MimeEncrypted_parse_decoded_buffer;
00087 
00088   return 0;
00089 }
00090 
00091 
00092 static int
00093 MimeEncrypted_initialize (MimeObject *obj)
00094 {
00095   return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(obj);
00096 }
00097 
00098 
00099 static int
00100 MimeEncrypted_parse_begin (MimeObject *obj)
00101 {
00102   MimeEncrypted *enc = (MimeEncrypted *) obj;
00103   MimeDecoderData *(*fn) (nsresult (*) (const char*, PRInt32, void*), void*) = 0;
00104 
00105   if (enc->crypto_closure)
00106        return -1;
00107 
00108   enc->crypto_closure = (((MimeEncryptedClass *) obj->clazz)->crypto_init) (obj, MimeHandleDecryptedOutput, obj);
00109   if (!enc->crypto_closure)
00110        return -1;
00111 
00112 
00113   /* (Mostly duplicated from MimeLeaf, see comments in mimecryp.h.)
00114      Initialize a decoder if necessary.
00115    */
00116   if (!obj->encoding)
00117        ;
00118   else if (!nsCRT::strcasecmp(obj->encoding, ENCODING_BASE64))
00119        fn = &MimeB64DecoderInit;
00120   else if (!nsCRT::strcasecmp(obj->encoding, ENCODING_QUOTED_PRINTABLE))
00121   {
00122     enc->decoder_data =
00123          MimeQPDecoderInit (/* The (int (*) ...) cast is to turn the `void' argument
00124                    into `MimeObject'. */
00125                 ((nsresult (*) (const char *, PRInt32, void *))
00126                  ((MimeEncryptedClass *)obj->clazz)->parse_decoded_buffer),
00127                 obj);
00128 
00129     if (!enc->decoder_data)
00130          return MIME_OUT_OF_MEMORY;
00131   }
00132   else if (!nsCRT::strcasecmp(obj->encoding, ENCODING_UUENCODE) ||
00133                  !nsCRT::strcasecmp(obj->encoding, ENCODING_UUENCODE2) ||
00134                  !nsCRT::strcasecmp(obj->encoding, ENCODING_UUENCODE3) ||
00135                  !nsCRT::strcasecmp(obj->encoding, ENCODING_UUENCODE4))
00136        fn = &MimeUUDecoderInit;
00137   else if (!nsCRT::strcasecmp(obj->encoding, ENCODING_YENCODE))
00138     fn = &MimeYDecoderInit;
00139   if (fn)
00140        {
00141          enc->decoder_data =
00142               fn (/* The (int (*) ...) cast is to turn the `void' argument
00143                         into `MimeObject'. */
00144                      ((nsresult (*) (const char *, PRInt32, void *))
00145                       ((MimeEncryptedClass *)obj->clazz)->parse_decoded_buffer),
00146                      obj);
00147 
00148          if (!enc->decoder_data)
00149               return MIME_OUT_OF_MEMORY;
00150        }
00151 
00152   return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_begin(obj);
00153 }
00154 
00155 
00156 static int
00157 MimeEncrypted_parse_buffer (const char *buffer, PRInt32 size, MimeObject *obj)
00158 {
00159   /* (Duplicated from MimeLeaf, see comments in mimecryp.h.)
00160    */
00161 
00162   MimeEncrypted *enc = (MimeEncrypted *) obj;
00163 
00164   if (obj->closed_p) return -1;
00165 
00166   /* Don't consult output_p here, since at this point we're behaving as a
00167         simple container object -- the output_p decision should be made by
00168         the child of this object. */
00169 
00170   if (enc->decoder_data)
00171        return MimeDecoderWrite (enc->decoder_data, buffer, size);
00172   else
00173        return ((MimeEncryptedClass *)obj->clazz)->parse_decoded_buffer (buffer,
00174                                                                                                                         size,
00175                                                                                                                         obj);
00176 }
00177 
00178 
00179 static int
00180 MimeEncrypted_parse_line (char *line, PRInt32 length, MimeObject *obj)
00181 {
00182   NS_ASSERTION(0, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59");
00183   /* This method shouldn't ever be called. */
00184   return -1;
00185 }
00186 
00187 static int
00188 MimeEncrypted_parse_decoded_buffer (const char *buffer, PRInt32 size, MimeObject *obj)
00189 {
00190   MimeEncrypted *enc = (MimeEncrypted *) obj;
00191   return
00192        ((MimeEncryptedClass *) obj->clazz)->crypto_write (buffer, size,
00193                                                                                               enc->crypto_closure);
00194 }
00195 
00196 
00197 static int
00198 MimeEncrypted_parse_eof (MimeObject *obj, PRBool abort_p)
00199 {
00200   int status = 0;
00201   MimeEncrypted *enc = (MimeEncrypted *) obj;
00202 
00203   if (obj->closed_p) return 0;
00204   NS_ASSERTION(!obj->parsed_p, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59");
00205 
00206   /* (Duplicated from MimeLeaf, see comments in mimecryp.h.)
00207      Close off the decoder, to cause it to give up any buffered data that
00208         it is still holding.
00209    */
00210   if (enc->decoder_data)
00211        {
00212          int status = MimeDecoderDestroy(enc->decoder_data, PR_FALSE);
00213          enc->decoder_data = 0;
00214          if (status < 0) return status;
00215        }
00216 
00217 
00218   /* If there is still data in the ibuffer, that means that the last
00219         *decrypted* line of this part didn't end in a newline; so push it out
00220         anyway (this means that the parse_line method will be called with a
00221         string with no trailing newline, which isn't the usual case.)  */
00222   if (!abort_p &&
00223          obj->ibuffer_fp > 0)
00224        {
00225          int status = MimeHandleDecryptedOutputLine (obj->ibuffer,
00226                                                                                       obj->ibuffer_fp, obj);
00227          obj->ibuffer_fp = 0;
00228          if (status < 0)
00229               {
00230                 obj->closed_p = PR_TRUE;
00231                 return status;
00232               }
00233        }
00234 
00235 
00236   /* Now run the superclass's parse_eof, which (because we've already taken
00237         care of ibuffer in a way appropriate for this class, immediately above)
00238         will ony set closed_p to true.
00239    */
00240   status = ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof (obj, abort_p);
00241   if (status < 0) return status;
00242 
00243 
00244   /* Now close off the underlying crypto module.  At this point, the crypto
00245         module has all of the input.  (DecoderDestroy called parse_decoded_buffer
00246         which called crypto_write, with the last of the data.)
00247    */
00248   if (enc->crypto_closure)
00249        {
00250          status =
00251               ((MimeEncryptedClass *) obj->clazz)->crypto_eof (enc->crypto_closure,
00252                                                                                                    abort_p);
00253          if (status < 0 && !abort_p)
00254               return status;
00255        }
00256 
00257   /* Now we have the entire child part in the part buffer.
00258         We are now able to verify its signature, emit a blurb, and then
00259         emit the part.
00260    */
00261   if (abort_p)
00262        return 0;
00263   else
00264        return MimeEncrypted_emit_buffered_child (obj);
00265 }
00266 
00267 
00268 static int
00269 MimeEncrypted_parse_end (MimeObject *obj, PRBool abort_p)
00270 {
00271   return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_end (obj, abort_p);
00272 }
00273 
00274 
00275 static void
00276 MimeEncrypted_cleanup (MimeObject *obj, PRBool finalizing_p)
00277 {
00278   MimeEncrypted *enc = (MimeEncrypted *) obj;
00279 
00280   if (enc->part_buffer)
00281        {
00282          MimePartBufferDestroy(enc->part_buffer);
00283          enc->part_buffer = 0;
00284        }
00285 
00286   if (finalizing_p && enc->crypto_closure)
00287        {
00288          /* Don't free these until this object is really going away -- keep them
00289                around for the lifetime of the MIME object, so that we can get at the
00290                security info of sub-parts of the currently-displayed message. */
00291          ((MimeEncryptedClass *) obj->clazz)->crypto_free (enc->crypto_closure);
00292          enc->crypto_closure = 0;
00293        }
00294 
00295   /* (Duplicated from MimeLeaf, see comments in mimecryp.h.)
00296         Free the decoder data, if it's still around. */
00297   if (enc->decoder_data)
00298        {
00299          MimeDecoderDestroy(enc->decoder_data, PR_TRUE);
00300          enc->decoder_data = 0;
00301        }
00302 
00303   if (enc->hdrs)
00304        {
00305          MimeHeaders_free(enc->hdrs);
00306          enc->hdrs = 0;
00307        }
00308 }
00309 
00310 
00311 static void
00312 MimeEncrypted_finalize (MimeObject *obj)
00313 {
00314   MimeEncrypted_cleanup (obj, PR_TRUE);
00315   ((MimeObjectClass*)&MIME_SUPERCLASS)->finalize (obj);
00316 }
00317 
00318 
00319 static int
00320 MimeHandleDecryptedOutput (const char *buf, PRInt32 buf_size,
00321                                              void *output_closure)
00322 {
00323   /* This method is invoked by the underlying decryption module.
00324         The module is assumed to return a MIME object, and its associated
00325         headers.  For example, if a text/plain document was encrypted,
00326         the encryption module would return the following data:
00327 
00328           Content-Type: text/plain
00329 
00330           Decrypted text goes here.
00331 
00332         This function will then extract a header block (up to the first
00333         blank line, as usual) and will then handle the included data as
00334         appropriate.
00335    */
00336   MimeObject *obj = (MimeObject *) output_closure;
00337 
00338   /* Is it truly safe to use ibuffer here?  I think so... */
00339   return mime_LineBuffer (buf, buf_size,
00340                                            &obj->ibuffer, &obj->ibuffer_size, &obj->ibuffer_fp,
00341                                            PR_TRUE,
00342                                            ((int (*PR_CALLBACK) (char *, PRInt32, void *))
00343                                             /* This cast is to turn void into MimeObject */
00344                                             MimeHandleDecryptedOutputLine),
00345                                            obj);
00346 }
00347 
00348 static int
00349 MimeHandleDecryptedOutputLine (char *line, PRInt32 length, MimeObject *obj)
00350 {
00351   /* Largely the same as MimeMessage_parse_line (the other MIME container
00352         type which contains exactly one child.)
00353    */
00354   MimeEncrypted *enc = (MimeEncrypted *) obj;
00355   int status = 0;
00356 
00357   if (!line || !*line) return -1;
00358 
00359   /* If we're supposed to write this object, but aren't supposed to convert
00360         it to HTML, simply pass it through unaltered. */
00361   if (obj->output_p &&
00362          obj->options &&
00363          !obj->options->write_html_p &&
00364          obj->options->output_fn)
00365        return MimeObject_write(obj, line, length, PR_TRUE);
00366 
00367   /* If we already have a child object in the buffer, then we're done parsing
00368         headers, and all subsequent lines get passed to the inferior object
00369         without further processing by us.  (Our parent will stop feeding us
00370         lines when this MimeMessage part is out of data.)
00371    */
00372   if (enc->part_buffer)
00373        return MimePartBufferWrite (enc->part_buffer, line, length);
00374 
00375   /* Otherwise we don't yet have a child object in the buffer, which means
00376         we're not done parsing our headers yet.
00377    */
00378   if (!enc->hdrs)
00379        {
00380          enc->hdrs = MimeHeaders_new();
00381          if (!enc->hdrs) return MIME_OUT_OF_MEMORY;
00382        }
00383 
00384   status = MimeHeaders_parse_line(line, length, enc->hdrs);
00385   if (status < 0) return status;
00386 
00387   /* If this line is blank, we're now done parsing headers, and should
00388         examine our content-type to create our "body" part.
00389    */
00390   if (*line == nsCRT::CR || *line == nsCRT::LF)
00391        {
00392          status = MimeEncrypted_close_headers(obj);
00393          if (status < 0) return status;
00394        }
00395 
00396   return 0;
00397 }
00398 
00399 static int
00400 MimeEncrypted_close_headers (MimeObject *obj)
00401 {
00402   MimeEncrypted *enc = (MimeEncrypted *) obj;
00403 
00404   if (enc->part_buffer) return -1;
00405   enc->part_buffer = MimePartBufferCreate();
00406   if (!enc->part_buffer)
00407        return MIME_OUT_OF_MEMORY;
00408 
00409   return 0;
00410 }
00411 
00412 
00413 static int
00414 MimeEncrypted_add_child (MimeObject *parent, MimeObject *child)
00415 {
00416   MimeContainer *cont = (MimeContainer *) parent;
00417   if (!parent || !child) return -1;
00418 
00419   /* Encryption containers can only have one child. */
00420   if (cont->nchildren != 0) return -1;
00421 
00422   return ((MimeContainerClass*)&MIME_SUPERCLASS)->add_child (parent, child);
00423 }
00424 
00425 
00426 static int
00427 MimeEncrypted_emit_buffered_child(MimeObject *obj)
00428 {
00429   MimeEncrypted *enc = (MimeEncrypted *) obj;
00430   int status = 0;
00431   char *ct = 0;
00432   MimeObject *body;
00433 
00434   NS_ASSERTION(enc->crypto_closure, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59");
00435 
00436   /* Emit some HTML saying whether the signature was cool.
00437         But don't emit anything if in FO_QUOTE_MESSAGE mode.
00438 
00439         Also, don't emit anything if the enclosed object is itself a signed
00440         object -- in the case of an encrypted object which contains a signed
00441         object, we only emit the HTML once (since the normal way of encrypting
00442         and signing is to nest the signature inside the crypto envelope.)
00443    */
00444   if (enc->crypto_closure &&
00445          obj->options &&
00446          obj->options->headers != MimeHeadersCitation &&
00447          obj->options->write_html_p &&
00448          obj->options->output_fn)
00449          // && !mime_crypto_object_p(enc->hdrs, PR_TRUE)) // XXX fix later XXX //
00450        {
00451     char *html;
00452 #if 0 // XXX Fix this later XXX //
00453          char *html = (((MimeEncryptedClass *) obj->clazz)->crypto_generate_html
00454                                    (enc->crypto_closure));
00455          if (!html) return -1; /* MK_OUT_OF_MEMORY? */
00456          
00457     status = MimeObject_write(obj, html, nsCRT::strlen(html), PR_FALSE);
00458          PR_FREEIF(html);
00459          if (status < 0) return status;
00460 #endif
00461 
00462          /* Now that we have written out the crypto stamp, the outermost header
00463                block is well and truly closed.  If this is in fact the outermost
00464                message, then run the post_header_html_fn now.
00465           */
00466          if (obj->options &&
00467                 obj->options->state &&
00468                 obj->options->generate_post_header_html_fn &&
00469                 !obj->options->state->post_header_html_run_p)
00470               {
00471                 MimeHeaders *outer_headers = nsnull;
00472                 MimeObject *p;
00473                 for (p = obj; p->parent; p = p->parent)
00474                        outer_headers = p->headers;
00475                 NS_ASSERTION(obj->options->state->first_data_written_p, "1.2 <mscott@netscape.com> 01 Nov 2001 17:59");
00476                 html = obj->options->generate_post_header_html_fn(NULL,
00477                                                                                            obj->options->html_closure,
00478                                                                                                          outer_headers);
00479                 obj->options->state->post_header_html_run_p = PR_TRUE;
00480                 if (html)
00481                      {
00482         status = MimeObject_write(obj, html, strlen(html), PR_FALSE);
00483                        PR_FREEIF(html);
00484                        if (status < 0) return status;
00485                      }
00486               }
00487        }
00488   else if (enc->crypto_closure &&
00489                  obj->options &&
00490           obj->options->decrypt_p)
00491        {
00492          /* Do this just to cause `mime_set_crypto_stamp' to be called, and to
00493                cause the various `decode_error' and `verify_error' slots to be set:
00494                we don't actually use the returned HTML, because we're not emitting
00495                HTML.  It's maybe not such a good thing that the determination of
00496                whether it was encrypted or not is tied up with generating HTML,
00497                but oh well. */
00498          char *html = (((MimeEncryptedClass *) obj->clazz)->crypto_generate_html
00499                                    (enc->crypto_closure));
00500          PR_FREEIF(html);
00501        }
00502 
00503   if (enc->hdrs)
00504        ct = MimeHeaders_get (enc->hdrs, HEADER_CONTENT_TYPE, PR_TRUE, PR_FALSE);
00505   body = mime_create((ct ? ct : TEXT_PLAIN), enc->hdrs, obj->options);
00506   
00507 #ifdef MIME_DRAFTS
00508   if (obj->options->decompose_file_p) {
00509        if (mime_typep (body, (MimeObjectClass*) &mimeMultipartClass) )
00510               obj->options->is_multipart_msg = PR_TRUE;
00511        else if (obj->options->decompose_file_init_fn)
00512               obj->options->decompose_file_init_fn(obj->options->stream_closure,
00513                                                                               enc->hdrs);
00514   }
00515 #endif /* MIME_DRAFTS */
00516 
00517   PR_FREEIF(ct);
00518 
00519   if (!body) return MIME_OUT_OF_MEMORY;
00520   status = ((MimeContainerClass *) obj->clazz)->add_child (obj, body);
00521   if (status < 0)
00522        {
00523          mime_free(body);
00524          return status;
00525        }
00526 
00527   /* Now that we've added this new object to our list of children,
00528         start its parser going. */
00529   status = body->clazz->parse_begin(body);
00530   if (status < 0) return status;
00531 
00532   /* If this object (or the parent) is being output, then by definition
00533         the child is as well.  (This is only necessary because this is such
00534         a funny sort of container...)
00535    */
00536   if (!body->output_p &&
00537          (obj->output_p ||
00538           (obj->parent && obj->parent->output_p)))
00539        body->output_p = PR_TRUE;
00540 
00541   /* If the body is being written raw (not as HTML) then make sure to
00542         write its headers as well. */
00543   if (body->output_p && obj->output_p && !obj->options->write_html_p)
00544        {
00545          status = MimeObject_write(body, "", 0, PR_FALSE);  /* initialize */
00546          if (status < 0) return status;
00547          status = MimeHeaders_write_raw_headers(body->headers, obj->options,
00548                                                                               PR_FALSE);
00549          if (status < 0) return status;
00550        }
00551 
00552   if (enc->part_buffer)  /* part_buffer is 0 for 0-length encrypted data. */
00553 
00554 #ifdef MIME_DRAFTS
00555     if (obj->options->decompose_file_p && !obj->options->is_multipart_msg)
00556               status = MimePartBufferRead(enc->part_buffer,
00557                                                         /* The (nsresult (*) ...) cast is to turn the `void'
00558                                                            argument into `MimeObject'. */
00559                                                          ((nsresult (*) (const char *, PRInt32, void *))
00560                                                         obj->options->decompose_file_output_fn),
00561                                                         obj->options->stream_closure);
00562     else
00563 #endif /* MIME_DRAFTS */
00564 
00565        status = MimePartBufferRead(enc->part_buffer,
00566                                                         /* The (nsresult (*) ...) cast is to turn the `void'
00567                                                            argument into `MimeObject'. */
00568                                                          ((nsresult (*) (const char *, PRInt32, void *))
00569                                                          body->clazz->parse_buffer),
00570                                                         body);
00571   if (status < 0) return status;
00572 
00573   /* The child has been fully processed.  Close it off.
00574    */
00575   status = body->clazz->parse_eof(body, PR_FALSE);
00576   if (status < 0) return status;
00577 
00578   status = body->clazz->parse_end(body, PR_FALSE);
00579   if (status < 0) return status;
00580 
00581 #ifdef MIME_DRAFTS
00582   if (obj->options->decompose_file_p && !obj->options->is_multipart_msg)
00583          obj->options->decompose_file_close_fn(obj->options->stream_closure);
00584 #endif /* MIME_DRAFTS */
00585 
00586   /* Put out a separator after every encrypted object. */
00587   status = MimeObject_write_separator(obj);
00588   if (status < 0) return status;
00589 
00590   MimeEncrypted_cleanup (obj, PR_FALSE);
00591 
00592   return 0;
00593 }