1998-03-28 02:44:41 +00:00
|
|
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Netscape Public License
|
|
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
|
|
* http://www.mozilla.org/NPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* NPL.
|
|
|
|
*
|
|
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
|
|
* Communications Corporation. Portions created by Netscape are
|
|
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
|
|
* Reserved.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* mimemsig.c --- definition of the MimeMultipartSigned class (see mimei.h)
|
|
|
|
Created: Jamie Zawinski <jwz@netscape.com>, 23-Sep-96.
|
|
|
|
*/
|
|
|
|
|
1998-07-24 21:05:24 +00:00
|
|
|
#include "rosetta.h"
|
1998-03-28 02:44:41 +00:00
|
|
|
#include "mimemsig.h"
|
|
|
|
#include "nspr.h"
|
1998-07-02 02:38:19 +00:00
|
|
|
#include "xp_error.h"
|
1998-03-28 02:44:41 +00:00
|
|
|
|
|
|
|
#define MIME_SUPERCLASS mimeMultipartClass
|
|
|
|
MimeDefClass(MimeMultipartSigned, MimeMultipartSignedClass,
|
|
|
|
mimeMultipartSignedClass, &MIME_SUPERCLASS);
|
|
|
|
|
|
|
|
static int MimeMultipartSigned_initialize (MimeObject *);
|
|
|
|
static int MimeMultipartSigned_create_child (MimeObject *);
|
|
|
|
static int MimeMultipartSigned_close_child(MimeObject *);
|
|
|
|
static int MimeMultipartSigned_parse_line (char *, int32, MimeObject *);
|
|
|
|
static int MimeMultipartSigned_parse_child_line (MimeObject *, char *, int32,
|
|
|
|
XP_Bool);
|
|
|
|
static int MimeMultipartSigned_parse_eof (MimeObject *, XP_Bool);
|
|
|
|
static void MimeMultipartSigned_finalize (MimeObject *);
|
|
|
|
|
|
|
|
static int MimeMultipartSigned_emit_child (MimeObject *obj);
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartSignedClassInitialize(MimeMultipartSignedClass *class)
|
|
|
|
{
|
|
|
|
MimeObjectClass *oclass = (MimeObjectClass *) class;
|
|
|
|
MimeMultipartClass *mclass = (MimeMultipartClass *) class;
|
|
|
|
|
|
|
|
oclass->initialize = MimeMultipartSigned_initialize;
|
|
|
|
oclass->parse_line = MimeMultipartSigned_parse_line;
|
|
|
|
oclass->parse_eof = MimeMultipartSigned_parse_eof;
|
|
|
|
oclass->finalize = MimeMultipartSigned_finalize;
|
|
|
|
mclass->create_child = MimeMultipartSigned_create_child;
|
|
|
|
mclass->parse_child_line = MimeMultipartSigned_parse_child_line;
|
|
|
|
mclass->close_child = MimeMultipartSigned_close_child;
|
|
|
|
|
|
|
|
XP_ASSERT(!oclass->class_initialized);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartSigned_initialize (MimeObject *object)
|
|
|
|
{
|
|
|
|
MimeMultipartSigned *sig = (MimeMultipartSigned *) object;
|
|
|
|
|
|
|
|
/* This is an abstract class; it shouldn't be directly instantiated. */
|
|
|
|
XP_ASSERT(object->class != (MimeObjectClass *) &mimeMultipartSignedClass);
|
|
|
|
|
|
|
|
sig->state = MimeMultipartSignedPreamble;
|
|
|
|
|
|
|
|
return ((MimeObjectClass*)&MIME_SUPERCLASS)->initialize(object);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
MimeMultipartSigned_cleanup (MimeObject *obj, XP_Bool finalizing_p)
|
|
|
|
{
|
|
|
|
MimeMultipart *mult = (MimeMultipart *) obj; /* #58075. Fix suggested by jwz */
|
|
|
|
MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
|
|
|
|
if (sig->part_buffer)
|
|
|
|
{
|
|
|
|
MimePartBufferDestroy(sig->part_buffer);
|
|
|
|
sig->part_buffer = 0;
|
|
|
|
}
|
|
|
|
if (sig->body_hdrs)
|
|
|
|
{
|
|
|
|
MimeHeaders_free (sig->body_hdrs);
|
|
|
|
sig->body_hdrs = 0;
|
|
|
|
}
|
|
|
|
if (sig->sig_hdrs)
|
|
|
|
{
|
|
|
|
MimeHeaders_free (sig->sig_hdrs);
|
|
|
|
sig->sig_hdrs = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
mult->state = MimeMultipartEpilogue; /* #58075. Fix suggested by jwz */
|
|
|
|
sig->state = MimeMultipartSignedEpilogue;
|
|
|
|
|
1998-07-24 21:05:24 +00:00
|
|
|
HG09862
|
1998-03-28 02:44:41 +00:00
|
|
|
|
|
|
|
if (sig->sig_decoder_data)
|
|
|
|
{
|
|
|
|
MimeDecoderDestroy(sig->sig_decoder_data, TRUE);
|
|
|
|
sig->sig_decoder_data = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartSigned_parse_eof (MimeObject *obj, XP_Bool abort_p)
|
|
|
|
{
|
|
|
|
MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
if (obj->closed_p) return 0;
|
|
|
|
|
|
|
|
/* Close off the signature, if we've gotten that far.
|
|
|
|
*/
|
|
|
|
if (sig->state == MimeMultipartSignedSignatureHeaders ||
|
|
|
|
sig->state == MimeMultipartSignedSignatureFirstLine ||
|
|
|
|
sig->state == MimeMultipartSignedSignatureLine ||
|
|
|
|
sig->state == MimeMultipartSignedEpilogue)
|
|
|
|
{
|
1998-07-24 21:05:24 +00:00
|
|
|
HG09863
|
1998-03-28 02:44:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!abort_p)
|
|
|
|
{
|
|
|
|
/* Now that we've read both the signed object and the signature (and
|
|
|
|
have presumably verified the signature) write out a blurb, and then
|
|
|
|
the signed object.
|
|
|
|
*/
|
1998-07-24 21:05:24 +00:00
|
|
|
HG09864
|
1998-03-28 02:44:41 +00:00
|
|
|
status = MimeMultipartSigned_emit_child(obj);
|
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
MimeMultipartSigned_cleanup(obj, FALSE);
|
|
|
|
return ((MimeObjectClass*)&MIME_SUPERCLASS)->parse_eof(obj, abort_p);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
MimeMultipartSigned_finalize (MimeObject *obj)
|
|
|
|
{
|
|
|
|
MimeMultipartSigned_cleanup(obj, TRUE);
|
|
|
|
((MimeObjectClass*)&MIME_SUPERCLASS)->finalize(obj);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartSigned_parse_line (char *line, int32 length, MimeObject *obj)
|
|
|
|
{
|
|
|
|
MimeMultipart *mult = (MimeMultipart *) obj;
|
|
|
|
MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
|
|
|
|
MimeMultipartParseState old_state = mult->state;
|
|
|
|
XP_Bool hash_line_p = TRUE;
|
|
|
|
XP_Bool no_headers_p = FALSE;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
/* First do the parsing for normal multipart/ objects by handing it off to
|
|
|
|
the superclass method. This includes calling the create_child and
|
|
|
|
close_child methods.
|
|
|
|
*/
|
|
|
|
status = (((MimeObjectClass *)(&MIME_SUPERCLASS))
|
|
|
|
->parse_line (line, length, obj));
|
|
|
|
if (status < 0) return status;
|
|
|
|
|
|
|
|
|
1998-07-24 21:05:24 +00:00
|
|
|
HG09865
|
1998-03-28 02:44:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* The instance variable MimeMultipartClass->state tracks motion through
|
|
|
|
the various stages of multipart/ parsing. The instance variable
|
|
|
|
MimeMultipartSigned->state tracks the difference between the first
|
|
|
|
part (the body) and the second part (the signature.) This second,
|
|
|
|
more specific state variable is updated by noticing the transitions
|
|
|
|
of the first, more general state variable.
|
|
|
|
*/
|
|
|
|
if (old_state != mult->state) /* there has been a state change */
|
|
|
|
{
|
|
|
|
switch (mult->state)
|
|
|
|
{
|
|
|
|
case MimeMultipartPreamble:
|
|
|
|
XP_ASSERT(0); /* can't move *in* to preamble state. */
|
|
|
|
sig->state = MimeMultipartSignedPreamble;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MimeMultipartHeaders:
|
|
|
|
/* If we're moving in to the Headers state, then that means
|
|
|
|
that this line is the preceeding boundary string (and we
|
|
|
|
should ignore it.)
|
|
|
|
*/
|
|
|
|
hash_line_p = FALSE;
|
|
|
|
|
|
|
|
if (sig->state == MimeMultipartSignedPreamble)
|
|
|
|
sig->state = MimeMultipartSignedBodyFirstHeader;
|
|
|
|
else if (sig->state == MimeMultipartSignedBodyFirstLine ||
|
|
|
|
sig->state == MimeMultipartSignedBodyLine)
|
|
|
|
sig->state = MimeMultipartSignedSignatureHeaders;
|
|
|
|
else if (sig->state == MimeMultipartSignedSignatureFirstLine ||
|
|
|
|
sig->state == MimeMultipartSignedSignatureLine)
|
|
|
|
sig->state = MimeMultipartSignedEpilogue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MimeMultipartPartFirstLine:
|
|
|
|
if (sig->state == MimeMultipartSignedBodyFirstHeader)
|
|
|
|
{
|
|
|
|
sig->state = MimeMultipartSignedBodyFirstLine;
|
|
|
|
no_headers_p = TRUE;
|
|
|
|
}
|
|
|
|
else if (sig->state == MimeMultipartSignedBodyHeaders)
|
|
|
|
sig->state = MimeMultipartSignedBodyFirstLine;
|
|
|
|
else if (sig->state == MimeMultipartSignedSignatureHeaders)
|
|
|
|
sig->state = MimeMultipartSignedSignatureFirstLine;
|
|
|
|
else
|
|
|
|
sig->state = MimeMultipartSignedEpilogue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MimeMultipartPartLine:
|
|
|
|
|
|
|
|
XP_ASSERT(sig->state == MimeMultipartSignedBodyFirstLine ||
|
|
|
|
sig->state == MimeMultipartSignedBodyLine ||
|
|
|
|
sig->state == MimeMultipartSignedSignatureFirstLine ||
|
|
|
|
sig->state == MimeMultipartSignedSignatureLine);
|
|
|
|
|
|
|
|
if (sig->state == MimeMultipartSignedBodyFirstLine)
|
|
|
|
sig->state = MimeMultipartSignedBodyLine;
|
|
|
|
else if (sig->state == MimeMultipartSignedSignatureFirstLine)
|
|
|
|
sig->state = MimeMultipartSignedSignatureLine;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MimeMultipartEpilogue:
|
|
|
|
sig->state = MimeMultipartSignedEpilogue;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: /* bad state */
|
|
|
|
XP_ASSERT(0);
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Perform multipart/signed-related actions on this line based on the state
|
|
|
|
of the parser.
|
|
|
|
*/
|
|
|
|
switch (sig->state)
|
|
|
|
{
|
|
|
|
case MimeMultipartSignedPreamble:
|
|
|
|
/* Do nothing. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MimeMultipartSignedBodyFirstLine:
|
|
|
|
/* We have just moved out of the MimeMultipartSignedBodyHeaders
|
|
|
|
state, so cache away the headers that apply only to the body part.
|
|
|
|
*/
|
|
|
|
XP_ASSERT(mult->hdrs);
|
|
|
|
XP_ASSERT(!sig->body_hdrs);
|
|
|
|
sig->body_hdrs = mult->hdrs;
|
|
|
|
mult->hdrs = 0;
|
|
|
|
|
|
|
|
/* fall through. */
|
|
|
|
|
|
|
|
case MimeMultipartSignedBodyFirstHeader:
|
|
|
|
case MimeMultipartSignedBodyHeaders:
|
|
|
|
case MimeMultipartSignedBodyLine:
|
|
|
|
|
1998-07-24 21:05:24 +00:00
|
|
|
HG09866
|
1998-03-28 02:44:41 +00:00
|
|
|
|
|
|
|
if (hash_line_p)
|
|
|
|
{
|
|
|
|
/* this is the first hashed line if this is the first header
|
|
|
|
(that is, if it's the first line in the header state after
|
|
|
|
a state change.)
|
|
|
|
*/
|
|
|
|
XP_Bool first_line_p
|
|
|
|
= (no_headers_p ||
|
|
|
|
sig->state == MimeMultipartSignedBodyFirstHeader);
|
|
|
|
|
|
|
|
if (sig->state == MimeMultipartSignedBodyFirstHeader)
|
|
|
|
sig->state = MimeMultipartSignedBodyHeaders;
|
|
|
|
|
|
|
|
/* The newline issues here are tricky, since both the newlines
|
|
|
|
before and after the boundary string are to be considered part
|
|
|
|
of the boundary: this is so that a part can be specified such
|
|
|
|
that it does not end in a trailing newline.
|
|
|
|
|
|
|
|
To implement this, we send a newline *before* each line instead
|
|
|
|
of after, except for the first line, which is not preceeded by a
|
|
|
|
newline.
|
|
|
|
|
1998-07-24 21:05:24 +00:00
|
|
|
For purposes of bigfun hashing, we always hash line
|
1998-03-28 02:44:41 +00:00
|
|
|
breaks as CRLF -- the canonical, on-the-wire linebreaks, since
|
|
|
|
we have no idea of knowing what line breaks were used on the
|
|
|
|
originating system (SMTP rightly destroys that information.)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Remove the trailing newline... */
|
|
|
|
if (length > 0 && line[length-1] == LF) length--;
|
|
|
|
if (length > 0 && line[length-1] == CR) length--;
|
|
|
|
|
1998-07-24 21:05:24 +00:00
|
|
|
HG09867
|
1998-03-28 02:44:41 +00:00
|
|
|
|
|
|
|
if (!first_line_p)
|
|
|
|
{
|
|
|
|
/* Push out a preceeding newline... */
|
|
|
|
char nl[] = CRLF;
|
1998-07-24 21:05:24 +00:00
|
|
|
|
1998-03-28 02:44:41 +00:00
|
|
|
status = (((MimeMultipartSignedClass *) obj->class)
|
1998-07-24 21:05:24 +00:00
|
|
|
->bigfun_data_hash (nl, 2, sig->bigfun_closure));
|
1998-03-28 02:44:41 +00:00
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now push out the line sans trailing newline. */
|
|
|
|
if (length > 0)
|
|
|
|
status = (((MimeMultipartSignedClass *) obj->class)
|
1998-07-24 21:05:24 +00:00
|
|
|
->bigfun_data_hash (line,length, sig->bigfun_closure));
|
1998-03-28 02:44:41 +00:00
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MimeMultipartSignedSignatureHeaders:
|
|
|
|
|
1998-07-24 21:05:24 +00:00
|
|
|
if (sig->bigfun_closure &&
|
1998-03-28 02:44:41 +00:00
|
|
|
old_state != mult->state)
|
|
|
|
{
|
|
|
|
/* We have just moved out of the MimeMultipartSignedBodyLine
|
|
|
|
state, so tell the signature verification library that we've
|
|
|
|
reached the end of the signed data.
|
|
|
|
*/
|
|
|
|
status = (((MimeMultipartSignedClass *) obj->class)
|
1998-07-24 21:05:24 +00:00
|
|
|
->bigfun_data_eof) (sig->bigfun_closure, FALSE);
|
1998-03-28 02:44:41 +00:00
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MimeMultipartSignedSignatureFirstLine:
|
|
|
|
/* We have just moved out of the MimeMultipartSignedSignatureHeaders
|
|
|
|
state, so cache away the headers that apply only to the sig part.
|
|
|
|
*/
|
|
|
|
XP_ASSERT(mult->hdrs);
|
|
|
|
XP_ASSERT(!sig->sig_hdrs);
|
|
|
|
sig->sig_hdrs = mult->hdrs;
|
|
|
|
mult->hdrs = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* If the signature block has an encoding, set up a decoder for it.
|
|
|
|
(Similar logic is in MimeLeafClass->parse_begin.)
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
MimeDecoderData *(*fn) (int (*) (const char*, int32,void*), void*) = 0;
|
|
|
|
char *encoding = MimeHeaders_get (sig->sig_hdrs,
|
|
|
|
HEADER_CONTENT_TRANSFER_ENCODING,
|
|
|
|
TRUE, FALSE);
|
|
|
|
if (!encoding)
|
|
|
|
;
|
|
|
|
else if (!strcasecomp(encoding, ENCODING_BASE64))
|
|
|
|
fn = &MimeB64DecoderInit;
|
|
|
|
else if (!strcasecomp(encoding, ENCODING_QUOTED_PRINTABLE))
|
|
|
|
fn = &MimeQPDecoderInit;
|
|
|
|
else if (!strcasecomp(encoding, ENCODING_UUENCODE) ||
|
|
|
|
!strcasecomp(encoding, ENCODING_UUENCODE2) ||
|
|
|
|
!strcasecomp(encoding, ENCODING_UUENCODE3) ||
|
|
|
|
!strcasecomp(encoding, ENCODING_UUENCODE4))
|
|
|
|
fn = &MimeUUDecoderInit;
|
|
|
|
|
|
|
|
if (fn)
|
|
|
|
{
|
|
|
|
sig->sig_decoder_data =
|
|
|
|
fn (((int (*) (const char *, int32, void *))
|
|
|
|
(((MimeMultipartSignedClass *) obj->class)
|
1998-07-24 21:05:24 +00:00
|
|
|
->bigfun_signature_hash)),
|
|
|
|
sig->bigfun_closure);
|
1998-03-28 02:44:41 +00:00
|
|
|
if (!sig->sig_decoder_data)
|
|
|
|
return MK_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-07-24 21:05:24 +00:00
|
|
|
/* Show these headers to the bigfun module. */
|
1998-03-28 02:44:41 +00:00
|
|
|
if (hash_line_p)
|
|
|
|
{
|
|
|
|
status = (((MimeMultipartSignedClass *) obj->class)
|
1998-07-24 21:05:24 +00:00
|
|
|
->bigfun_signature_init) (sig->bigfun_closure,
|
1998-03-28 02:44:41 +00:00
|
|
|
obj, sig->sig_hdrs);
|
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fall through. */
|
|
|
|
|
|
|
|
case MimeMultipartSignedSignatureLine:
|
|
|
|
if (hash_line_p)
|
|
|
|
{
|
|
|
|
/* Feed this line into the signature verification routines. */
|
|
|
|
|
|
|
|
if (sig->sig_decoder_data)
|
|
|
|
status = MimeDecoderWrite (sig->sig_decoder_data, line, length);
|
|
|
|
else
|
|
|
|
status = (((MimeMultipartSignedClass *) obj->class)
|
1998-07-24 21:05:24 +00:00
|
|
|
->bigfun_signature_hash (line, length,
|
|
|
|
sig->bigfun_closure));
|
1998-03-28 02:44:41 +00:00
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MimeMultipartSignedEpilogue:
|
|
|
|
/* Nothing special to do here. */
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: /* bad state */
|
|
|
|
XP_ASSERT(0);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartSigned_create_child (MimeObject *parent)
|
|
|
|
{
|
|
|
|
/* Don't actually create a child -- we call the superclass create_child
|
|
|
|
method later, after we've fully parsed everything. (And we only call
|
|
|
|
it once, for part #1, and never for part #2 (the signature.))
|
|
|
|
*/
|
|
|
|
MimeMultipart *mult = (MimeMultipart *) parent;
|
|
|
|
mult->state = MimeMultipartPartFirstLine;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartSigned_close_child (MimeObject *obj)
|
|
|
|
{
|
|
|
|
/* The close_child method on MimeMultipartSigned doesn't actually do
|
|
|
|
anything to the children list, since the create_child method also
|
|
|
|
doesn't do anything.
|
|
|
|
*/
|
|
|
|
MimeMultipart *mult = (MimeMultipart *) obj;
|
|
|
|
MimeContainer *cont = (MimeContainer *) obj;
|
|
|
|
MimeMultipartSigned *msig = (MimeMultipartSigned *) obj;
|
|
|
|
|
|
|
|
if (msig->part_buffer)
|
|
|
|
/* Closes the tmp file, if there is one: doesn't free the part_buffer. */
|
|
|
|
MimePartBufferClose(msig->part_buffer);
|
|
|
|
|
|
|
|
if (mult->hdrs) /* duplicated from MimeMultipart_close_child, ugh. */
|
|
|
|
{
|
|
|
|
MimeHeaders_free(mult->hdrs);
|
|
|
|
mult->hdrs = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Should be no kids yet. */
|
|
|
|
XP_ASSERT(cont->nchildren == 0);
|
|
|
|
if (cont->nchildren != 0) return -1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartSigned_parse_child_line (MimeObject *obj,
|
|
|
|
char *line, int32 length,
|
|
|
|
XP_Bool first_line_p)
|
|
|
|
{
|
|
|
|
MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
|
|
|
|
MimeContainer *cont = (MimeContainer *) obj;
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
/* Shouldn't have made any sub-parts yet. */
|
|
|
|
XP_ASSERT(cont->nchildren == 0);
|
|
|
|
if (cont->nchildren != 0) return -1;
|
|
|
|
|
|
|
|
switch (sig->state)
|
|
|
|
{
|
|
|
|
case MimeMultipartSignedPreamble:
|
|
|
|
case MimeMultipartSignedBodyFirstHeader:
|
|
|
|
case MimeMultipartSignedBodyHeaders:
|
|
|
|
XP_ASSERT(0); /* How'd we get here? Oh well, fall through. */
|
|
|
|
|
|
|
|
case MimeMultipartSignedBodyFirstLine:
|
|
|
|
XP_ASSERT(first_line_p);
|
|
|
|
if (!sig->part_buffer)
|
|
|
|
{
|
|
|
|
sig->part_buffer = MimePartBufferCreate();
|
|
|
|
if (!sig->part_buffer)
|
|
|
|
return MK_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
/* fall through */
|
|
|
|
|
|
|
|
case MimeMultipartSignedBodyLine:
|
|
|
|
{
|
|
|
|
/* This is the first part; we are buffering it, and will emit it all
|
|
|
|
at the end (so that we know whether the signature matches before
|
|
|
|
showing anything to the user.)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* The newline issues here are tricky, since both the newlines
|
|
|
|
before and after the boundary string are to be considered part
|
|
|
|
of the boundary: this is so that a part can be specified such
|
|
|
|
that it does not end in a trailing newline.
|
|
|
|
|
|
|
|
To implement this, we send a newline *before* each line instead
|
|
|
|
of after, except for the first line, which is not preceeded by a
|
|
|
|
newline.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Remove the trailing newline... */
|
|
|
|
if (length > 0 && line[length-1] == LF) length--;
|
|
|
|
if (length > 0 && line[length-1] == CR) length--;
|
|
|
|
|
|
|
|
XP_ASSERT(sig->part_buffer);
|
|
|
|
XP_ASSERT(first_line_p ==
|
|
|
|
(sig->state == MimeMultipartSignedBodyFirstLine));
|
|
|
|
|
|
|
|
if (!first_line_p)
|
|
|
|
{
|
|
|
|
/* Push out a preceeding newline... */
|
|
|
|
char nl[] = LINEBREAK;
|
|
|
|
status = MimePartBufferWrite (sig->part_buffer, nl, LINEBREAK_LEN);
|
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now push out the line sans trailing newline. */
|
|
|
|
if (length > 0)
|
|
|
|
status = MimePartBufferWrite (sig->part_buffer, line, length);
|
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MimeMultipartSignedSignatureHeaders:
|
|
|
|
XP_ASSERT(0); /* How'd we get here? Oh well, fall through. */
|
|
|
|
|
|
|
|
case MimeMultipartSignedSignatureFirstLine:
|
|
|
|
case MimeMultipartSignedSignatureLine:
|
|
|
|
/* Nothing to do here -- hashing of the signature part is handled up
|
|
|
|
in MimeMultipartSigned_parse_line().
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MimeMultipartSignedEpilogue:
|
|
|
|
/* Too many kids? MimeMultipartSigned_create_child() should have
|
|
|
|
prevented us from getting here. */
|
|
|
|
XP_ASSERT(0);
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: /* bad state */
|
|
|
|
XP_ASSERT(0);
|
|
|
|
return -1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
MimeMultipartSigned_emit_child (MimeObject *obj)
|
|
|
|
{
|
|
|
|
MimeMultipartSigned *sig = (MimeMultipartSigned *) obj;
|
|
|
|
MimeMultipart *mult = (MimeMultipart *) obj;
|
|
|
|
MimeContainer *cont = (MimeContainer *) obj;
|
|
|
|
int status = 0;
|
|
|
|
MimeObject *body;
|
|
|
|
|
1998-07-24 21:05:24 +00:00
|
|
|
XP_ASSERT(sig->bigfun_closure);
|
1998-03-28 02:44:41 +00:00
|
|
|
|
|
|
|
/* Emit some HTML saying whether the signature was cool.
|
|
|
|
But don't emit anything if in FO_QUOTE_MESSAGE mode.
|
|
|
|
*/
|
|
|
|
if (obj->options &&
|
|
|
|
obj->options->headers != MimeHeadersCitation &&
|
|
|
|
obj->options->write_html_p &&
|
|
|
|
obj->options->output_fn &&
|
|
|
|
obj->options->headers != MimeHeadersCitation &&
|
1998-07-24 21:05:24 +00:00
|
|
|
sig->bigfun_closure)
|
1998-03-28 02:44:41 +00:00
|
|
|
{
|
|
|
|
char *html = (((MimeMultipartSignedClass *) obj->class)
|
1998-07-24 21:05:24 +00:00
|
|
|
->bigfun_generate_html (sig->bigfun_closure));
|
1998-03-28 02:44:41 +00:00
|
|
|
if (!html) return -1; /* MK_OUT_OF_MEMORY? */
|
|
|
|
|
|
|
|
status = MimeObject_write(obj, html, XP_STRLEN(html), FALSE);
|
|
|
|
XP_FREE(html);
|
|
|
|
if (status < 0) return status;
|
|
|
|
|
1998-07-24 21:05:24 +00:00
|
|
|
/* Now that we have written out the bigfun stamp, the outermost header
|
1998-03-28 02:44:41 +00:00
|
|
|
block is well and truly closed. If this is in fact the outermost
|
|
|
|
message, then run the post_header_html_fn now.
|
|
|
|
*/
|
|
|
|
if (obj->options &&
|
|
|
|
obj->options->state &&
|
|
|
|
obj->options->generate_post_header_html_fn &&
|
|
|
|
!obj->options->state->post_header_html_run_p)
|
|
|
|
{
|
|
|
|
MimeHeaders *outer_headers;
|
|
|
|
MimeObject *p;
|
|
|
|
for (p = obj; p->parent; p = p->parent)
|
|
|
|
outer_headers = p->headers;
|
|
|
|
XP_ASSERT(obj->options->state->first_data_written_p);
|
|
|
|
html = obj->options->generate_post_header_html_fn(NULL,
|
|
|
|
obj->options->html_closure,
|
|
|
|
outer_headers);
|
|
|
|
obj->options->state->post_header_html_run_p = TRUE;
|
|
|
|
if (html)
|
|
|
|
{
|
|
|
|
status = MimeObject_write(obj, html, XP_STRLEN(html), FALSE);
|
|
|
|
XP_FREE(html);
|
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Oh, this is fairly nasty. We're skipping over our "create child" method
|
|
|
|
and using the one our superclass defines. Perhaps instead we should add
|
|
|
|
a new method on this class, and initialize that method to be the
|
|
|
|
create_child method of the superclass. Whatever.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/* The superclass method expects to find the headers for the part that it's
|
|
|
|
to create in mult->hdrs, so ensure that they're there. */
|
|
|
|
XP_ASSERT(!mult->hdrs);
|
|
|
|
if (mult->hdrs) MimeHeaders_free(mult->hdrs);
|
|
|
|
mult->hdrs = sig->body_hdrs;
|
|
|
|
sig->body_hdrs = 0;
|
|
|
|
|
|
|
|
/* Run the superclass create_child method.
|
|
|
|
*/
|
|
|
|
status = (((MimeMultipartClass *)(&MIME_SUPERCLASS))->create_child(obj));
|
|
|
|
if (status < 0) return status;
|
|
|
|
|
|
|
|
/* Retrieve the child that it created.
|
|
|
|
*/
|
|
|
|
XP_ASSERT(cont->nchildren == 1);
|
|
|
|
if (cont->nchildren != 1) return -1;
|
|
|
|
body = cont->children[0];
|
|
|
|
XP_ASSERT(body);
|
|
|
|
if (!body) return -1;
|
|
|
|
|
|
|
|
#ifdef MIME_DRAFTS
|
|
|
|
if (body->options->decompose_file_p) {
|
|
|
|
body->options->signed_p = TRUE;
|
|
|
|
if (!mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
|
|
|
|
body->options->decompose_file_init_fn)
|
|
|
|
body->options->decompose_file_init_fn ( body->options->stream_closure, body->headers );
|
|
|
|
}
|
|
|
|
#endif /* MIME_DRAFTS */
|
|
|
|
|
|
|
|
/* If there's no part_buffer, this is a zero-length signed message? */
|
|
|
|
if (sig->part_buffer)
|
|
|
|
{
|
|
|
|
#ifdef MIME_DRAFTS
|
|
|
|
if (body->options->decompose_file_p &&
|
|
|
|
!mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
|
|
|
|
body->options->decompose_file_output_fn)
|
|
|
|
status = MimePartBufferRead (sig->part_buffer,
|
|
|
|
/* The (int (*) ...) cast is to turn the
|
|
|
|
`void' argument into `MimeObject'. */
|
|
|
|
((int (*) (char *, int32, void *))
|
|
|
|
body->options->decompose_file_output_fn),
|
|
|
|
body->options->stream_closure);
|
|
|
|
else
|
|
|
|
#endif /* MIME_DRAFTS */
|
|
|
|
|
|
|
|
status = MimePartBufferRead (sig->part_buffer,
|
|
|
|
/* The (int (*) ...) cast is to turn the
|
|
|
|
`void' argument into `MimeObject'. */
|
|
|
|
((int (*) (char *, int32, void *))
|
|
|
|
body->class->parse_buffer),
|
|
|
|
body);
|
|
|
|
if (status < 0) return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
MimeMultipartSigned_cleanup(obj, FALSE);
|
|
|
|
|
|
|
|
/* Done parsing. */
|
|
|
|
status = body->class->parse_eof(body, FALSE);
|
|
|
|
if (status < 0) return status;
|
|
|
|
status = body->class->parse_end(body, FALSE);
|
|
|
|
if (status < 0) return status;
|
|
|
|
|
|
|
|
#ifdef MIME_DRAFTS
|
|
|
|
if (body->options->decompose_file_p &&
|
|
|
|
!mime_typep(body, (MimeObjectClass*)&mimeMultipartClass) &&
|
|
|
|
body->options->decompose_file_close_fn)
|
|
|
|
body->options->decompose_file_close_fn(body->options->stream_closure);
|
|
|
|
#endif /* MIME_DRAFTS */
|
|
|
|
|
|
|
|
/* Put out a separator after every multipart/signed object. */
|
|
|
|
status = MimeObject_write_separator(obj);
|
|
|
|
if (status < 0) return status;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|