From 27937ec41f94720ffded271dff901dc74712845b Mon Sep 17 00:00:00 2001 From: Michael Zucci Date: Mon, 24 Apr 2000 09:12:31 +0000 Subject: Big fixes for the last commit which broke the message creation entirely. Plus an address decoder/formatter, etc. * camel-mime-message.c (construct_from_parser): Allow MESSAGE_END _or_ EOF as valid termination conditions. * providers/mbox/camel-mbox-summary.c (message_struct_new): Decode and then re-encode the addresses, so they are consistently formatted. * camel-mime-utils.c (header_decode_mailbox): Store the address in a _header_address. And try to get a comment-stored name if there is one. (header_decode_address): Actually return an address. (header_to_decode): Renamed to header_address_decode() (header_mailbox_decode): New function to get a single mailbox. (header_mime_decode): Return the major/minor value, as appropriate. (header_address_new, and friends): Whole bunch of utility functions for working with the address thingies. (header_decode_domain): Free the string header, and dont expand '.' into ' . '. (camel_mime_part_construct_content_from_parser): Oops, this was totally screwed up, try creating the right cotnent on the right object. svn path=/trunk/; revision=2579 --- camel/ChangeLog | 23 +++ camel/camel-mime-message.c | 10 +- camel/camel-mime-message.h | 8 +- camel/camel-mime-part-utils.c | 8 +- camel/camel-mime-part.c | 6 +- camel/camel-mime-utils.c | 299 ++++++++++++++++++++++++++---- camel/camel-mime-utils.h | 38 ++++ camel/providers/mbox/camel-mbox-summary.c | 20 +- 8 files changed, 364 insertions(+), 48 deletions(-) diff --git a/camel/ChangeLog b/camel/ChangeLog index a7b0e30621..1760e032a4 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -1,5 +1,25 @@ 2000-04-24 NotZed + * camel-mime-message.c (construct_from_parser): Allow MESSAGE_END + _or_ EOF as valid termination conditions. + + * providers/mbox/camel-mbox-summary.c (message_struct_new): Decode + and then re-encode the addresses, so they are consistently + formatted. + + * camel-mime-utils.c (header_decode_mailbox): Store the address in + a _header_address. And try to get a comment-stored name if there + is one. + (header_decode_address): Actually return an address. + (header_to_decode): Renamed to header_address_decode() + (header_mailbox_decode): New function to get a single mailbox. + (header_mime_decode): Return the major/minor value, as + appropriate. + (header_address_new, and friends): Whole bunch of utility + functions for working with the address thingies. + (header_decode_domain): Free the string header, and dont expand + '.' into ' . '. + * camel.c (camel_init): No longer call data_wrapper_repository_init. @@ -23,6 +43,9 @@ * camel-mime-part-utils.c (camel_mime_part_construct_content_from_parser): Create the contents of multipart and simple messages. + (camel_mime_part_construct_content_from_parser): Oops, this was + totally screwed up, try creating the right cotnent on the right + object. * camel-multipart.c (construct_from_parser): Moved to camel-mime-part-utils. diff --git a/camel/camel-mime-message.c b/camel/camel-mime-message.c index f63548e5a5..25c1c667e2 100644 --- a/camel/camel-mime-message.c +++ b/camel/camel-mime-message.c @@ -485,17 +485,23 @@ construct_from_parser(CamelMimePart *dw, CamelMimeParser *mp) { char *buf; int len; + int state; d(printf("constructing mime-message\n")); + d(printf("mime_message::construct_from_parser()\n")); + /* let the mime-part construct the guts ... */ ((CamelMimePartClass *)parent_class)->construct_from_parser(dw, mp); /* ... then clean up the follow-on state */ - if (camel_mime_parser_step(mp, &buf, &len) != HSCAN_MESSAGE_END) { - g_warning("Bad parser state: Expecing MESSAGE_END, got: %d", camel_mime_parser_state(mp)); + state = camel_mime_parser_step(mp, &buf, &len); + if (!(state == HSCAN_MESSAGE_END || state == HSCAN_EOF)) { + g_warning("Bad parser state: Expecing MESSAGE_END or EOF, got: %d", camel_mime_parser_state(mp)); camel_mime_parser_unstep(mp); } + + d(printf("mime_message::construct_from_parser() leaving\n")); } #ifdef WHPT diff --git a/camel/camel-mime-message.h b/camel/camel-mime-message.h index ddcf846c20..7142d876de 100644 --- a/camel/camel-mime-message.h +++ b/camel/camel-mime-message.h @@ -35,10 +35,10 @@ extern "C" { #endif /* __cplusplus }*/ #include -#include "camel-types.h" -#include "camel-mime-part.h" -#include "camel-recipient.h" - +#include +#include +#include +#include #define CAMEL_RECIPIENT_TYPE_TO "To" #define CAMEL_RECIPIENT_TYPE_CC "Cc" diff --git a/camel/camel-mime-part-utils.c b/camel/camel-mime-part-utils.c index 460a6c7d03..50e9fd29d5 100644 --- a/camel/camel-mime-part-utils.c +++ b/camel/camel-mime-part-utils.c @@ -104,7 +104,6 @@ simple_data_wrapper_construct_from_parser(CamelDataWrapper *dw, CamelMimeParser buffer = g_byte_array_new(); - /* write to a memory buffer or something??? */ start = camel_mime_parser_tell(mp); while ( camel_mime_parser_step(mp, &buf, &len) != HSCAN_BODY_END ) { if (buffer) { @@ -174,11 +173,12 @@ camel_mime_part_construct_content_from_parser(CamelMimePart *dw, CamelMimeParser case HSCAN_HEADER: d(printf("Creating body part\n")); content = (CamelDataWrapper *)camel_simple_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(); - simple_data_wrapper_construct_from_parser(content, mp); + camel_mime_part_construct_from_parser((CamelMimePart *)content, mp); break; case HSCAN_MULTIPART: { CamelDataWrapper *bodypart; @@ -193,8 +193,10 @@ camel_mime_part_construct_content_from_parser(CamelMimePart *dw, CamelMimeParser camel_mime_parser_unstep(mp); bodypart = (CamelDataWrapper *)camel_mime_body_part_new(); camel_mime_part_construct_from_parser((CamelMimePart *)bodypart, mp); - camel_multipart_add_part((CamelMultipart *)dw, (CamelMimeBodyPart *)bodypart); + camel_multipart_add_part((CamelMultipart *)content, (CamelMimeBodyPart *)bodypart); } + + d(printf("Created multi-part\n")); break; } default: g_warning("Invalid state encountered???: %d", camel_mime_parser_state(mp)); diff --git a/camel/camel-mime-part.c b/camel/camel-mime-part.c index d84d560eff..9d4b98a07e 100644 --- a/camel/camel-mime-part.c +++ b/camel/camel-mime-part.c @@ -546,7 +546,7 @@ construct_from_parser(CamelMimePart *dw, CamelMimeParser *mp) char *buf; int len; - d(printf("constructing mime-part\n")); + d(printf("mime_part::construct_from_parser()\n")); switch (camel_mime_parser_step(mp, &buf, &len)) { case HSCAN_HEADER: @@ -563,6 +563,8 @@ construct_from_parser(CamelMimePart *dw, CamelMimeParser *mp) default: g_warning("Invalid state encountered???: %d", camel_mime_parser_state(mp)); } + + d(printf("mime_part::construct_from_parser() leaving\n")); } void @@ -576,6 +578,8 @@ construct_from_stream(CamelDataWrapper *dw, CamelStream *s) { CamelMimeParser *mp; + printf("mime_part::construct_from_stream()\n"); + mp = camel_mime_parser_new(); if (camel_mime_parser_init_with_stream(mp, s) == -1) { g_warning("Cannot create parser for stream"); diff --git a/camel/camel-mime-utils.c b/camel/camel-mime-utils.c index f9e2389fee..ceb528ce28 100644 --- a/camel/camel-mime-utils.c +++ b/camel/camel-mime-utils.c @@ -1195,6 +1195,7 @@ header_decode_domain(const char **in) { const char *inptr = *in, *start; int go = TRUE; + char *ret; GString *domain = g_string_new(""); /* domain ref | domain literal */ @@ -1226,7 +1227,7 @@ header_decode_domain(const char **in) } header_decode_lwsp(&inptr); if (*inptr == '.') { /* next sub-domain? */ - g_string_append(domain, " . "); + g_string_append_c(domain, '.'); inptr++; header_decode_lwsp(&inptr); } else @@ -1235,8 +1236,9 @@ header_decode_domain(const char **in) *in = inptr; - /* FIXME:L free string header */ - return domain->str; + ret = domain->str; + g_string_free(domain, FALSE); + return ret; } static char * @@ -1301,15 +1303,15 @@ header_decode_addrspec(const char **in) *(word) '<' [ *('@' domain ) ':' ] word *( '.' word) @ domain */ -/* FIXME: what does this return? */ - -static void +static struct _header_address * header_decode_mailbox(const char **in) { const char *inptr = *in; char *pre; int closeme = FALSE; GString *addr; + GString *name = NULL; + struct _header_address *address = NULL; addr = g_string_new(""); @@ -1317,10 +1319,18 @@ header_decode_mailbox(const char **in) pre = header_decode_word(&inptr); header_decode_lwsp(&inptr); if (!(*inptr == '.' || *inptr == '@' || *inptr==',' || *inptr=='\0')) { /* ',' and '\0' required incase it is a simple address, no @ domain part (buggy writer) */ - /* FIXME: rfc 2047 decode each word */ + name = g_string_new(""); while (pre) { + char *text; + + text = header_decode_string(pre); + g_string_append(name, text); + g_free(pre); + /* rfc_decode(pre) */ pre = header_decode_word(&inptr); + if (pre) + g_string_append_c(name, ' '); } header_decode_lwsp(&inptr); if (*inptr == '<') { @@ -1359,11 +1369,13 @@ header_decode_mailbox(const char **in) /* should be at word '.' localpart */ while (*inptr == '.' && pre) { inptr++; + g_free(pre); pre = header_decode_word(&inptr); g_string_append_c(addr, '.'); g_string_append(addr, pre); header_decode_lwsp(&inptr); } + g_free(pre); /* now at '@' domain part */ if (*inptr == '@') { @@ -1384,20 +1396,41 @@ header_decode_mailbox(const char **in) } else { g_warning("invalid route address, no closing '>': %s", *in); } + } else if (name == NULL) { /* check for comment after address */ + char *text, *tmp; + const char *comment = inptr; + + header_decode_lwsp(&inptr); + if (inptr-comment > 3) { /* just guess ... */ + tmp = g_strndup(comment, inptr-comment); + text = header_decode_string(tmp); + name = g_string_new(text); + g_free(tmp); + g_free(text); + } } *in = inptr; + if (addr->len > 0) { + address = header_address_new_name(name?name->str:"", addr->str); + } + + g_string_free(addr, TRUE); + if (name) + g_string_free(name, TRUE); + d(printf("got mailbox: %s\n", addr->str)); + return address; } -/* FIXME: what does this return? */ -static void +static struct _header_address * header_decode_address(const char **in) { const char *inptr = *in; char *pre; GString *group = g_string_new(""); + struct _header_address *addr = NULL, *member; /* pre-scan, trying to work out format, discard results */ header_decode_lwsp(&inptr); @@ -1409,6 +1442,7 @@ header_decode_address(const char **in) header_decode_lwsp(&inptr); if (*inptr == ':') { d(printf("group detected: %s\n", group->str)); + addr = header_address_new_group(group->str); /* that was a group spec, scan mailbox's */ inptr++; /* FIXME: check rfc 2047 encodings of words, here or above in the loop */ @@ -1416,7 +1450,9 @@ header_decode_address(const char **in) if (*inptr != ';') { int go = TRUE; do { - header_decode_mailbox(&inptr); + member = header_decode_mailbox(&inptr); + if (member) + header_address_add_member(addr, member); header_decode_lwsp(&inptr); if (*inptr == ',') inptr++; @@ -1432,17 +1468,13 @@ header_decode_address(const char **in) inptr++; } *in = inptr; -/* } else if (*inptr == '.' || *inptr == '<' || *inptr == '@') {*/ } else { - /* back-track, and rescan. not worth the code duplication to do this faster */ - /* this will detect invalid input */ - header_decode_mailbox(in); - }/* else { - g_warning("Cannot scan address at '%c': %s", *inptr, *in); - }*/ - - /* FIXME: store gropu somewhere */ + addr = header_decode_mailbox(in); + } + g_string_free(group, TRUE); + + return addr; } char * @@ -1481,21 +1513,33 @@ header_msgid_decode(const char *in) return msgid; } -void -header_to_decode(const char *in) +struct _header_address * +header_mailbox_decode(const char *in) +{ + if (in == NULL) + return NULL; + + return header_decode_mailbox(&in); +} + +struct _header_address * +header_address_decode(const char *in) { const char *inptr = in, *last; + struct _header_address *list = NULL, *addr; d(printf("decoding To: '%s'\n", in)); #warning header_to_decode needs to return some structure if (in == NULL) - return; + return NULL; do { last = inptr; - header_decode_address(&inptr); + addr = header_decode_address(&inptr); + if (addr) + header_address_list_append(&list, addr); header_decode_lwsp(&inptr); if (*inptr == ',') inptr++; @@ -1510,33 +1554,37 @@ header_to_decode(const char *in) if (inptr == last) { g_warning("detected invalid input loop at : %s", last); } + + return list; } void -header_mime_decode(const char *in) +header_mime_decode(const char *in, int *maj, int *min) { const char *inptr = in; int major=-1, minor=-1; d(printf("decoding MIME-Version: '%s'\n", in)); -#warning header_mime_decode needs to return the version - - if (in == NULL) - return; - - header_decode_lwsp(&inptr); - if (isdigit(*inptr)) { - major = header_decode_int(&inptr); + if (in != NULL) { header_decode_lwsp(&inptr); - if (*inptr == '.') { - inptr++; + if (isdigit(*inptr)) { + major = header_decode_int(&inptr); header_decode_lwsp(&inptr); - if (isdigit(*inptr)) - minor = header_decode_int(&inptr); + if (*inptr == '.') { + inptr++; + header_decode_lwsp(&inptr); + if (isdigit(*inptr)) + minor = header_decode_int(&inptr); + } } } + if (maj) + *maj = major; + if (min) + *min = minor; + d(printf("major = %d, minor = %d\n", major, minor)); } @@ -2081,6 +2129,185 @@ header_raw_clear(struct _header_raw **list) } +/* ok, here's the address stuff, what a mess ... */ +struct _header_address *header_address_new(void) +{ + struct _header_address *h; + h = g_malloc0(sizeof(*h)); + h->type = HEADER_ADDRESS_NONE; + h->refcount = 1; + return h; +} + +struct _header_address *header_address_new_name(const char *name, const char *addr) +{ + struct _header_address *h; + + h = header_address_new(); + h->type = HEADER_ADDRESS_NAME; + h->name = g_strdup(name); + h->v.addr = g_strdup(addr); + return h; +} + +struct _header_address *header_address_new_group(const char *name) +{ + struct _header_address *h; + + h = header_address_new(); + h->type = HEADER_ADDRESS_GROUP; + h->name = g_strdup(name); + return h; +} + +void header_address_ref(struct _header_address *h) +{ + if (h) + h->refcount++; +} + +void header_address_unref(struct _header_address *h) +{ + if (h) { + if (h->refcount <= 1) { + if (h->type == HEADER_ADDRESS_GROUP) { + header_address_list_clear(&h->v.members); + } else if (h->type == HEADER_ADDRESS_NAME) { + g_free(h->v.addr); + } + g_free(h->name); + g_free(h); + } else { + h->refcount--; + } + } +} + +void header_address_set_name(struct _header_address *h, const char *name) +{ + if (h) { + g_free(h->name); + h->name = g_strdup(name); + } +} + +void header_address_set_addr(struct _header_address *h, const char *addr) +{ + if (h) { + if (h->type == HEADER_ADDRESS_NAME + || h->type == HEADER_ADDRESS_NONE) { + h->type = HEADER_ADDRESS_NAME; + g_free(h->v.addr); + h->v.addr = g_strdup(addr); + } else { + g_warning("Trying to set the address on a group"); + } + } +} + +void header_address_set_members(struct _header_address *h, struct _header_address *group) +{ + if (h) { + if (h->type == HEADER_ADDRESS_GROUP + || h->type == HEADER_ADDRESS_NONE) { + h->type = HEADER_ADDRESS_GROUP; + header_address_list_clear(&h->v.members); + /* should this ref them? */ + h->v.members = group; + } else { + g_warning("Trying to set the members on a name, not group"); + } + } +} + +void header_address_add_member(struct _header_address *h, struct _header_address *member) +{ + if (h) { + if (h->type == HEADER_ADDRESS_GROUP + || h->type == HEADER_ADDRESS_NONE) { + h->type = HEADER_ADDRESS_GROUP; + header_address_list_append(&h->v.members, member); + } + } +} + +void header_address_list_append_list(struct _header_address **l, struct _header_address **h) +{ + if (l) { + struct _header_address *n = (struct _header_address *)l; + + while (n->next) + n = n->next; + n->next = *h; + } +} + + +void header_address_list_append(struct _header_address **l, struct _header_address *h) +{ + if (h) { + header_address_list_append_list(l, &h); + h->next = NULL; + } +} + +void header_address_list_clear(struct _header_address **l) +{ + struct _header_address *a, *n; + a = (struct _header_address *)l; + while (a && a->next) { + n = a->next; + a = n->next; + header_address_unref(n); + } +} + +static void +header_address_list_format_append(GString *out, struct _header_address *a) +{ + char *text; + + while (a) { + switch (a->type) { + case HEADER_ADDRESS_NAME: +#warning needs to rfc2047 encode address phrase + /* FIXME: 2047 encoding?? */ + if (a->name) + g_string_sprintfa(out, "\"%s\" <%s>", a->name, a->v.addr); + else + g_string_sprintfa(out, "<%s>", a->v.addr); + break; + case HEADER_ADDRESS_GROUP: + text = header_encode_string(a->name); + g_string_sprintfa(out, "%s:\n ", text); + header_address_list_format_append(out, a->v.members); + g_string_sprintfa(out, ";"); + break; + default: + g_warning("Invalid address type"); + break; + } + a = a->next; + } +} + +/* FIXME: need a 'display friendly' version, as well as a 'rfc friendly' version? */ +char * +header_address_list_format(struct _header_address *a) +{ + GString *out; + char *ret; + + if (a == NULL) + return NULL; + + out = g_string_new(""); + + header_address_list_format_append(out, a); + ret = out->str; + g_string_free(out, FALSE); + return ret; +} #ifdef BUILD_TABLE diff --git a/camel/camel-mime-utils.h b/camel/camel-mime-utils.h index febd0f8dbe..f95f76363c 100644 --- a/camel/camel-mime-utils.h +++ b/camel/camel-mime-utils.h @@ -52,6 +52,41 @@ typedef struct _CamelMimeDisposition { unsigned int refcount; } CamelMimeDisposition; +enum _header_address_type { + HEADER_ADDRESS_NONE, /* uninitialised */ + HEADER_ADDRESS_NAME, + HEADER_ADDRESS_GROUP +}; + +struct _header_address { + struct _header_address *next; + enum _header_address_type type; + char *name; + union { + char *addr; + struct _header_address *members; + } v; + unsigned int refcount; +}; + +/* Address lists */ +struct _header_address *header_address_new(void); +struct _header_address *header_address_new_name(const char *name, const char *addr); +struct _header_address *header_address_new_group(const char *name); +void header_address_ref(struct _header_address *); +void header_address_unref(struct _header_address *); +void header_address_set_name(struct _header_address *, const char *name); +void header_address_set_addr(struct _header_address *, const char *addr); +void header_address_set_members(struct _header_address *, struct _header_address *group); +void header_address_add_member(struct _header_address *, struct _header_address *member); +void header_address_list_append_list(struct _header_address **l, struct _header_address **h); +void header_address_list_append(struct _header_address **, struct _header_address *); +void header_address_list_clear(struct _header_address **); + +struct _header_address *header_address_decode(const char *in); +struct _header_address *header_mailbox_decode(const char *in); +char *header_address_list_format(struct _header_address *a); + /* structured header prameters */ char *header_param(struct _header_param *p, const char *name); struct _header_param *header_set_param(struct _header_param **l, const char *name, const char *value); @@ -102,6 +137,9 @@ char *header_format_date(time_t time, int offset); /* decode a message id */ char *header_msgid_decode(const char *in); +/* decode the mime-type header */ +void header_mime_decode(const char *in, int *maj, int *min); + /* do incremental base64/quoted-printable (de/en)coding */ int base64_decode_step(unsigned char *in, int len, unsigned char *out, int *state, unsigned int *save); diff --git a/camel/providers/mbox/camel-mbox-summary.c b/camel/providers/mbox/camel-mbox-summary.c index 22060bf8fe..f1d4a41b7d 100644 --- a/camel/providers/mbox/camel-mbox-summary.c +++ b/camel/providers/mbox/camel-mbox-summary.c @@ -345,13 +345,29 @@ static CamelMboxMessageInfo * message_struct_new(CamelMimeParser *mp, CamelMboxMessageContentInfo *parent, int start, int body, off_t xev_offset) { CamelMboxMessageInfo *ms; + struct _header_address *addr; + const char *text; ms = g_malloc0(sizeof(*ms)); /* FIXME: what about cc, sender vs from? */ ms->info.subject = header_decode_string(camel_mime_parser_header(mp, "subject", NULL)); - ms->info.from = g_strdup(camel_mime_parser_header(mp, "from", NULL)); - ms->info.to = g_strdup(camel_mime_parser_header(mp, "to", NULL)); + text = camel_mime_parser_header(mp, "from", NULL); + addr = header_address_decode(text); + if (addr) { + ms->info.from = header_address_list_format(addr); + header_address_list_clear(&addr); + } else { + ms->info.from = g_strdup(text); + } + text = camel_mime_parser_header(mp, "to", NULL); + addr = header_address_decode(text); + if (addr) { + ms->info.to = header_address_list_format(addr); + header_address_list_clear(&addr); + } else { + ms->info.to = g_strdup(text); + } ms->info.date_sent = header_decode_date(camel_mime_parser_header(mp, "date", NULL), NULL); ms->info.date_received = 0; -- cgit v1.2.3