From 1c309b14c41ba3305bc72404fa290769251fda4a Mon Sep 17 00:00:00 2001 From: Not Zed Date: Fri, 31 May 2002 01:05:47 +0000 Subject: Added a protocol field for users to use. 2002-05-30 Not Zed * camel-cipher-context.h: Added a protocol field for users to use. * camel-stream-filter.c (do_read): Sigh, the pre-size is READ_PAD not READ_SIZE. Big difference. (READ_PAD): Bumped upto 128 from 64, so we can fit a typical line in full. * providers/imap/camel-imap-folder.c (get_content): Changed to load multipart/signed as an opaque block into the right kind of object. * camel-multipart.h (struct _CamelMultipart): Removed the boundary field. It wans't actually used anywhere. * camel-seekable-substream.c (camel_seekable_substream_new_with_seekable_stream_and_bounds): Shortened this stupidly long name to just :new(), its the only way its ever used. Fixed all callers. * camel-multipart-signed.[ch]: new wrapper for multipart/signed types. We need to treat the entire content as a blob of data for transport purposes. This also cleans up a lot of the sign/verify handling. * camel-mime-part-utils.c (camel_mime_part_construct_content_from_parser): Just call camel_multipart_construct_from_parser for multipart parts, also use a multipart_signed for multipart/signed types. * camel-multipart.c (camel_multipart_construct_from_parser): New virtual function for multiparts to buld themselves. (construct_from_parser): Implement for normal multiparts. (toplevel): Got rid of a warning, it'll never be an abstract class. * camel-pgp-context.c (pgp_hash_to_id): (pgp_id_to_hash): Implement. (camel_pgp_context_init): Init the pgp protocol specifier. * camel-cipher-context.c (camel_cipher_id_to_hash): (camel_cipher_hash_to_id): Util virtual methods to handle the cipher id string. * camel-mime-filter-canon.[ch]: A new filter, end/start of line canonicalisation filter. Can escape "From " and strip trailing whitespace, and canonicalise the end of line to crlf or to lf. 2002-05-29 Not Zed * camel-multipart.h (struct _CamelMultipart): Removed the 'parent', nothing used it, anywhere. Cleaned up some formatting slightly. svn path=/trunk/; revision=17057 --- camel/ChangeLog | 55 +++ camel/Makefile.am | 4 + camel/camel-cipher-context.c | 33 ++ camel/camel-cipher-context.h | 9 + camel/camel-folder-summary.h | 2 +- camel/camel-mime-filter-canon.c | 226 ++++++++++ camel/camel-mime-filter-canon.h | 65 +++ camel/camel-mime-parser.c | 10 +- camel/camel-mime-part-utils.c | 42 +- camel/camel-mime-part.h | 1 + camel/camel-multipart-signed.c | 720 +++++++++++++++++++++++++++++++ camel/camel-multipart-signed.h | 103 +++++ camel/camel-multipart.c | 57 ++- camel/camel-multipart.h | 12 +- camel/camel-pgp-context.c | 55 +++ camel/camel-seekable-substream.c | 5 +- camel/camel-seekable-substream.h | 3 +- camel/camel-smime-context.c | 48 +++ camel/camel-stream-filter.c | 20 +- camel/providers/nntp/camel-nntp-stream.c | 2 +- camel/tests/stream/test3.c | 8 +- 21 files changed, 1422 insertions(+), 58 deletions(-) create mode 100644 camel/camel-mime-filter-canon.c create mode 100644 camel/camel-mime-filter-canon.h create mode 100644 camel/camel-multipart-signed.c create mode 100644 camel/camel-multipart-signed.h diff --git a/camel/ChangeLog b/camel/ChangeLog index 5c024f3900..c25feb4098 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,3 +1,58 @@ +2002-05-30 Not Zed + + * camel-cipher-context.h: Added a protocol field for users to use. + + * camel-stream-filter.c (do_read): Sigh, the pre-size is READ_PAD + not READ_SIZE. Big difference. + (READ_PAD): Bumped upto 128 from 64, so we can fit a typical line + in full. + + * providers/imap/camel-imap-folder.c (get_content): Changed to + load multipart/signed as an opaque block into the right kind of + object. + + * camel-multipart.h (struct _CamelMultipart): Removed the boundary + field. It wans't actually used anywhere. + + * camel-seekable-substream.c + (camel_seekable_substream_new_with_seekable_stream_and_bounds): + Shortened this stupidly long name to just :new(), its the only way + its ever used. Fixed all callers. + + * camel-multipart-signed.[ch]: new wrapper for multipart/signed + types. We need to treat the entire content as a blob of data for + transport purposes. This also cleans up a lot of the sign/verify + handling. + + * camel-mime-part-utils.c + (camel_mime_part_construct_content_from_parser): Just call + camel_multipart_construct_from_parser for multipart parts, also + use a multipart_signed for multipart/signed types. + + * camel-multipart.c (camel_multipart_construct_from_parser): New + virtual function for multiparts to buld themselves. + (construct_from_parser): Implement for normal multiparts. + (toplevel): Got rid of a warning, it'll never be an abstract + class. + + * camel-pgp-context.c (pgp_hash_to_id): + (pgp_id_to_hash): Implement. + (camel_pgp_context_init): Init the pgp protocol specifier. + + * camel-cipher-context.c (camel_cipher_id_to_hash): + (camel_cipher_hash_to_id): Util virtual methods to handle the + cipher id string. + + * camel-mime-filter-canon.[ch]: A new filter, end/start of line + canonicalisation filter. Can escape "From " and strip trailing + whitespace, and canonicalise the end of line to crlf or to lf. + +2002-05-29 Not Zed + + * camel-multipart.h (struct _CamelMultipart): Removed the + 'parent', nothing used it, anywhere. Cleaned up some formatting + slightly. + 2002-05-30 Jeffrey Stedfast * providers/imap/camel-imap-store.c (camel_imap_store_readline): diff --git a/camel/Makefile.am b/camel/Makefile.am index 36be0ea18d..a58f0e35db 100644 --- a/camel/Makefile.am +++ b/camel/Makefile.am @@ -50,6 +50,7 @@ libcamel_la_SOURCES = \ camel-mime-filter-basic.c \ camel-mime-filter-charset.c \ camel-mime-filter-chomp.c \ + camel-mime-filter-canon.c \ camel-mime-filter-crlf.c \ camel-mime-filter-from.c \ camel-mime-filter-html.c \ @@ -65,6 +66,7 @@ libcamel_la_SOURCES = \ camel-mime-utils.c \ camel-movemail.c \ camel-multipart.c \ + camel-multipart-signed.c \ camel-object.c \ camel-operation.c \ camel-partition-table.c \ @@ -146,6 +148,7 @@ libcamelinclude_HEADERS = \ camel-mime-filter-basic.h \ camel-mime-filter-charset.h \ camel-mime-filter-chomp.h \ + camel-mime-filter-canon.h \ camel-mime-filter-crlf.h \ camel-mime-filter-from.h \ camel-mime-filter-html.h \ @@ -161,6 +164,7 @@ libcamelinclude_HEADERS = \ camel-mime-utils.h \ camel-movemail.h \ camel-multipart.h \ + camel-multipart-signed.h \ camel-object.h \ camel-operation.h \ camel-partition-table.h \ diff --git a/camel/camel-cipher-context.c b/camel/camel-cipher-context.c index feefff5565..7771a3a64a 100644 --- a/camel/camel-cipher-context.c +++ b/camel/camel-cipher-context.c @@ -61,6 +61,9 @@ static int cipher_encrypt (CamelCipherContext *context, gboolea static int cipher_decrypt (CamelCipherContext *context, CamelStream *istream, CamelStream *ostream, CamelException *ex); +static const char *cipher_hash_to_id(CamelCipherContext *context, CamelCipherHash hash); +static CamelCipherHash cipher_id_to_hash(CamelCipherContext *context, const char *id); + static CamelObjectClass *parent_class; static void @@ -96,6 +99,8 @@ camel_cipher_context_class_init (CamelCipherContextClass *camel_cipher_context_c camel_cipher_context_class->verify = cipher_verify; camel_cipher_context_class->encrypt = cipher_encrypt; camel_cipher_context_class->decrypt = cipher_decrypt; + camel_cipher_context_class->hash_to_id = cipher_hash_to_id; + camel_cipher_context_class->id_to_hash = cipher_id_to_hash; } CamelType @@ -364,6 +369,34 @@ camel_cipher_decrypt (CamelCipherContext *context, CamelStream *istream, return retval; } +static CamelCipherHash +cipher_id_to_hash(CamelCipherContext *context, const char *id) +{ + return CAMEL_CIPHER_HASH_DEFAULT; +} + +/* a couple of util functions */ +CamelCipherHash +camel_cipher_id_to_hash(CamelCipherContext *context, const char *id) +{ + g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), CAMEL_CIPHER_HASH_DEFAULT); + + return ((CamelCipherContextClass *)((CamelObject *)context)->klass)->id_to_hash(context, id); +} + +static const char * +cipher_hash_to_id(CamelCipherContext *context, CamelCipherHash hash) +{ + return NULL; +} + +const char * +camel_cipher_hash_to_id(CamelCipherContext *context, CamelCipherHash hash) +{ + g_return_val_if_fail (CAMEL_IS_CIPHER_CONTEXT (context), NULL); + + return ((CamelCipherContextClass *)((CamelObject *)context)->klass)->hash_to_id(context, hash); +} /* Cipher Validity stuff */ struct _CamelCipherValidity { diff --git a/camel/camel-cipher-context.h b/camel/camel-cipher-context.h index 5147cc5ebc..c3a6641967 100644 --- a/camel/camel-cipher-context.h +++ b/camel/camel-cipher-context.h @@ -51,6 +51,8 @@ typedef struct _CamelCipherContext { CamelObject parent_object; struct _CamelCipherContextPrivate *priv; + + const char *protocol; /* this MUST be set by implementors */ CamelSession *session; @@ -76,6 +78,9 @@ typedef struct _CamelCipherContextClass { int (*decrypt) (CamelCipherContext *context, CamelStream *istream, CamelStream *ostream, CamelException *ex); + CamelCipherHash (*id_to_hash)(CamelCipherContext *context, const char *id); + const char * (*hash_to_id)(CamelCipherContext *context, CamelCipherHash hash); + } CamelCipherContextClass; CamelType camel_cipher_context_get_type (void); @@ -102,6 +107,10 @@ int camel_cipher_encrypt (CamelCipherContext *context, gboolean int camel_cipher_decrypt (CamelCipherContext *context, CamelStream *istream, CamelStream *ostream, CamelException *ex); +/* cipher context util routines */ +CamelCipherHash camel_cipher_id_to_hash(CamelCipherContext *context, const char *id); +const char * camel_cipher_hash_to_id(CamelCipherContext *context, CamelCipherHash hash); + /* CamelCipherValidity utility functions */ CamelCipherValidity *camel_cipher_validity_new (void); diff --git a/camel/camel-folder-summary.h b/camel/camel-folder-summary.h index 7545f2af22..62ea39a1df 100644 --- a/camel/camel-folder-summary.h +++ b/camel/camel-folder-summary.h @@ -51,7 +51,7 @@ struct _CamelMessageContentInfo { struct _header_content_type *type; char *id; char *description; - char *encoding; + char *encoding; /* this should be an enum?? */ guint32 size; }; diff --git a/camel/camel-mime-filter-canon.c b/camel/camel-mime-filter-canon.c new file mode 100644 index 0000000000..0fd48fb7b3 --- /dev/null +++ b/camel/camel-mime-filter-canon.c @@ -0,0 +1,226 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002 Ximian, Inc. + * + * Authors: Jeffrey Stedfast + * Michael Zucchi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* canonicalisation filter, used for secure mime incoming and outgoing */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include "camel-mime-filter-canon.h" + +static void filter (CamelMimeFilter *f, char *in, size_t len, size_t prespace, + char **out, size_t *outlen, size_t *outprespace); +static void complete (CamelMimeFilter *f, char *in, size_t len, + size_t prespace, char **out, size_t *outlen, + size_t *outprespace); +static void reset (CamelMimeFilter *f); + + +static void +camel_mime_filter_canon_class_init (CamelMimeFilterCanonClass *klass) +{ + CamelMimeFilterClass *mime_filter_class = (CamelMimeFilterClass *) klass; + + mime_filter_class->filter = filter; + mime_filter_class->complete = complete; + mime_filter_class->reset = reset; +} + +CamelType +camel_mime_filter_canon_get_type (void) +{ + static CamelType type = CAMEL_INVALID_TYPE; + + if (type == CAMEL_INVALID_TYPE) { + type = camel_type_register (camel_mime_filter_get_type(), "CamelMimeFilterCanon", + sizeof (CamelMimeFilterCanon), + sizeof (CamelMimeFilterCanonClass), + (CamelObjectClassInitFunc) camel_mime_filter_canon_class_init, + NULL, + NULL, + NULL); + } + + return type; +} + +static void +filter(CamelMimeFilter *f, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace) +{ + register unsigned char *inptr, c; + const unsigned char *inend, *start; + char *starto; + register char *o; + int lf = 0; + guint32 flags; + + flags = ((CamelMimeFilterCanon *)f)->flags; + + /* first, work out how much space we need */ + inptr = in; + inend = in+len; + while (inptr < inend) + if (*inptr++ == '\n') + lf++; + + /* worst case, extra 3 chars per line + "From \n" -> "=46rom \r\n" + We add 1 extra incase we're called from complete, when we didn't end in \n */ + + camel_mime_filter_set_size(f, len+lf*3+4, FALSE); + + o = f->outbuf; + inptr = in; + start = inptr; + starto = o; + while (inptr < inend) { + /* first, check start of line, we always start at the start of the line */ + c = *inptr; + if (flags & CAMEL_MIME_FILTER_CANON_FROM && c == 'F') { + inptr++; + if (inptr < inend-4) { + if (strncmp(inptr, "rom ", 4) == 0) { + *o++ = '='; + *o++ = '4'; + *o++ = '6'; + } else + *o++ = 'F'; + *o++ = *inptr++; + *o++ = *inptr++; + *o++ = *inptr++; + *o++ = *inptr++; + } else { + break; + } + } + + /* now scan for end of line */ + while (inptr < inend) { + c = *inptr++; + if (c == '\n') { + /* check to strip trailing space */ + if (flags & CAMEL_MIME_FILTER_CANON_STRIP) { + while (o>starto && (o[-1] == ' ' || o[-1] == '\t' || o[-1]=='\r')) + o--; + } + /* check end of line canonicalisation */ + if (o>starto) { + if (flags & CAMEL_MIME_FILTER_CANON_CRLF) { + if (o[-1] != '\r') + *o++ = '\r'; + } else { + if (o[-1] == '\r') + o--; + } + } + *o++ = c; + start = inptr; + starto = o; + break; + } else + *o++ = c; + } + } + + /* TODO: We should probably track if we end somewhere in the middle of a line, + otherwise we potentially backup a full line, which could be large */ + + /* we got to the end of the data without finding anything, backup to start and re-process next time around */ + camel_mime_filter_backup(f, start, inend - start); + + *out = f->outbuf; + *outlen = starto - f->outbuf; + *outprespace = f->outpre; +} + +static void +complete(CamelMimeFilter *f, char *in, size_t len, size_t prespace, char **out, size_t *outlen, size_t *outprespace) +{ + unsigned char *inptr, *inend; + char *o, *starto; + guint32 flags; + + if (len) + filter(f, in, len, prespace, out, outlen, outprespace); + + /* the data didn't contain an eol or was too short for "From ", we only need to check for "From" and add an eol */ + if (f->backlen) { + inptr = (unsigned char *)f->backbuf; + inend = (unsigned char *)f->backbuf + f->backlen; + starto = o = *out + *outlen; + flags = ((CamelMimeFilterCanon *)f)->flags; + + /* Check any embedded "From " */ + if (f->backlen >= 5 + && (flags & CAMEL_MIME_FILTER_CANON_FROM) + && strcmp(inptr, "From ") == 0) { + strcpy(o, "=46rom "); + o += 7; + inptr += 5; + } + + /* copy the rest of it */ + while (inptr < inend) + *o++ = *inptr++; + + /* check to strip trailing space */ + if (flags & CAMEL_MIME_FILTER_CANON_STRIP) { + while (o>starto && (o[-1] == ' ' || o[-1] == '\t' || o[-1]=='\r')) + o--; + } + /* check end of line canonicalisation */ + if (o>starto) { + if (flags & CAMEL_MIME_FILTER_CANON_CRLF) { + if (o[-1] != '\r') + *o++ = '\r'; + } else { + if (o[-1] == '\r') + o--; + } + } + + /* and always finish with an eol */ + *o++ = '\n'; + + *outlen = o - *out; + } +} + +static void +reset (CamelMimeFilter *f) +{ + /* no-op */ +} + +CamelMimeFilter * +camel_mime_filter_canon_new(guint32 flags) +{ + CamelMimeFilterCanon *chomp = CAMEL_MIME_FILTER_CANON (camel_object_new (CAMEL_MIME_FILTER_CANON_TYPE)); + + chomp->flags = flags; + + return (CamelMimeFilter *) chomp; +} diff --git a/camel/camel-mime-filter-canon.h b/camel/camel-mime-filter-canon.h new file mode 100644 index 0000000000..d3cffb240e --- /dev/null +++ b/camel/camel-mime-filter-canon.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Copyright (C) 2002 Ximian Inc. + * + * Authors: Jeffrey Stedfast + * Michael Zucchi + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _CAMEL_MIME_FILTER_CANON_H +#define _CAMEL_MIME_FILTER_CANON_H + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +#include + +#define CAMEL_MIME_FILTER_CANON_TYPE (camel_mime_filter_canon_get_type ()) +#define CAMEL_MIME_FILTER_CANON(obj) CAMEL_CHECK_CAST (obj, CAMEL_MIME_FILTER_CANON_TYPE, CamelMimeFilterCanon) +#define CAMEL_MIME_FILTER_CANON_CLASS(klass) CAMEL_CHECK_CLASS_CAST (klass, CAMEL_MIME_FILTER_CANON_TYPE, CamelMimeFilterCanonClass) +#define CAMEL_IS_MIME_FILTER_CANON(obj) CAMEL_CHECK_TYPE (obj, CAMEL_MIME_FILTER_CANON_TYPE) + +typedef struct _CamelMimeFilterCanon CamelMimeFilterCanon; +typedef struct _CamelMimeFilterCanonClass CamelMimeFilterCanonClass; + +enum { + CAMEL_MIME_FILTER_CANON_CRLF = (1<<0), /* canoncialise end of line to crlf, otherwise canonicalise to lf only */ + CAMEL_MIME_FILTER_CANON_FROM = (1<<1), /* escape "^From " using quoted-printable semantics into "=46rom " */ + CAMEL_MIME_FILTER_CANON_STRIP = (1<<2), /* strip trailing space */ +}; + +struct _CamelMimeFilterCanon { + CamelMimeFilter parent; + + guint32 flags; +}; + +struct _CamelMimeFilterCanonClass { + CamelMimeFilterClass parent_class; +}; + +CamelType camel_mime_filter_canon_get_type (void); + +CamelMimeFilter *camel_mime_filter_canon_new(guint32 flags); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* ! _CAMEL_MIME_FILTER_CANON_H */ diff --git a/camel/camel-mime-parser.c b/camel/camel-mime-parser.c index f57583135d..2bfe4e5322 100644 --- a/camel/camel-mime-parser.c +++ b/camel/camel-mime-parser.c @@ -1681,8 +1681,8 @@ tail_recurse: if ( (content = header_raw_find(&h->headers, "Content-Type", NULL)) && (ct = header_content_type_decode(content))) { if (!strcasecmp(ct->type, "multipart")) { - bound = header_content_type_param(ct, "boundary"); - if (bound) { + if (!header_content_type_is(ct, "multipart", "signed") + && (bound = header_content_type_param(ct, "boundary"))) { d(printf("multipart, boundary = %s\n", bound)); h->boundarylen = strlen(bound)+2; h->boundarylenfinal = h->boundarylen+2; @@ -1690,11 +1690,11 @@ tail_recurse: sprintf(h->boundary, "--%s--", bound); type = HSCAN_MULTIPART; } else { - header_content_type_unref(ct); - ct = header_content_type_decode("text/plain"); + /*header_content_type_unref(ct); + ct = header_content_type_decode("text/plain");*/ /* We can't quite do this, as it will mess up all the offsets ... */ /* header_raw_replace(&h->headers, "Content-Type", "text/plain", offset);*/ - g_warning("Multipart with no boundary, treating as text/plain"); + /*g_warning("Multipart with no boundary, treating as text/plain");*/ } } else if (!strcasecmp(ct->type, "message")) { if (!strcasecmp(ct->subtype, "rfc822") diff --git a/camel/camel-mime-part-utils.c b/camel/camel-mime-part-utils.c index 987e5e77d7..037493b501 100644 --- a/camel/camel-mime-part-utils.c +++ b/camel/camel-mime-part-utils.c @@ -38,6 +38,7 @@ #include "camel-mime-part-utils.h" #include "camel-mime-message.h" #include "camel-multipart.h" +#include "camel-multipart-signed.h" #include "camel-seekable-substream.h" #include "camel-stream-fs.h" #include "camel-stream-filter.h" @@ -336,47 +337,30 @@ void camel_mime_part_construct_content_from_parser (CamelMimePart *dw, CamelMimeParser *mp) { CamelDataWrapper *content = NULL; - char *buf; - int len; switch (camel_mime_parser_state (mp)) { case HSCAN_HEADER: d(printf("Creating body part\n")); - content = camel_data_wrapper_new (); - simple_data_wrapper_construct_from_parser (content, mp); + /* multipart/signed is some fucked up type that we must treat as binary data, fun huh, idiots. */ + if (header_content_type_is(camel_mime_parser_content_type(mp), "multipart", "signed")) { + content = (CamelDataWrapper *)camel_multipart_signed_new(); + camel_multipart_construct_from_parser((CamelMultipart *)content, mp); + } else { + content = camel_data_wrapper_new (); + simple_data_wrapper_construct_from_parser (content, mp); + } break; case HSCAN_MESSAGE: d(printf("Creating message part\n")); content = (CamelDataWrapper *) camel_mime_message_new (); camel_mime_part_construct_from_parser ((CamelMimePart *)content, mp); break; - case HSCAN_MULTIPART: { - struct _header_content_type *content_type; - CamelDataWrapper *bodypart; - - /* FIXME: we should use a came-mime-mutlipart, not jsut a camel-multipart, but who cares */ + case HSCAN_MULTIPART: d(printf("Creating multi-part\n")); - - content = (CamelDataWrapper *)camel_multipart_new (); - - content_type = camel_mime_parser_content_type (mp); - camel_multipart_set_boundary ((CamelMultipart *)content, - header_content_type_param (content_type, "boundary")); - - while (camel_mime_parser_step (mp, &buf, &len) != HSCAN_MULTIPART_END) { - camel_mime_parser_unstep (mp); - bodypart = (CamelDataWrapper *)camel_mime_part_new (); - camel_mime_part_construct_from_parser ((CamelMimePart *)bodypart, mp); - camel_multipart_add_part ((CamelMultipart *)content, (CamelMimePart *)bodypart); - camel_object_unref ((CamelObject *)bodypart); - } - - /* these are only return valid data in the MULTIPART_END state */ - camel_multipart_set_preface ((CamelMultipart *)content, camel_mime_parser_preface (mp)); - camel_multipart_set_postface ((CamelMultipart *)content, camel_mime_parser_postface (mp)); - + content = (CamelDataWrapper *)camel_multipart_new(); + camel_multipart_construct_from_parser((CamelMultipart *)content, mp); d(printf("Created multi-part\n")); - break; } + break; default: g_warning("Invalid state encountered???: %d", camel_mime_parser_state (mp)); } diff --git a/camel/camel-mime-part.h b/camel/camel-mime-part.h index 4f05ecb71b..e745f57a6a 100644 --- a/camel/camel-mime-part.h +++ b/camel/camel-mime-part.h @@ -65,6 +65,7 @@ struct _CamelMimePart struct _header_raw *headers; /* mime headers */ /* All fields here are -** PRIVATE **- */ + /* TODO: these should be in a camelcontentinfo */ char *description; CamelMimeDisposition *disposition; char *content_id; diff --git a/camel/camel-multipart-signed.c b/camel/camel-multipart-signed.c new file mode 100644 index 0000000000..95e0e848da --- /dev/null +++ b/camel/camel-multipart-signed.c @@ -0,0 +1,720 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * camel-multipart.c : Abstract class for a multipart + * + * Authors: Michael Zucchi + * + * Copyright 2002 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#include + +#include + +#include "camel-mime-part.h" +#include "camel-mime-message.h" +#include "camel-mime-parser.h" +#include "camel-stream-mem.h" +#include "camel-multipart-signed.h" +#include "camel-mime-part.h" +#include "camel-exception.h" +#include "md5-utils.h" + +#include "camel-stream-filter.h" +#include "camel-seekable-substream.h" +#include "camel-mime-filter-chomp.h" +#include "camel-mime-filter-crlf.h" +#include "camel-mime-filter-canon.h" + +#define d(x) + +static void signed_add_part(CamelMultipart *multipart, CamelMimePart *part); +static void signed_add_part_at(CamelMultipart *multipart, CamelMimePart *part, guint index); +static void signed_remove_part(CamelMultipart *multipart, CamelMimePart *part); +static CamelMimePart *signed_remove_part_at (CamelMultipart *multipart, guint index); +static CamelMimePart *signed_get_part(CamelMultipart *multipart, guint index); +static guint signed_get_number(CamelMultipart *multipart); + +static int write_to_stream(CamelDataWrapper *data_wrapper, CamelStream *stream); +static void set_mime_type_field(CamelDataWrapper *data_wrapper, CamelContentType *mime_type); +static int construct_from_stream(CamelDataWrapper *data_wrapper, CamelStream *stream); +static int signed_construct_from_parser(CamelMultipart *multipart, struct _CamelMimeParser *mp); + +static CamelMultipartClass *parent_class = NULL; + +/* Returns the class for a CamelMultipartSigned */ +#define CMP_CLASS(so) CAMEL_MULTIPART_SIGNED_CLASS (CAMEL_OBJECT_GET_CLASS(so)) + +/* Returns the class for a CamelDataWrapper */ +#define CDW_CLASS(so) CAMEL_DATA_WRAPPER_CLASS (CAMEL_OBJECT_GET_CLASS(so)) + +static void +camel_multipart_signed_class_init (CamelMultipartSignedClass *camel_multipart_signed_class) +{ + CamelDataWrapperClass *camel_data_wrapper_class = CAMEL_DATA_WRAPPER_CLASS(camel_multipart_signed_class); + CamelMultipartClass *mpclass = (CamelMultipartClass *)camel_multipart_signed_class; + + parent_class = (CamelMultipartClass *)camel_multipart_get_type(); + + /* virtual method overload */ + camel_data_wrapper_class->construct_from_stream = construct_from_stream; + camel_data_wrapper_class->write_to_stream = write_to_stream; + camel_data_wrapper_class->set_mime_type_field = set_mime_type_field; + + mpclass->add_part = signed_add_part; + mpclass->add_part_at = signed_add_part_at; + mpclass->remove_part = signed_remove_part; + mpclass->remove_part_at = signed_remove_part_at; + mpclass->get_part = signed_get_part; + mpclass->get_number = signed_get_number; + mpclass->construct_from_parser = signed_construct_from_parser; + +/* + mpclass->get_boundary = signed_get_boundary; + mpclass->set_boundary = signed_set_boundary; +*/ +} + +static void +camel_multipart_signed_init (gpointer object, gpointer klass) +{ + CamelMultipartSigned *multipart = (CamelMultipartSigned *)object; + + camel_data_wrapper_set_mime_type(CAMEL_DATA_WRAPPER(multipart), "multipart/signed"); + multipart->start1 = -1; +} + +static void +camel_multipart_signed_finalize (CamelObject *object) +{ + CamelMultipartSigned *mps = (CamelMultipartSigned *)object; + + g_free(mps->protocol); + g_free(mps->micalg); + if (mps->signature) + camel_object_unref((CamelObject *)mps->signature); + if (mps->content) + camel_object_unref((CamelObject *)mps->content); + if (mps->contentraw) + camel_object_unref((CamelObject *)mps->contentraw); +} + +CamelType +camel_multipart_signed_get_type (void) +{ + static CamelType camel_multipart_signed_type = CAMEL_INVALID_TYPE; + + if (camel_multipart_signed_type == CAMEL_INVALID_TYPE) { + camel_multipart_signed_type = camel_type_register (camel_multipart_get_type (), "CamelMultipartSigned", + sizeof (CamelMultipartSigned), + sizeof (CamelMultipartSignedClass), + (CamelObjectClassInitFunc) camel_multipart_signed_class_init, + NULL, + (CamelObjectInitFunc) camel_multipart_signed_init, + (CamelObjectFinalizeFunc) camel_multipart_signed_finalize); + } + + return camel_multipart_signed_type; +} + +/** + * camel_multipart_signed_new: + * + * Create a new CamelMultipartSigned object. + * + * A MultipartSigned should be used to store and create parts of + * type "multipart/signed". This is because multipart/signed is + * entirely broken-by-design (tm) and uses completely + * different semantics to other mutlipart types. It must be treated + * as opaque data by any transport. See rfc 3156 for details. + * + * There are 3 ways to create the part: + * Use construct_from_stream. If this is used, then you must + * set the mime_type appropriately to match the data uses, so + * that the multiple parts my be extracted. + * + * Use construct_from_parser. The parser MUST be in the HSCAN_HEADER + * state, and the current content_type MUST be "multipart/signed" with + * the appropriate boundary and it SHOULD include the appropriate protocol + * and hash specifiers. + * + * Use sign_part. A signature part will automatically be created + * and the whole part may be written using write_to_stream to + * create a 'transport-safe' version (as safe as can be expected with + * such a broken specification). + * + * Return value: a new CamelMultipartSigned + **/ +CamelMultipartSigned * +camel_multipart_signed_new (void) +{ + CamelMultipartSigned *multipart; + + multipart = (CamelMultipartSigned *)camel_object_new(CAMEL_MULTIPART_SIGNED_TYPE); + + return multipart; +} + +/* yeah yuck. + Well, we could probably use the normal mime parser, but then it would change our + headers. + This is good enough ... till its not! */ +static int +parse_content(CamelMultipartSigned *mps) +{ + CamelMultipart *mp = (CamelMultipart *)mps; + char *start, *end, *start2, *end2, *last; + CamelStreamMem *mem; + char *bound; + const char *boundary; + + boundary = camel_multipart_get_boundary(mp); + if (boundary == NULL) { + g_warning("Trying to get multipart/signed content without setting boundary first"); + return -1; + } + + /* turn it into a string, and 'fix' it up */ + /* this is extremely dodgey but should work! */ + mem = (CamelStreamMem *)((CamelDataWrapper *)mps)->stream; + if (mem == NULL) { + g_warning("Trying to parse multipart/signed without constructing first"); + return -1; + } + + camel_stream_write((CamelStream *)mem, "", 1); + g_byte_array_set_size(mem->buffer, mem->buffer->len-1); + last = mem->buffer->data + mem->buffer->len; + + bound = alloca(strlen(boundary)+5); + sprintf(bound, "--%s", boundary); + + start = strstr(mem->buffer->data, bound); + if (start == NULL) { + printf("construct from stream, cannot find first boundary\n"); + return -1; + } + + if (start > (char *)mem->buffer->data) { + char *tmp = g_strndup(mem->buffer->data, start-(char *)mem->buffer->data-1); + camel_multipart_set_preface(mp, tmp); + g_free(tmp); + } + + start += strlen(bound)+1; + if (start >= last) + return -1; + end = strstr(start, bound); + if (end == NULL) { + printf("construct from stream, cannot find second boundary\n"); + return -1; + } + + start2 = end + strlen(bound)+1; + if (start2 >= last) + return -1; + sprintf(bound, "--%s--", boundary); + end2 = strstr(start2, bound); + if (end2 == NULL) { + printf("construct from stream, cannot find last boundary\n"); + return -1; + } + + if (end2+strlen(bound)+1 < last) + camel_multipart_set_postface(mp, end2+strlen(bound)+1); + + mps->start1 = start-(char *)mem->buffer->data; + mps->end1 = end-(char *)mem->buffer->data-1; + mps->start2 = start2-(char *)mem->buffer->data; + mps->end2 = end2-(char *)mem->buffer->data-1; + + return 0; +} + +/* we snoop the mime type to get boundary and hash info */ +static void +set_mime_type_field(CamelDataWrapper *data_wrapper, CamelContentType *mime_type) +{ + CamelMultipartSigned *mps = (CamelMultipartSigned *)data_wrapper; + + ((CamelDataWrapperClass *)parent_class)->set_mime_type_field(data_wrapper, mime_type); + if (mime_type) { + const char *micalg, *protocol; + + protocol = header_content_type_param(mime_type, "protocol"); + g_free(mps->protocol); + mps->protocol = g_strdup(protocol); + + micalg = header_content_type_param(mime_type, "micalg"); + g_free(mps->micalg); + mps->micalg = g_strdup(micalg); + } +} + +static void +signed_add_part(CamelMultipart *multipart, CamelMimePart *part) +{ + g_warning("Cannot add parts to a signed part using add_part"); +} + +static void +signed_add_part_at(CamelMultipart *multipart, CamelMimePart *part, guint index) +{ + g_warning("Cannot add parts to a signed part using add_part_at"); +} + +static void +signed_remove_part(CamelMultipart *multipart, CamelMimePart *part) +{ + g_warning("Cannot remove parts from a signed part using remove_part"); +} + +static CamelMimePart * +signed_remove_part_at (CamelMultipart *multipart, guint index) +{ + g_warning("Cannot remove parts from a signed part using remove_part"); + return NULL; +} + +static CamelMimePart * +signed_get_part(CamelMultipart *multipart, guint index) +{ + CamelMultipartSigned *mps = (CamelMultipartSigned *)multipart; + CamelDataWrapper *dw = (CamelDataWrapper *)multipart; + CamelStream *stream; + + switch (index) { + case CAMEL_MULTIPART_SIGNED_CONTENT: + if (mps->content) + return mps->content; + if (mps->contentraw) { + stream = mps->contentraw; + camel_object_ref((CamelObject *)stream); + } else if (mps->start1 == -1 + && parse_content(mps) == -1 + && (stream = ((CamelDataWrapper *)mps)->stream) == NULL) { + g_warning("Trying to get content on an invalid multipart/signed"); + return NULL; + } else if (dw->stream == NULL) { + return NULL; + } else { + stream = camel_seekable_substream_new((CamelSeekableStream *)dw->stream, mps->start1, mps->end1); + } + camel_stream_reset(stream); + mps->content = camel_mime_part_new(); + camel_data_wrapper_construct_from_stream((CamelDataWrapper *)mps->content, stream); + camel_object_unref((CamelObject *)stream); + return mps->content; + case CAMEL_MULTIPART_SIGNED_SIGNATURE: + if (mps->signature) + return mps->signature; + if (mps->start1 == -1 + && parse_content(mps) == -1) { + g_warning("Trying to get signature on invalid multipart/signed"); + return NULL; + } else if (dw->stream == NULL) { + return NULL; + } + stream = camel_seekable_substream_new((CamelSeekableStream *)dw->stream, mps->start2, mps->end2); + camel_stream_reset(stream); + mps->signature = camel_mime_part_new(); + camel_data_wrapper_construct_from_stream((CamelDataWrapper *)mps->signature, stream); + camel_object_unref((CamelObject *)stream); + return mps->signature; + default: + g_warning("trying to get object out of bounds for multipart"); + } + + return NULL; +} + +static guint +signed_get_number(CamelMultipart *multipart) +{ + CamelDataWrapper *dw = (CamelDataWrapper *)multipart; + CamelMultipartSigned *mps = (CamelMultipartSigned *)multipart; + + /* check what we have, so we return something reasonable */ + + if ((mps->content || mps->contentraw) && mps->signature) + return 2; + + if (mps->start1 == -1 && parse_content(mps) == -1) { + if (dw->stream == NULL) + return 0; + else + return 1; + } else { + return 2; + } +} + +static void +set_stream(CamelMultipartSigned *mps, CamelStream *mem) +{ + CamelDataWrapper *dw = (CamelDataWrapper *)mps; + + if (dw->stream) + camel_object_unref((CamelObject *)dw->stream); + dw->stream = (CamelStream *)mem; + + mps->start1 = -1; + if (mps->content) { + camel_object_unref((CamelObject *)mps->content); + mps->content = NULL; + } + if (mps->contentraw) { + camel_object_unref((CamelObject *)mps->contentraw); + mps->contentraw = NULL; + } + if (mps->signature) { + camel_object_unref((CamelObject *)mps->signature); + mps->signature = NULL; + } +} + +static int +construct_from_stream(CamelDataWrapper *data_wrapper, CamelStream *stream) +{ + CamelMultipartSigned *mps = (CamelMultipartSigned *)data_wrapper; + CamelStream *mem = camel_stream_mem_new(); + + if (camel_stream_write_to_stream(stream, mem) == -1) + return -1; + + set_stream(mps, mem); + + return 0; +} + +static int +signed_construct_from_parser(CamelMultipart *multipart, struct _CamelMimeParser *mp) +{ + int err; + struct _header_content_type *content_type; + CamelMultipartSigned *mps = (CamelMultipartSigned *)multipart; + char *buf; + unsigned int len; + CamelStream *mem; + + /* we *must not* be in multipart state, otherwise the mime parser will + parse the headers which is a no no @#$@# stupid multipart/signed spec */ + g_assert(camel_mime_parser_state(mp) == HSCAN_HEADER); + + /* All we do is copy it to a memstream */ + content_type = camel_mime_parser_content_type(mp); + camel_multipart_set_boundary(multipart, header_content_type_param(content_type, "boundary")); + + mem = camel_stream_mem_new(); + while (camel_mime_parser_step(mp, &buf, &len) != HSCAN_BODY_END) + camel_stream_write(mem, buf, len); + + set_stream(mps, mem); + + err = camel_mime_parser_errno(mp); + if (err != 0) { + errno = err; + return -1; + } else + return 0; +} + +static int +write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream) +{ + CamelMultipartSigned *mps = (CamelMultipartSigned *)data_wrapper; + CamelMultipart *mp = (CamelMultipart *)mps; + const char *boundary; + int count, total=0; + + /* we have 3 basic cases: + 1. constructed, we write out the data wrapper stream we got + 2. signed content, we create and write out a new stream + 3. invalid + */ + + /* 1 */ + /* FIXME: locking? */ + if (data_wrapper->stream) { + camel_stream_reset(data_wrapper->stream); + return camel_stream_write_to_stream(data_wrapper->stream, stream); + } + + /* 3 */ + if (mps->signature == NULL || mps->contentraw == NULL) + return -1; + + /* 2 */ + boundary = camel_multipart_get_boundary(mp); + if (mp->preface) { + count = camel_stream_write_string(stream, mp->preface); + if (count == -1) + return -1; + total += count; + } + + /* first boundary */ + count = camel_stream_printf(stream, "\n--%s\n", boundary); + if (count == -1) + return -1; + total += count; + + /* output content part */ + camel_stream_reset(mps->contentraw); + count = camel_stream_write_to_stream(mps->contentraw, stream); + if (count == -1) + return -1; + total += count; + + /* boundary */ + count = camel_stream_printf(stream, "\n--%s\n", boundary); + if (count == -1) + return -1; + total += count; + + /* signature */ + count = camel_data_wrapper_write_to_stream((CamelDataWrapper *)mps->signature, stream); + if (count == -1) + return -1; + total += count; + + /* write the terminating boudary delimiter */ + count = camel_stream_printf(stream, "\n--%s--\n", boundary); + if (count == -1) + return -1; + total += count; + + /* and finally the postface */ + if (mp->postface) { + count = camel_stream_write_string(stream, mp->postface); + if (count == -1) + return -1; + total += count; + } + + return total; +} + +/* See rfc3156, section 2 and others */ +/* We do this simply: Anything not base64 must be qp + This is so that we can safely translate any occurance of "From " + into the quoted-printable escaped version safely. */ +static void +prepare_sign(CamelMimePart *mime_part) +{ + CamelDataWrapper *wrapper; + CamelMimePartEncodingType encoding; + int parts, i; + + wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (mime_part)); + if (!wrapper) + return; + + if (CAMEL_IS_MULTIPART (wrapper)) { + parts = camel_multipart_get_number((CamelMultipart *)wrapper); + for (i = 0; i < parts; i++) + prepare_sign(camel_multipart_get_part((CamelMultipart *)wrapper, i)); + } else if (CAMEL_IS_MIME_MESSAGE (wrapper)) { + prepare_sign((CamelMimePart *)wrapper); + } else { + encoding = camel_mime_part_get_encoding(mime_part); + + if (encoding != CAMEL_MIME_PART_ENCODING_BASE64 + && encoding != CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE) { + camel_mime_part_set_encoding(mime_part, CAMEL_MIME_PART_ENCODING_QUOTEDPRINTABLE); + } + } +} + +/** + * camel_multipart_signed_sign: + * @mps: + * @context: The CipherContext to use for signing. + * @content: CamelMimePart content you wish to sign/transport. + * @userid: The id of the signing key to use. + * @hash: The algorithm to use. + * @ex: + * + * Sign the part @content, and attach it as the first part + * (CAMEL_MULTIPART_SIGNED_CONTENT) of the multipart @mps. A + * signature object will be created and setup as the second part + * (CAMEL_MULTIPART_SIGNED_SIGNATURE) of the object. Once a part has + * been successfully signed the mutlipart is ready for transmission. + * + * This method should be used to create multipart/signed objects + * which are properly canoncalised before signing, etc. + * + * Return value: -1 on error, setting @ex appropriately. On error + * neither the content or signature parts will be setup. + **/ +int +camel_multipart_signed_sign(CamelMultipartSigned *mps, CamelCipherContext *context, CamelMimePart *content, const char *userid, CamelCipherHash hash, CamelException *ex) +{ + CamelMimeFilter *canon_filter; + CamelStream *sigstream, *mem; + CamelStreamFilter *filter; + CamelContentType *mime_type; + CamelMimePart *signature; + CamelDataWrapper *dw; + char *type; + + /* this needs to be set */ + g_return_val_if_fail(context->protocol != NULL, -1); + + prepare_sign(content); + + mem = camel_stream_mem_new(); + filter = camel_stream_filter_new_with_stream(mem); + + canon_filter = camel_mime_filter_canon_new(CAMEL_MIME_FILTER_CANON_STRIP|CAMEL_MIME_FILTER_CANON_CRLF|CAMEL_MIME_FILTER_CANON_FROM); + camel_stream_filter_add(filter, (CamelMimeFilter *)canon_filter); + camel_object_unref((CamelObject *)canon_filter); + + camel_data_wrapper_write_to_stream((CamelDataWrapper *)content, (CamelStream *)filter); + camel_stream_flush((CamelStream *)filter); + camel_object_unref((CamelObject *)filter); + camel_stream_reset(mem); + +#if 0 + printf("-- Signing:\n"); + fwrite(((CamelStreamMem *)mem)->buffer->data, ((CamelStreamMem *)mem)->buffer->len, 1, stdout); + printf("-- end\n"); +#endif + + sigstream = camel_stream_mem_new(); + + if (camel_cipher_sign(context, userid, hash, mem, sigstream, ex) == -1) { + camel_object_unref((CamelObject *)mem); + camel_object_unref((CamelObject *)sigstream); + return -1; + } + + /* create the signature wrapper object */ + signature = camel_mime_part_new(); + dw = camel_data_wrapper_new(); + type = alloca(strlen(context->protocol) + 32); + sprintf(type, "%s; name=signature.asc", context->protocol); + camel_data_wrapper_set_mime_type(dw, type); + camel_stream_reset(sigstream); + camel_data_wrapper_construct_from_stream(dw, sigstream); + camel_object_unref((CamelObject *)sigstream); + camel_medium_set_content_object((CamelMedium *)signature, dw); + camel_object_unref((CamelObject *)dw); + camel_mime_part_set_description(signature, _("This is a digitally signed message part")); + + /* setup our mime type and boundary */ + mime_type = header_content_type_new("multipart", "signed"); + header_content_type_set_param(mime_type, "micalg", camel_cipher_hash_to_id(context, hash)); + header_content_type_set_param(mime_type, "protocol", context->protocol); + camel_data_wrapper_set_mime_type_field(CAMEL_DATA_WRAPPER (mps), mime_type); + header_content_type_unref(mime_type); + camel_multipart_set_boundary((CamelMultipart *)mps, NULL); + + /* just keep the whole raw content. We dont *really* need to do this because + we know how we just proccessed it, but, well, better to be safe than sorry */ + mps->signature = signature; + mps->contentraw = mem; + camel_stream_reset(mem); + + /* clear the data-wrapper stream - tells write_to_stream to use the right object */ + if (((CamelDataWrapper *)mps)->stream) { + camel_object_unref((CamelObject *) ((CamelDataWrapper *)mps)->stream); + ((CamelDataWrapper *)mps)->stream = NULL; + } + + return 0; +} + +/** + * camel_multipart_signed_verify: + * @mps: + * @context: + * @ex: + * + * Verify a signed object. This may be used to verify newly signed + * objects as well as those created from external streams or parsers. + * + * Return value: A validity value, or NULL on error, setting @ex + * appropriately. + **/ +CamelCipherValidity * +camel_multipart_signed_verify(CamelMultipartSigned *mps, CamelCipherContext *context, CamelException *ex) +{ + CamelCipherValidity *valid; + CamelMimePart *sigpart; + CamelStream *sigstream, *constream; + + /* we need to be able to verify stuff we just signed as well as stuff we loaded from a stream/parser */ + + if (mps->contentraw) { + constream = mps->contentraw; + camel_object_ref((CamelObject *)constream); + } else { + CamelStream *sub; + CamelMimeFilter *canon_filter; + + if (mps->start1 == -1 && parse_content(mps) == -1) { + camel_exception_setv(ex, 1, _("parse error")); + return NULL; + } + + /* first, prepare our parts */ + sub = camel_seekable_substream_new((CamelSeekableStream *)((CamelDataWrapper *)mps)->stream, mps->start1, mps->end1); + constream = (CamelStream *)camel_stream_filter_new_with_stream(sub); + camel_object_unref((CamelObject *)sub); + + /* Note: see rfc2015 or rfc3156, section 5 */ + canon_filter = camel_mime_filter_canon_new(CAMEL_MIME_FILTER_CANON_STRIP|CAMEL_MIME_FILTER_CANON_CRLF); + camel_stream_filter_add((CamelStreamFilter *)constream, (CamelMimeFilter *)canon_filter); + camel_object_unref((CamelObject *)canon_filter); + } + + /* we do this as a normal mime part so we can have it handle transfer encoding etc */ + sigstream = camel_stream_mem_new(); + sigpart = camel_multipart_get_part((CamelMultipart *)mps, CAMEL_MULTIPART_SIGNED_SIGNATURE); + camel_data_wrapper_write_to_stream((CamelDataWrapper *)sigpart, sigstream); + camel_stream_reset(sigstream); + + /* do the magic, the caller must supply the right context for this kind of object */ + valid = camel_cipher_verify(context, camel_cipher_id_to_hash(context, mps->micalg), constream, sigstream, ex); + +#if 0 + { + CamelStream *sout = camel_stream_fs_new_with_fd(dup(0)); + + camel_stream_printf(sout, "-- Verifying:\n"); + camel_stream_reset(constream); + camel_stream_write_to_stream(constream, sout); + camel_stream_printf(sout, "-- end\n"); + camel_object_unref((CamelObject *)sout); + } +#endif + + camel_object_unref((CamelObject *)constream); + camel_object_unref((CamelObject *)sigstream); + + return valid; +} + + diff --git a/camel/camel-multipart-signed.h b/camel/camel-multipart-signed.h new file mode 100644 index 0000000000..7371e370ed --- /dev/null +++ b/camel/camel-multipart-signed.h @@ -0,0 +1,103 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * camel-signed--multipart.h : class for a signed-multipart + * + * Authors: Michael Zucchi + * + * Copyright 2002 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +/* Should this be a subclass of multipart? + No, because we dont have different parts? + I'm not really sure yet ... ? */ + +#ifndef CAMEL_MULTIPART_SIGNED_H +#define CAMEL_MULTIPART_SIGNED_H 1 + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus }*/ + +#include +#include + +#define CAMEL_MULTIPART_SIGNED_TYPE (camel_multipart_signed_get_type ()) +#define CAMEL_MULTIPART_SIGNED(obj) (CAMEL_CHECK_CAST((obj), CAMEL_MULTIPART_SIGNED_TYPE, CamelMultipartSigned)) +#define CAMEL_MULTIPART_SIGNED_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_MULTIPART_SIGNED_TYPE, CamelMultipartSignedClass)) +#define CAMEL_IS_MULTIPART_SIGNED(o) (CAMEL_CHECK_TYPE((o), CAMEL_MULTIPART_SIGNED_TYPE)) + +/* +enum { + CAMEL_MULTIPART_EMPTY, + CAMEL_MULTIPART_CONST, + CAMEL_MULTIPART_SIGN, + CAMEL_MULTIPART_ENCR, +}; +*/ + +/* 'handy' enums for getting the internal parts of the multipart */ +enum { + CAMEL_MULTIPART_SIGNED_CONTENT, + CAMEL_MULTIPART_SIGNED_SIGNATURE, +}; + +typedef struct _CamelMultipartSigned CamelMultipartSigned; + +struct _CamelMultipartSigned +{ + CamelMultipart parent_object; + + /* these are the client visible parts, decoded forms of our data wrapper content */ + CamelMimePart *content; + CamelMimePart *signature; + + /* the raw content which must go over the wire, if we have generated it */ + /* perhaps this should jsut set data_wrapper->stream and update start1/end1 accordingly, as it is done + for other parts, or visa versa? */ + CamelStream *contentraw; + + /*int state;*/ + + /* just cache some info we use */ + char *protocol; + char *micalg; + + /* offset pointers of start of boundary in content object */ + off_t start1, end1; + off_t start2, end2; +}; + +typedef struct { + CamelMultipartClass parent_class; +} CamelMultipartSignedClass; + +/* Standard Camel function */ +CamelType camel_multipart_signed_get_type (void); + +/* public methods */ +CamelMultipartSigned *camel_multipart_signed_new (void); + +int camel_multipart_signed_sign (CamelMultipartSigned *mps, CamelCipherContext *context, CamelMimePart *content, const char *uiserid, CamelCipherHash hash, CamelException *ex); +CamelCipherValidity *camel_multipart_signed_verify (CamelMultipartSigned *mps, CamelCipherContext *context, CamelException *ex); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* CAMEL_MULTIPART_SIGNED_H */ + + diff --git a/camel/camel-multipart.c b/camel/camel-multipart.c index f82b66312a..f792dffaaa 100644 --- a/camel/camel-multipart.c +++ b/camel/camel-multipart.c @@ -1,9 +1,6 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* camel-multipart.c : Abstract class for a multipart */ -#ifndef NO_WARNINGS -#warning This should be a mostly abstract class, but it is not! -#endif /* * @@ -34,6 +31,7 @@ #include /* strlen() */ #include /* for getpid */ #include /* for time */ +#include #include "camel-stream-mem.h" #include "camel-multipart.h" @@ -63,6 +61,8 @@ static int write_to_stream (CamelDataWrapper *data_wrapper, CamelStream *stream); static void unref_part (gpointer data, gpointer user_data); +static int construct_from_parser(CamelMultipart *multipart, struct _CamelMimeParser *mp); + static CamelDataWrapperClass *parent_class = NULL; @@ -91,6 +91,7 @@ camel_multipart_class_init (CamelMultipartClass *camel_multipart_class) camel_multipart_class->get_number = get_number; camel_multipart_class->set_boundary = set_boundary; camel_multipart_class->get_boundary = get_boundary; + camel_multipart_class->construct_from_parser = construct_from_parser; /* virtual method overload */ camel_data_wrapper_class->write_to_stream = write_to_stream; @@ -115,8 +116,8 @@ camel_multipart_finalize (CamelObject *object) g_list_foreach (multipart->parts, unref_part, NULL); - if (multipart->boundary) - g_free (multipart->boundary); + /*if (multipart->boundary) + g_free (multipart->boundary);*/ if (multipart->preface) g_free (multipart->preface); if (multipart->postface) @@ -539,3 +540,49 @@ camel_multipart_set_postface(CamelMultipart *multipart, const char *postface) multipart->postface = NULL; } } + +static int +construct_from_parser(CamelMultipart *multipart, struct _CamelMimeParser *mp) +{ + int err; + struct _header_content_type *content_type; + CamelMimePart *bodypart; + char *buf; + unsigned int len; + + g_assert(camel_mime_parser_state(mp) == HSCAN_MULTIPART); + + /* FIXME: we should use a came-mime-mutlipart, not jsut a camel-multipart, but who cares */ + d(printf("Creating multi-part\n")); + + content_type = camel_mime_parser_content_type(mp); + camel_multipart_set_boundary(multipart, + header_content_type_param(content_type, "boundary")); + + while (camel_mime_parser_step(mp, &buf, &len) != HSCAN_MULTIPART_END) { + camel_mime_parser_unstep(mp); + bodypart = camel_mime_part_new(); + camel_mime_part_construct_from_parser(bodypart, mp); + camel_multipart_add_part(multipart, bodypart); + camel_object_unref((CamelObject *)bodypart); + } + + /* these are only return valid data in the MULTIPART_END state */ + camel_multipart_set_preface(multipart, camel_mime_parser_preface (mp)); + camel_multipart_set_postface(multipart, camel_mime_parser_postface (mp)); + + err = camel_mime_parser_errno(mp); + if (err != 0) { + errno = err; + return -1; + } else + return 0; +} + +int +camel_multipart_construct_from_parser(CamelMultipart *multipart, struct _CamelMimeParser *mp) +{ + g_return_val_if_fail(CAMEL_IS_MULTIPART(multipart), -1); + + return CMP_CLASS(multipart)->construct_from_parser(multipart, mp); +} diff --git a/camel/camel-multipart.h b/camel/camel-multipart.h index dfa8908bb2..ac2e37f540 100644 --- a/camel/camel-multipart.h +++ b/camel/camel-multipart.h @@ -40,21 +40,17 @@ extern "C" { #define CAMEL_MULTIPART_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), CAMEL_MULTIPART_TYPE, CamelMultipartClass)) #define CAMEL_IS_MULTIPART(o) (CAMEL_CHECK_TYPE((o), CAMEL_MULTIPART_TYPE)) +struct _CamelMimeParser; struct _CamelMultipart { CamelDataWrapper parent_object; - CamelMimePart *parent; GList *parts; - gchar *boundary; gchar *preface; gchar *postface; - }; - - typedef struct { CamelDataWrapperClass parent_class; @@ -68,8 +64,10 @@ typedef struct { void (*set_boundary) (CamelMultipart *multipart, const char *boundary); const gchar * (*get_boundary) (CamelMultipart *multipart); -} CamelMultipartClass; + int (*construct_from_parser)(CamelMultipart *, struct _CamelMimeParser *); + /*int (*construct_from_stream)(CamelMultipart *, CamelStream *);*/ +} CamelMultipartClass; /* Standard Camel function */ CamelType camel_multipart_get_type (void); @@ -96,6 +94,8 @@ const gchar * camel_multipart_get_boundary (CamelMultipart *multipart); void camel_multipart_set_preface (CamelMultipart *multipart, const char *preface); void camel_multipart_set_postface (CamelMultipart *multipart, const char *postface); +int camel_multipart_construct_from_parser(CamelMultipart *multipart, struct _CamelMimeParser *mp); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/camel/camel-pgp-context.c b/camel/camel-pgp-context.c index e6ef1a7820..d1c04b831f 100644 --- a/camel/camel-pgp-context.c +++ b/camel/camel-pgp-context.c @@ -43,6 +43,8 @@ #include #include +#include + #include #include #include @@ -51,6 +53,8 @@ #include #include +#include + #include #include #include @@ -76,12 +80,19 @@ static int pgp_encrypt (CamelCipherContext *context, gboolean s static int pgp_decrypt (CamelCipherContext *context, CamelStream *istream, CamelStream *ostream, CamelException *ex); +static const char *pgp_hash_to_id(CamelCipherContext *context, CamelCipherHash hash); +static CamelCipherHash pgp_id_to_hash(CamelCipherContext *context, const char *id); + static CamelCipherContextClass *parent_class; static void camel_pgp_context_init (CamelPgpContext *context) { + CamelCipherContext *ciph = (CamelCipherContext *)context; + context->priv = g_new0 (struct _CamelPgpContextPrivate, 1); + + ciph->protocol = "application/pgp-encrypted"; } static void @@ -107,6 +118,8 @@ camel_pgp_context_class_init (CamelPgpContextClass *camel_pgp_context_class) camel_cipher_context_class->verify = pgp_verify; camel_cipher_context_class->encrypt = pgp_encrypt; camel_cipher_context_class->decrypt = pgp_decrypt; + camel_cipher_context_class->hash_to_id = pgp_hash_to_id; + camel_cipher_context_class->id_to_hash = pgp_id_to_hash; } CamelType @@ -1476,3 +1489,45 @@ pgp_decrypt (CamelCipherContext *ctx, CamelStream *istream, return -1; } + +/* this has a 1:1 relationship to CamelCipherHash */ +static char *name_table[] = { + "pgp-sha1", /* we use sha1 as the 'default' */ + "pgp-md2", + "pgp-md5", + "pgp-sha1", + "pgp-ripemd160", +}; + +static const char *pgp_hash_to_id(CamelCipherContext *context, CamelCipherHash hash) +{ + /* if we dont know, just use default? */ + if (hash > sizeof(name_table)/sizeof(name_table[0])) + hash = CAMEL_CIPHER_HASH_DEFAULT; + + return name_table[hash]; +} + +static CamelCipherHash pgp_id_to_hash(CamelCipherContext *context, const char *id) +{ + int i; + unsigned char *tmpid, *o; + const char *in; + unsigned char c; + + if (id == NULL) + return CAMEL_CIPHER_HASH_DEFAULT; + + tmpid = alloca(strlen(id)+1); + in = id; + o = tmpid; + while ((c = (unsigned char)*in++)) + *o++ = tolower(c); + + for (i=1;i sizeof(name_table)/sizeof(name_table[0]) + || name_table[hash] == NULL; + hash = CAMEL_CIPHER_HASH_DEFAULT; + + return name_table[hash]; +} + +static CamelCipherHash smime_id_to_hash(CamelCipherContext *context, const char *id) +{ + int i; + unsigned char *tmpid, *o, *in; + unsigned char c; + + if (id == NULL) + return CAMEL_CIPHER_HASH_DEFAULT; + + tmpid = alloca(strlen(id)+1); + in = id; + o = tmpid; + while ((c = *in++)) + *o++ = tolower(c); + + for (i=1;i*/ +/* use my malloc debugger? */ +/*extern void g_check(void *mp);*/ +#define g_check(x) + struct _filter { struct _filter *next; int id; @@ -44,7 +48,7 @@ struct _CamelStreamFilterPrivate { int last_was_read; /* was the last op read or write? */ }; -#define READ_PAD (64) /* bytes padded before buffer */ +#define READ_PAD (128) /* bytes padded before buffer */ #define READ_SIZE (4096) #define _PRIVATE(o) (((CamelStreamFilter *)(o))->priv) @@ -211,8 +215,10 @@ do_read (CamelStream *stream, char *buffer, size_t n) p->last_was_read = TRUE; + g_check(p->realbuffer); + if (p->filteredlen<=0) { - int presize = READ_SIZE; + int presize = READ_PAD; size = camel_stream_read(filter->source, p->buffer, READ_SIZE); if (size <= 0) { @@ -224,6 +230,7 @@ do_read (CamelStream *stream, char *buffer, size_t n) while (f) { camel_mime_filter_complete(f->filter, p->filtered, p->filteredlen, presize, &p->filtered, &p->filteredlen, &presize); + g_check(p->realbuffer); f = f->next; } size = p->filteredlen; @@ -242,6 +249,7 @@ do_read (CamelStream *stream, char *buffer, size_t n) while (f) { camel_mime_filter_filter(f->filter, p->filtered, p->filteredlen, presize, &p->filtered, &p->filteredlen, &presize); + g_check(p->realbuffer); d(printf ("Filtered content (%s): '", camel_type_to_name(((CamelObject *)f->filter)->s.type))); @@ -258,6 +266,8 @@ do_read (CamelStream *stream, char *buffer, size_t n) p->filteredlen -= size; p->filtered += size; + g_check(p->realbuffer); + return size; } @@ -280,11 +290,15 @@ do_write (CamelStream *stream, const char *buf, size_t n) d(fwrite(buffer, sizeof(char), len, stdout)); d(printf("'\n")); + g_check(p->realbuffer); + f = p->filters; presize = 0; while (f) { camel_mime_filter_filter(f->filter, buffer, len, presize, &buffer, &len, &presize); + g_check(p->realbuffer); + d(printf ("Filtered content (%s): '", camel_type_to_name(((CamelObject *)f->filter)->s.type))); d(fwrite(buffer, sizeof(char), len, stdout)); @@ -296,6 +310,8 @@ do_write (CamelStream *stream, const char *buf, size_t n) if (camel_stream_write(filter->source, buffer, len) != len) return -1; + g_check(p->realbuffer); + return n; } diff --git a/camel/providers/nntp/camel-nntp-stream.c b/camel/providers/nntp/camel-nntp-stream.c index 9072a8b75a..1e2dcb23f9 100644 --- a/camel/providers/nntp/camel-nntp-stream.c +++ b/camel/providers/nntp/camel-nntp-stream.c @@ -457,6 +457,6 @@ int camel_nntp_stream_getd(CamelNNTPStream *is, unsigned char **start, unsigned *start = s; dd(printf("NNTP_STREAM_GETD(%s,%d): '%.*s'\n", "more", *len, (int)*len, *start)); - return 1; } + diff --git a/camel/tests/stream/test3.c b/camel/tests/stream/test3.c index b870a7773e..f85a8ce105 100644 --- a/camel/tests/stream/test3.c +++ b/camel/tests/stream/test3.c @@ -50,13 +50,13 @@ int main(int argc, char **argv) check(ss != NULL); for (i=0;i