home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / lib / libmime / mimemsig.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  22.2 KB  |  717 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /* mimemsig.c --- definition of the MimeMultipartSigned class (see mimei.h)
  20.    Created: Jamie Zawinski <jwz@netscape.com>, 23-Sep-96.
  21.  */
  22.  
  23. #include "mimemsig.h"
  24. #include "nspr.h"
  25.  
  26. #define MIME_SUPERCLASS mimeMultipartClass
  27. MimeDefClass(MimeMultipartSigned, MimeMultipartSignedClass,
  28.              mimeMultipartSignedClass, &MIME_SUPERCLASS);
  29.  
  30. static int MimeMultipartSigned_initialize (MimeObject *);
  31. static int MimeMultipartSigned_create_child (MimeObject *);
  32. static int MimeMultipartSigned_close_child(MimeObject *);
  33. static int MimeMultipartSigned_parse_line (char *, int32, MimeObject *);
  34. static int MimeMultipartSigned_parse_child_line (MimeObject *, char *, int32,
  35.                                                  XP_Bool);
  36. static int MimeMultipartSigned_parse_eof (MimeObject *, XP_Bool);
  37. static void MimeMultipartSigned_finalize (MimeObject *);
  38.  
  39. static int MimeMultipartSigned_emit_child (MimeObject *obj);
  40.  
  41. static int
  42. MimeMultipartSignedClassInitialize(MimeMultipartSignedClass *class)
  43. {
  44.   MimeObjectClass    *oclass = (MimeObjectClass *)    class;
  45.   MimeMultipartClass *mclass = (MimeMultipartClass *) class;
  46.  
  47.   oclass->initialize       = MimeMultipartSigned_initialize;
  48.   oclass->parse_line       = MimeMultipartSigned_parse_line;
  49.   oclass->parse_eof        = MimeMultipartSigned_parse_eof;
  50.   oclass->finalize         = MimeMultipartSigned_finalize;
  51.   mclass->create_child     = MimeMultipartSigned_create_child;
  52.   mclass->parse_child_line = MimeMultipartSigned_parse_child_line;
  53.   mclass->close_child      = MimeMultipartSigned_close_child;
  54.  
  55.   XP_ASSERT(!oclass->class_initialized);
  56.   return 0;
  57. }
  58.  
  59. static int
  60. MimeMultipartSigned_initialize (MimeObject *object)
  61. {
  62.   MimeMultipartSigned *sig = (MimeMultipartSigned *) object;
  63.  
  64.   /* This is an abstract class; it shouldn't be directly instantiated. */
  65.   XP_ASSERT(object->class != (MimeObjectClass *) &mimeMultipartSignedClass);
  66.  
  67.   sig->state = MimeMultipartSignedPreamble;
  68.  
  69.   return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
  70. }
  71.  
  72. static void
  73. MimeMultipartSigned_cleanup (MimeObject *obj, XP_Bool finalizing_p)
  74. {
  75.   MimeMultipart *mult = (MimeMultipart *) obj; /* #58075.  Fix suggested by jwz */
  76.   MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
  77.   if (sig->part_buffer)
  78.     {
  79.       MimePartBufferDestroy(sig->part_buffer);
  80.       sig->part_buffer = 0;
  81.     }
  82.   if (sig->body_hdrs)
  83.     {
  84.       MimeHeaders_free (sig->body_hdrs);
  85.       sig->body_hdrs = 0;
  86.     }
  87.   if (sig->sig_hdrs)
  88.     {
  89.       MimeHeaders_free (sig->sig_hdrs);
  90.       sig->sig_hdrs = 0;
  91.     }
  92.  
  93.   mult->state = MimeMultipartEpilogue;  /* #58075.  Fix suggested by jwz */
  94.   sig->state = MimeMultipartSignedEpilogue;
  95.  
  96.   if (finalizing_p && sig->crypto_closure)
  97.     {
  98.       /* Don't free these until this object is really going away -- keep them
  99.          around for the lifetime of the MIME object, so that we can get at the
  100.          security info of sub-parts of the currently-displayed message. */
  101.       ((MimeMultipartSignedClass *) obj->class)
  102.         ->crypto_free (sig->crypto_closure);
  103.       sig->crypto_closure = 0;
  104.     }
  105.  
  106.   if (sig->sig_decoder_data)
  107.     {
  108.       MimeDecoderDestroy(sig->sig_decoder_data, TRUE);
  109.       sig->sig_decoder_data = 0;
  110.     }
  111. }
  112.  
  113.  
  114. static int
  115. MimeMultipartSigned_parse_eof (MimeObject *obj, XP_Bool abort_p)
  116. {
  117.   MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
  118.   int status = 0;
  119.  
  120.   if (obj->closed_p) return 0;
  121.  
  122.   /* Close off the signature, if we've gotten that far.
  123.    */
  124.   if (sig->state == MimeMultipartSignedSignatureHeaders ||
  125.       sig->state == MimeMultipartSignedSignatureFirstLine ||
  126.       sig->state == MimeMultipartSignedSignatureLine ||
  127.       sig->state == MimeMultipartSignedEpilogue)
  128.     {
  129.       status = (((MimeMultipartSignedClass *) obj->class)
  130.                 ->crypto_signature_eof) (sig->crypto_closure, abort_p);
  131.       if (status < 0) return status;
  132.     }
  133.  
  134.   if (!abort_p)
  135.     {
  136.       /* Now that we've read both the signed object and the signature (and
  137.          have presumably verified the signature) write out a blurb, and then
  138.          the signed object.
  139.        */
  140.       XP_ASSERT(sig->crypto_closure);
  141.       status = MimeMultipartSigned_emit_child(obj);
  142.       if (status < 0) return status;
  143.     }
  144.  
  145.   MimeMultipartSigned_cleanup(obj, FALSE);
  146.   return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
  147. }
  148.  
  149.  
  150. static void
  151. MimeMultipartSigned_finalize (MimeObject *obj)
  152. {
  153.   MimeMultipartSigned_cleanup(obj, TRUE);
  154.   ((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(obj);
  155. }
  156.  
  157.  
  158. static int
  159. MimeMultipartSigned_parse_line (char *line, int32 length, MimeObject *obj)
  160. {
  161.   MimeMultipart *mult = (MimeMultipart *) obj;
  162.   MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
  163.   MimeMultipartParseState old_state = mult->state;
  164.   XP_Bool hash_line_p = TRUE;
  165.   XP_Bool no_headers_p = FALSE;
  166.   int status = 0;
  167.  
  168.   /* First do the parsing for normal multipart/ objects by handing it off to
  169.      the superclass method.  This includes calling the create_child and
  170.      close_child methods.
  171.    */
  172.   status = (((MimeObjectClass *)(&MIME_SUPERCLASS))
  173.             ->parse_line (line, length, obj));
  174.   if (status < 0) return status;
  175.  
  176.  
  177.   /* Now we want to do various other crypto-related things to these lines.
  178.    */
  179.  
  180.  
  181.   /* The instance variable MimeMultipartClass->state tracks motion through
  182.      the various stages of multipart/ parsing.  The instance variable
  183.      MimeMultipartSigned->state tracks the difference between the first
  184.      part (the body) and the second part (the signature.)  This second,
  185.      more specific state variable is updated by noticing the transitions
  186.      of the first, more general state variable.
  187.    */
  188.   if (old_state != mult->state)  /* there has been a state change */
  189.     {
  190.       switch (mult->state)
  191.         {
  192.         case MimeMultipartPreamble:
  193.           XP_ASSERT(0);  /* can't move *in* to preamble state. */
  194.           sig->state = MimeMultipartSignedPreamble;
  195.           break;
  196.  
  197.         case MimeMultipartHeaders:
  198.           /* If we're moving in to the Headers state, then that means
  199.              that this line is the preceeding boundary string (and we
  200.              should ignore it.)
  201.            */
  202.           hash_line_p = FALSE;
  203.  
  204.           if (sig->state == MimeMultipartSignedPreamble)
  205.             sig->state = MimeMultipartSignedBodyFirstHeader;
  206.           else if (sig->state == MimeMultipartSignedBodyFirstLine ||
  207.                    sig->state == MimeMultipartSignedBodyLine)
  208.             sig->state = MimeMultipartSignedSignatureHeaders;
  209.           else if (sig->state == MimeMultipartSignedSignatureFirstLine ||
  210.                    sig->state == MimeMultipartSignedSignatureLine)
  211.             sig->state = MimeMultipartSignedEpilogue;
  212.           break;
  213.  
  214.         case MimeMultipartPartFirstLine:
  215.           if (sig->state == MimeMultipartSignedBodyFirstHeader)
  216.             {            
  217.               sig->state = MimeMultipartSignedBodyFirstLine;
  218.               no_headers_p = TRUE;
  219.             }
  220.           else if (sig->state == MimeMultipartSignedBodyHeaders)
  221.             sig->state = MimeMultipartSignedBodyFirstLine;
  222.           else if (sig->state == MimeMultipartSignedSignatureHeaders)
  223.             sig->state = MimeMultipartSignedSignatureFirstLine;
  224.           else
  225.             sig->state = MimeMultipartSignedEpilogue;
  226.           break;
  227.  
  228.         case MimeMultipartPartLine:
  229.  
  230.           XP_ASSERT(sig->state == MimeMultipartSignedBodyFirstLine ||
  231.                     sig->state == MimeMultipartSignedBodyLine ||
  232.                     sig->state == MimeMultipartSignedSignatureFirstLine ||
  233.                     sig->state == MimeMultipartSignedSignatureLine);
  234.  
  235.           if (sig->state == MimeMultipartSignedBodyFirstLine)
  236.             sig->state = MimeMultipartSignedBodyLine;
  237.           else if (sig->state == MimeMultipartSignedSignatureFirstLine)
  238.             sig->state = MimeMultipartSignedSignatureLine;
  239.           break;
  240.  
  241.         case MimeMultipartEpilogue:
  242.           sig->state = MimeMultipartSignedEpilogue;
  243.           break;
  244.  
  245.         default:  /* bad state */
  246.           XP_ASSERT(0);
  247.           return -1;
  248.           break;
  249.         }
  250.     }
  251.  
  252.  
  253.   /* Perform multipart/signed-related actions on this line based on the state
  254.      of the parser.
  255.    */
  256.   switch (sig->state)
  257.     {
  258.     case MimeMultipartSignedPreamble:
  259.       /* Do nothing. */
  260.       break;
  261.  
  262.     case MimeMultipartSignedBodyFirstLine:
  263.       /* We have just moved out of the MimeMultipartSignedBodyHeaders
  264.          state, so cache away the headers that apply only to the body part.
  265.        */
  266.       XP_ASSERT(mult->hdrs);
  267.       XP_ASSERT(!sig->body_hdrs);
  268.       sig->body_hdrs = mult->hdrs;
  269.       mult->hdrs = 0;
  270.  
  271.       /* fall through. */
  272.  
  273.     case MimeMultipartSignedBodyFirstHeader:
  274.     case MimeMultipartSignedBodyHeaders:
  275.     case MimeMultipartSignedBodyLine:
  276.  
  277.       if (!sig->crypto_closure)
  278.         {
  279.           XP_SetError(0);
  280.           /* Initialize the signature verification library. */
  281.           sig->crypto_closure = (((MimeMultipartSignedClass *) obj->class)
  282.                                  ->crypto_init) (obj);
  283.           if (!sig->crypto_closure)
  284.             {
  285.               status = PR_GetError();
  286.               XP_ASSERT(status < 0);
  287.               if (status >= 0) status = -1;
  288.               return status;
  289.             }
  290.         }
  291.  
  292.       if (hash_line_p)
  293.         {
  294.           /* this is the first hashed line if this is the first header
  295.              (that is, if it's the first line in the header state after
  296.              a state change.)
  297.            */
  298.           XP_Bool first_line_p
  299.             = (no_headers_p ||
  300.                sig->state == MimeMultipartSignedBodyFirstHeader);
  301.  
  302.           if (sig->state == MimeMultipartSignedBodyFirstHeader)
  303.             sig->state = MimeMultipartSignedBodyHeaders;
  304.  
  305.           /* The newline issues here are tricky, since both the newlines
  306.              before and after the boundary string are to be considered part
  307.              of the boundary: this is so that a part can be specified such
  308.              that it does not end in a trailing newline.
  309.  
  310.              To implement this, we send a newline *before* each line instead
  311.              of after, except for the first line, which is not preceeded by a
  312.              newline.
  313.  
  314.              For purposes of cryptographic hashing, we always hash line
  315.              breaks as CRLF -- the canonical, on-the-wire linebreaks, since
  316.              we have no idea of knowing what line breaks were used on the
  317.              originating system (SMTP rightly destroys that information.)
  318.            */
  319.  
  320.           /* Remove the trailing newline... */
  321.           if (length > 0 && line[length-1] == LF) length--;
  322.           if (length > 0 && line[length-1] == CR) length--;
  323.  
  324.           XP_ASSERT(sig->crypto_closure);
  325.  
  326.           if (!first_line_p)
  327.             {
  328.               /* Push out a preceeding newline... */
  329.               char nl[] = CRLF;
  330.               status = (((MimeMultipartSignedClass *) obj->class)
  331.                         ->crypto_data_hash (nl, 2, sig->crypto_closure));
  332.               if (status < 0) return status;
  333.             }
  334.  
  335.           /* Now push out the line sans trailing newline. */
  336.           if (length > 0)
  337.             status = (((MimeMultipartSignedClass *) obj->class)
  338.                       ->crypto_data_hash (line,length, sig->crypto_closure));
  339.           if (status < 0) return status;
  340.         }
  341.       break;
  342.  
  343.     case MimeMultipartSignedSignatureHeaders:
  344.  
  345.       if (sig->crypto_closure &&
  346.           old_state != mult->state)
  347.         {
  348.           /* We have just moved out of the MimeMultipartSignedBodyLine
  349.              state, so tell the signature verification library that we've
  350.              reached the end of the signed data.
  351.            */
  352.           status = (((MimeMultipartSignedClass *) obj->class)
  353.                     ->crypto_data_eof) (sig->crypto_closure, FALSE);
  354.           if (status < 0) return status;
  355.         }
  356.       break;
  357.  
  358.     case MimeMultipartSignedSignatureFirstLine:
  359.       /* We have just moved out of the MimeMultipartSignedSignatureHeaders
  360.          state, so cache away the headers that apply only to the sig part.
  361.        */
  362.       XP_ASSERT(mult->hdrs);
  363.       XP_ASSERT(!sig->sig_hdrs);
  364.       sig->sig_hdrs = mult->hdrs;
  365.       mult->hdrs = 0;
  366.  
  367.  
  368.       /* If the signature block has an encoding, set up a decoder for it.
  369.          (Similar logic is in MimeLeafClass->parse_begin.)
  370.        */
  371.       {
  372.         MimeDecoderData *(*fn) (int (*) (const char*, int32,void*), void*) = 0;
  373.         char *encoding = MimeHeaders_get (sig->sig_hdrs,
  374.                                           HEADER_CONTENT_TRANSFER_ENCODING,
  375.                                           TRUE, FALSE);
  376.         if (!encoding)
  377.           ;
  378.         else if (!strcasecomp(encoding, ENCODING_BASE64))
  379.           fn = &MimeB64DecoderInit;
  380.         else if (!strcasecomp(encoding, ENCODING_QUOTED_PRINTABLE))
  381.           fn = &MimeQPDecoderInit;
  382.         else if (!strcasecomp(encoding, ENCODING_UUENCODE) ||
  383.                  !strcasecomp(encoding, ENCODING_UUENCODE2) ||
  384.                  !strcasecomp(encoding, ENCODING_UUENCODE3) ||
  385.                  !strcasecomp(encoding, ENCODING_UUENCODE4))
  386.           fn = &MimeUUDecoderInit;
  387.  
  388.         if (fn)
  389.           {
  390.             sig->sig_decoder_data =
  391.               fn (((int (*) (const char *, int32, void *))
  392.                    (((MimeMultipartSignedClass *) obj->class)
  393.                     ->crypto_signature_hash)),
  394.                   sig->crypto_closure);
  395.             if (!sig->sig_decoder_data)
  396.               return MK_OUT_OF_MEMORY;
  397.           }
  398.       }
  399.  
  400.       /* Show these headers to the crypto module. */
  401.       if (hash_line_p)
  402.         {
  403.           status = (((MimeMultipartSignedClass *) obj->class)
  404.                     ->crypto_signature_init) (sig->crypto_closure,
  405.                                               obj, sig->sig_hdrs);
  406.           if (status < 0) return status;
  407.         }
  408.  
  409.       /* fall through. */
  410.  
  411.     case MimeMultipartSignedSignatureLine:
  412.       if (hash_line_p)
  413.         {
  414.           /* Feed this line into the signature verification routines. */
  415.  
  416.           if (sig->sig_decoder_data)
  417.             status = MimeDecoderWrite (sig->sig_decoder_data, line, length);
  418.           else
  419.             status = (((MimeMultipartSignedClass *) obj->class)
  420.                       ->crypto_signature_hash (line, length,
  421.                                                sig->crypto_closure));
  422.           if (status < 0) return status;
  423.         }
  424.       break;
  425.  
  426.     case MimeMultipartSignedEpilogue:
  427.       /* Nothing special to do here. */
  428.       break;
  429.  
  430.     default:  /* bad state */
  431.       XP_ASSERT(0);
  432.       return -1;
  433.     }
  434.  
  435.   return status;
  436. }
  437.  
  438.  
  439. static int
  440. MimeMultipartSigned_create_child (MimeObject *parent)
  441. {
  442.   /* Don't actually create a child -- we call the superclass create_child
  443.      method later, after we've fully parsed everything.  (And we only call
  444.      it once, for part #1, and never for part #2 (the signature.))
  445.    */
  446.   MimeMultipart *mult = (MimeMultipart *) parent;
  447.   mult->state = MimeMultipartPartFirstLine;
  448.   return 0;
  449. }
  450.  
  451.  
  452. static int
  453. MimeMultipartSigned_close_child (MimeObject *obj)
  454. {
  455.   /* The close_child method on MimeMultipartSigned doesn't actually do
  456.      anything to the children list, since the create_child method also
  457.      doesn't do anything.
  458.    */
  459.   MimeMultipart *mult = (MimeMultipart *) obj;
  460.   MimeContainer *cont = (MimeContainer *) obj;
  461.   MimeMultipartSigned *msig = (MimeMultipartSigned *) obj;
  462.  
  463.   if (msig->part_buffer)
  464.     /* Closes the tmp file, if there is one: doesn't free the part_buffer. */
  465.     MimePartBufferClose(msig->part_buffer);
  466.  
  467.   if (mult->hdrs)    /* duplicated from MimeMultipart_close_child, ugh. */
  468.     {
  469.       MimeHeaders_free(mult->hdrs);
  470.       mult->hdrs = 0;
  471.     }
  472.  
  473.   /* Should be no kids yet. */
  474.   XP_ASSERT(cont->nchildren == 0);
  475.   if (cont->nchildren != 0) return -1;
  476.  
  477.   return 0;
  478. }
  479.  
  480.  
  481. static int
  482. MimeMultipartSigned_parse_child_line (MimeObject *obj,
  483.                                       char *line, int32 length,
  484.                                       XP_Bool first_line_p)
  485. {
  486.   MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
  487.   MimeContainer *cont = (MimeContainer *) obj;
  488.   int status = 0;
  489.  
  490.   /* Shouldn't have made any sub-parts yet. */
  491.   XP_ASSERT(cont->nchildren == 0);
  492.   if (cont->nchildren != 0) return -1;
  493.  
  494.   switch (sig->state)
  495.     {
  496.     case MimeMultipartSignedPreamble:
  497.     case MimeMultipartSignedBodyFirstHeader:
  498.     case MimeMultipartSignedBodyHeaders:
  499.       XP_ASSERT(0);  /* How'd we get here?  Oh well, fall through. */
  500.  
  501.     case MimeMultipartSignedBodyFirstLine:
  502.       XP_ASSERT(first_line_p);
  503.       if (!sig->part_buffer)
  504.         {
  505.           sig->part_buffer = MimePartBufferCreate();
  506.           if (!sig->part_buffer)
  507.             return MK_OUT_OF_MEMORY;
  508.         }
  509.       /* fall through */
  510.  
  511.     case MimeMultipartSignedBodyLine:
  512.       {
  513.         /* This is the first part; we are buffering it, and will emit it all
  514.            at the end (so that we know whether the signature matches before
  515.            showing anything to the user.)
  516.          */
  517.  
  518.         /* The newline issues here are tricky, since both the newlines
  519.            before and after the boundary string are to be considered part
  520.            of the boundary: this is so that a part can be specified such
  521.            that it does not end in a trailing newline.
  522.  
  523.            To implement this, we send a newline *before* each line instead
  524.            of after, except for the first line, which is not preceeded by a
  525.            newline.
  526.          */
  527.  
  528.         /* Remove the trailing newline... */
  529.         if (length > 0 && line[length-1] == LF) length--;
  530.         if (length > 0 && line[length-1] == CR) length--;
  531.  
  532.         XP_ASSERT(sig->part_buffer);
  533.         XP_ASSERT(first_line_p ==
  534.                   (sig->state == MimeMultipartSignedBodyFirstLine));
  535.  
  536.         if (!first_line_p)
  537.           {
  538.             /* Push out a preceeding newline... */
  539.             char nl[] = LINEBREAK;
  540.             status = MimePartBufferWrite (sig->part_buffer, nl, LINEBREAK_LEN);
  541.             if (status < 0) return status;
  542.           }
  543.  
  544.         /* Now push out the line sans trailing newline. */
  545.         if (length > 0)
  546.             status = MimePartBufferWrite (sig->part_buffer, line, length);
  547.         if (status < 0) return status;
  548.       }
  549.     break;
  550.  
  551.     case MimeMultipartSignedSignatureHeaders:
  552.       XP_ASSERT(0);  /* How'd we get here?  Oh well, fall through. */
  553.  
  554.     case MimeMultipartSignedSignatureFirstLine:
  555.     case MimeMultipartSignedSignatureLine:
  556.       /* Nothing to do here -- hashing of the signature part is handled up
  557.          in MimeMultipartSigned_parse_line().
  558.        */
  559.       break;
  560.  
  561.     case MimeMultipartSignedEpilogue:
  562.       /* Too many kids?  MimeMultipartSigned_create_child() should have
  563.          prevented us from getting here. */
  564.       XP_ASSERT(0);
  565.       return -1;
  566.       break;
  567.  
  568.     default: /* bad state */
  569.       XP_ASSERT(0);
  570.       return -1;
  571.       break;
  572.     }
  573.  
  574.   return status;
  575. }
  576.  
  577.  
  578. static int
  579. MimeMultipartSigned_emit_child (MimeObject *obj)
  580. {
  581.   MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
  582.   MimeMultipart *mult = (MimeMultipart *) obj;
  583.   MimeContainer *cont = (MimeContainer *) obj;
  584.   int status = 0;
  585.   MimeObject *body;
  586.  
  587.   XP_ASSERT(sig->crypto_closure);
  588.  
  589.   /* Emit some HTML saying whether the signature was cool.
  590.      But don't emit anything if in FO_QUOTE_MESSAGE mode.
  591.    */
  592.   if (obj->options &&
  593.       obj->options->headers != MimeHeadersCitation &&
  594.       obj->options->write_html_p &&
  595.       obj->options->output_fn &&
  596.       obj->options->headers != MimeHeadersCitation &&
  597.       sig->crypto_closure)
  598.     {
  599.       char *html = (((MimeMultipartSignedClass *) obj->class)
  600.                     ->crypto_generate_html (sig->crypto_closure));
  601.       if (!html) return -1; /* MK_OUT_OF_MEMORY? */
  602.  
  603.       status = MimeObject_write(obj, html, XP_STRLEN(html), FALSE);
  604.       XP_FREE(html);
  605.       if (status < 0) return status;
  606.  
  607.       /* Now that we have written out the crypto stamp, the outermost header
  608.          block is well and truly closed.  If this is in fact the outermost
  609.          message, then run the post_header_html_fn now.
  610.        */
  611.       if (obj->options &&
  612.           obj->options->state &&
  613.           obj->options->generate_post_header_html_fn &&
  614.           !obj->options->state->post_header_html_run_p)
  615.         {
  616.           MimeHeaders *outer_headers;
  617.           MimeObject *p;
  618.           for (p = obj; p->parent; p = p->parent)
  619.             outer_headers = p->headers;
  620.           XP_ASSERT(obj->options->state->first_data_written_p);
  621.           html = obj->options->generate_post_header_html_fn(NULL,
  622.                                                     obj->options->html_closure,
  623.                                                             outer_headers);
  624.           obj->options->state->post_header_html_run_p = TRUE;
  625.           if (html)
  626.             {
  627.               status = MimeObject_write(obj, html, XP_STRLEN(html), FALSE);
  628.               XP_FREE(html);
  629.               if (status < 0) return status;
  630.             }
  631.         }
  632.     }
  633.  
  634.  
  635.   /* Oh, this is fairly nasty.  We're skipping over our "create child" method
  636.      and using the one our superclass defines.  Perhaps instead we should add
  637.      a new method on this class, and initialize that method to be the
  638.      create_child method of the superclass.  Whatever.
  639.    */
  640.  
  641.  
  642.   /* The superclass method expects to find the headers for the part that it's
  643.      to create in mult->hdrs, so ensure that they're there. */
  644.   XP_ASSERT(!mult->hdrs);
  645.   if (mult->hdrs) MimeHeaders_free(mult->hdrs);
  646.   mult->hdrs = sig->body_hdrs;
  647.   sig->body_hdrs = 0;
  648.  
  649.   /* Run the superclass create_child method.
  650.    */
  651.   status = (((MimeMultipartClass *)(&MIME_SUPERCLASS))->create_child(obj));
  652.   if (status < 0) return status;
  653.  
  654.   /* Retrieve the child that it created.
  655.    */
  656.   XP_ASSERT(cont->nchildren == 1);
  657.   if (cont->nchildren != 1) return -1;
  658.   body = cont->children[0];
  659.   XP_ASSERT(body);
  660.   if (!body) return -1;
  661.  
  662. #ifdef MIME_DRAFTS
  663.   if (body->options->decompose_file_p) {
  664.       body->options->signed_p = TRUE;
  665.       if (!mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
  666.         body->options->decompose_file_init_fn)
  667.         body->options->decompose_file_init_fn ( body->options->stream_closure, body->headers );
  668.   }
  669. #endif /* MIME_DRAFTS */
  670.  
  671.   /* If there's no part_buffer, this is a zero-length signed message? */
  672.   if (sig->part_buffer)
  673.     {
  674. #ifdef MIME_DRAFTS
  675.       if (body->options->decompose_file_p &&
  676.           !mime_typep(body, (MimeObjectClass*)&mimeMultipartClass)  &&
  677.           body->options->decompose_file_output_fn)
  678.           status = MimePartBufferRead (sig->part_buffer,
  679.                                /* The (int (*) ...) cast is to turn the
  680.                                   `void' argument into `MimeObject'. */
  681.                                ((int (*) (char *, int32, void *))
  682.                                body->options->decompose_file_output_fn),
  683.                                body->options->stream_closure);
  684.       else
  685. #endif /* MIME_DRAFTS */
  686.  
  687.       status = MimePartBufferRead (sig->part_buffer,
  688.                                /* The (int (*) ...) cast is to turn the
  689.                                   `void' argument into `MimeObject'. */
  690.                                ((int (*) (char *, int32, void *))
  691.                                 body->class->parse_buffer),
  692.                                 body);
  693.       if (status < 0) return status;
  694.     }
  695.  
  696.   MimeMultipartSigned_cleanup(obj, FALSE);
  697.  
  698.   /* Done parsing. */
  699.   status = body->class->parse_eof(body, FALSE);
  700.   if (status < 0) return status;
  701.   status = body->class->parse_end(body, FALSE);
  702.   if (status < 0) return status;
  703.  
  704. #ifdef MIME_DRAFTS
  705.   if (body->options->decompose_file_p &&
  706.       !mime_typep(body, (MimeObjectClass*)&mimeMultipartClass)  &&
  707.       body->options->decompose_file_close_fn)
  708.       body->options->decompose_file_close_fn(body->options->stream_closure);
  709. #endif /* MIME_DRAFTS */
  710.  
  711.   /* Put out a separator after every multipart/signed object. */
  712.   status = MimeObject_write_separator(obj);
  713.   if (status < 0) return status;
  714.  
  715.   return 0;
  716. }
  717.