diff options
Diffstat (limited to 'libproto/imap/fetch.c')
-rw-r--r-- | libproto/imap/fetch.c | 498 |
1 files changed, 465 insertions, 33 deletions
diff --git a/libproto/imap/fetch.c b/libproto/imap/fetch.c index b1994ae48..e44804110 100644 --- a/libproto/imap/fetch.c +++ b/libproto/imap/fetch.c @@ -25,6 +25,9 @@ #include <mailutils/address.h> #include <mailutils/cstr.h> #include <mailutils/cctype.h> +#include <mailutils/message.h> +#include <mailutils/mime.h> +#include <mailutils/assoc.h> #include <mailutils/imap.h> #include <mailutils/sys/imap.h> @@ -67,19 +70,11 @@ _free_fetch_response (void *ptr) break; case MU_IMAP_FETCH_BODYSTRUCTURE: - /* FIXME */ + mu_bodystructure_free (resp->bodystructure.bs); break; case MU_IMAP_FETCH_ENVELOPE: - free (resp->envelope.subject); - mu_address_destroy (&resp->envelope.from); - mu_address_destroy (&resp->envelope.sender); - mu_address_destroy (&resp->envelope.reply_to); - mu_address_destroy (&resp->envelope.to); - mu_address_destroy (&resp->envelope.cc); - mu_address_destroy (&resp->envelope.bcc); - free (resp->envelope.in_reply_to); - free (resp->envelope.message_id); + mu_message_imapenvelope_free (resp->envelope.imapenvelope); break; case MU_IMAP_FETCH_FLAGS: @@ -228,8 +223,9 @@ _body_mapper (union mu_imap_fetch_response *resp, partv = calloc (partc, sizeof (partv[0])); for (i = 0, p = section; i < partc; i++) { - partv[i] = strtoul (p, &p, 10); - p++; + char *q; + partv[i] = strtoul (p, &q, 10); + p = q + 1; } } @@ -327,12 +323,438 @@ _date_mapper (union mu_imap_fetch_response *resp, return 0; } -/* FIXME */ -#define _bodystructure_mapper NULL +static int parse_bodystructure (struct imap_list_element *elt, + struct mu_bodystructure **pbs); + +struct body_field_map +{ + size_t offset; /* Offset of the target member of mu_bodystructure */ + int (*mapper) (struct imap_list_element *, void *); +}; + +static int +parse_bs_list (struct imap_list_element *elt, + struct mu_bodystructure *bs, + struct body_field_map *map) +{ + int rc; + mu_iterator_t itr; + + rc = mu_list_get_iterator (elt->v.list, &itr); + if (rc) + return rc; + for (mu_iterator_first (itr); + map->mapper && !mu_iterator_is_done (itr); + mu_iterator_next (itr), map++) + { + struct imap_list_element *tok; + mu_iterator_current (itr, (void**)&tok); + rc = map->mapper (tok, (char*)bs + map->offset); + if (rc) + break; + } + mu_iterator_destroy (&itr); + return rc; +} + +static int +_map_body_param (void **itmv, size_t itmc, void *call_data) +{ + mu_assoc_t assoc = call_data; + struct mu_mime_param param; + struct imap_list_element *key, *val; + int rc; + + if (itmc != 2) + return MU_ERR_PARSE; + + key = itmv[0]; + val = itmv[1]; + if (key->type != imap_eltype_string || val->type != imap_eltype_string) + return MU_ERR_PARSE; + + rc = mu_rfc2047_decode_param ("UTF-8", val->v.string, ¶m); + if (rc) + { + param.lang = param.cset = NULL; + param.value = strdup (val->v.string); + if (!param.value) + return ENOMEM; + } + return mu_assoc_install (assoc, key->v.string, ¶m); +} + +static int +_body_field_text_mapper (struct imap_list_element *tok, void *ptr) +{ + char *s; + + if (_mu_imap_list_element_is_nil (tok)) + s = NULL; + else if (tok->type != imap_eltype_string) + return MU_ERR_PARSE; + else if (!(s = strdup (tok->v.string))) + return ENOMEM; + *(char**) ptr = s; + return 0; +} + +static int +_body_field_size_mapper (struct imap_list_element *tok, void *ptr) +{ + unsigned long n; + + if (_mu_imap_list_element_is_nil (tok)) + n = 0; + else if (tok->type != imap_eltype_string) + return MU_ERR_PARSE; + else + { + char *s; + + errno = 0; + n = strtoul (tok->v.string, &s, 10); + if (*s || errno) + return MU_ERR_PARSE; + } + *(size_t*) ptr = n; + return 0; +} + +static int +_body_field_param_mapper (struct imap_list_element *tok, void *ptr) +{ + mu_assoc_t param; + int rc = mu_mime_param_assoc_create (¶m); + if (rc) + return rc; + *(mu_assoc_t*) ptr = param; + if (_mu_imap_list_element_is_nil (tok)) + return 0; + if (tok->type != imap_eltype_list) + return MU_ERR_PARSE; + return mu_list_gmap (tok->v.list, _map_body_param, 2, param); +} + +static int +_body_field_disposition_mapper (struct imap_list_element *tok, void *ptr) +{ + int rc; + struct mu_bodystructure *bs = ptr; + struct imap_list_element *elt; + + if (_mu_imap_list_element_is_nil (tok)) + return 0; + if (tok->type != imap_eltype_list) + return MU_ERR_PARSE; + elt = _mu_imap_list_at (tok->v.list, 0); + if (_mu_imap_list_element_is_nil (elt)) + bs->body_disposition = NULL; + else if (elt->type != imap_eltype_string) + return MU_ERR_PARSE; + else if ((bs->body_disposition = strdup (elt->v.string)) == NULL) + return ENOMEM; + + rc = mu_mime_param_assoc_create (&bs->body_disp_param); + if (rc) + return rc; + + elt = _mu_imap_list_at (tok->v.list, 1); + if (_mu_imap_list_element_is_nil (elt)) + return 0; + else if (elt->type != imap_eltype_list) + return MU_ERR_PARSE; + return mu_list_gmap (elt->v.list, _map_body_param, 2, bs->body_disp_param); +} + +static int parse_envelope (struct imap_list_element *elt, + struct mu_imapenvelope **penv); + +static int +_body_field_imapenvelope_mapper (struct imap_list_element *tok, void *ptr) +{ + return parse_envelope (tok, ptr); +} + +static int +_body_field_bodystructure_mapper (struct imap_list_element *tok, void *ptr) +{ + return parse_bodystructure (tok, ptr); +} + +/* Simple text or message/rfc822 body. + + Sample TEXT body structure: + + ("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") + "<960723163407.20117h@cac.washington.edu>" "Compiler diff" + "BASE64" 4554 73) + + Elements: + + 0 "TEXT" body_type + 1 "PLAIN" body_subtype + 2 (...) body_param + 3 "<9607...>" body_id + 4 "Compiler diff" body_descr + 5 "BASE64" body_encoding + 6 4554 body_size + 7 73 v.text.body_lines + [Optional] + 8 body_md5 + 9 body_disposition; + 10 body_language; + 11 body_location; +*/ + +struct body_field_map base_field_map[] = { + { mu_offsetof (struct mu_bodystructure, body_type), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_subtype), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_param), + _body_field_param_mapper }, + { mu_offsetof (struct mu_bodystructure, body_id), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_descr), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_encoding), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_size), + _body_field_size_mapper }, + { mu_offsetof (struct mu_bodystructure, body_md5), + _body_field_text_mapper }, + { 0, _body_field_disposition_mapper }, + { mu_offsetof (struct mu_bodystructure, body_language), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_location), + _body_field_text_mapper }, + { 0, NULL } +}; + +struct body_field_map text_field_map[] = { + { mu_offsetof (struct mu_bodystructure, body_type), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_subtype), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_param), + _body_field_param_mapper }, + { mu_offsetof (struct mu_bodystructure, body_id), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_descr), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_encoding), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_size), + _body_field_size_mapper }, + { mu_offsetof (struct mu_bodystructure, v.text.body_lines), + _body_field_size_mapper }, + { mu_offsetof (struct mu_bodystructure, body_md5), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_disposition), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_language), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_location), + _body_field_text_mapper }, + { 0, NULL } +}; + +struct body_field_map message_field_map[] = { + { mu_offsetof (struct mu_bodystructure, body_type), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_subtype), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_param), + _body_field_param_mapper }, + { mu_offsetof (struct mu_bodystructure, body_id), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_descr), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_encoding), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_size), + _body_field_size_mapper }, + { mu_offsetof (struct mu_bodystructure, v.rfc822.body_env), + _body_field_imapenvelope_mapper }, + { mu_offsetof (struct mu_bodystructure, v.rfc822.body_struct), + _body_field_bodystructure_mapper }, + { mu_offsetof (struct mu_bodystructure, v.rfc822.body_lines), + _body_field_size_mapper }, + { mu_offsetof (struct mu_bodystructure, body_md5), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_disposition), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_language), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_location), + _body_field_text_mapper }, + { 0, NULL } +}; + +struct body_field_map multipart_field_map[] = { + /* Body type is processed separately */ + { mu_offsetof (struct mu_bodystructure, body_subtype), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_param), + _body_field_param_mapper }, + { 0, _body_field_disposition_mapper }, + { mu_offsetof (struct mu_bodystructure, body_language), + _body_field_text_mapper }, + { mu_offsetof (struct mu_bodystructure, body_location), + _body_field_text_mapper }, + { 0, NULL } +}; + +#define BSTOK_BODY_TYPE 0 +#define BSTOK_BODY_SUBTYPE 1 + +static int +_parse_bodystructure_simple (struct imap_list_element *elt, + struct mu_bodystructure *bs) +{ + size_t n; + struct imap_list_element *tok, *subtype; + struct body_field_map *map; + + mu_list_count (elt->v.list, &n); + if (n < 8) + return MU_ERR_PARSE; + + tok = _mu_imap_list_at (elt->v.list, BSTOK_BODY_TYPE); + if (!tok || tok->type != imap_eltype_string) + return MU_ERR_PARSE; + subtype = _mu_imap_list_at (elt->v.list, BSTOK_BODY_SUBTYPE); + if (!subtype || subtype->type != imap_eltype_string) + return MU_ERR_PARSE; + + if (mu_c_strcasecmp (tok->v.string, "TEXT") == 0) + { + bs->body_message_type = mu_message_text; + map = text_field_map; + } + else if (mu_c_strcasecmp (tok->v.string, "MESSAGE") == 0 && + mu_c_strcasecmp (subtype->v.string, "RFC822") == 0) + { + bs->body_message_type = mu_message_rfc822; + map = message_field_map; + } + else + { + bs->body_message_type = mu_message_other; + map = base_field_map; + } + + return parse_bs_list (elt, bs, map); +} + +/* Example multipart: + (("TEXT" "PLAIN" ("CHARSET" "US-ASCII") NIL NIL "7BIT" 1152 + 23) + ("TEXT" "PLAIN" ("CHARSET" "US-ASCII" "NAME" "cc.diff") + "<960723163407.20117h@cac.washington.edu>" "Compiler diff" + "BASE64" 4554 73) + "MIXED") + +*/ +static int +_parse_bodystructure_mixed (struct imap_list_element *elt, + struct mu_bodystructure *bs) +{ + int rc; + struct imap_list_element *tok; + mu_iterator_t itr; + struct body_field_map *map = multipart_field_map; + + bs->body_message_type = mu_message_multipart; + if (!(bs->body_type = strdup ("MULTIPART"))) + return ENOMEM; + + rc = mu_list_create (&bs->v.multipart.body_parts); + if (rc) + return rc; + + mu_list_set_destroy_item (bs->v.multipart.body_parts, + mu_list_free_bodystructure); + + rc = mu_list_get_iterator (elt->v.list, &itr); + if (rc) + return rc; + for (mu_iterator_first (itr); + !mu_iterator_is_done (itr); + mu_iterator_next (itr)) + { + struct mu_bodystructure *bspart; + + mu_iterator_current (itr, (void**) &tok); + if (!tok) + return MU_ERR_PARSE; + if (tok->type != imap_eltype_list) + break; + rc = parse_bodystructure (tok, &bspart); + if (rc) + return rc; + rc = mu_list_append (bs->v.multipart.body_parts, bspart); + if (rc) + { + mu_bodystructure_free (bspart); + return rc; + } + } + + if (mu_iterator_is_done (itr)) + return MU_ERR_PARSE; + + for (; map->mapper && !mu_iterator_is_done (itr); + mu_iterator_next (itr), map++) + { + struct imap_list_element *tok; + mu_iterator_current (itr, (void**)&tok); + rc = map->mapper (tok, (char*)bs + map->offset); + if (rc) + return rc; + } + mu_iterator_destroy (&itr); + return 0; +} + +static int +parse_bodystructure (struct imap_list_element *elt, + struct mu_bodystructure **pbs) +{ + int rc; + struct mu_bodystructure *bs; + struct imap_list_element *tok; + + if (elt->type != imap_eltype_list) + return MU_ERR_FAILURE; + bs = calloc (1, sizeof (*bs)); + if (!bs) + return ENOMEM; + tok = _mu_imap_list_at (elt->v.list, 0); + if (tok->type == imap_eltype_string) + rc = _parse_bodystructure_simple (elt, bs); + else + rc = _parse_bodystructure_mixed (elt, bs); + + if (rc) + mu_bodystructure_free (bs); + else + *pbs = bs; + return rc; +} + +static int +_bodystructure_mapper (union mu_imap_fetch_response *resp, + struct imap_list_element *elt, + struct parse_response_env *parse_env) +{ + return parse_bodystructure (elt, &resp->bodystructure.bs); +} struct fill_env { - struct mu_imap_fetch_envelope *envelope; + struct mu_imapenvelope *imapenvelope; size_t n; }; @@ -435,7 +857,8 @@ _fill_response (void *item, void *data) int rc; struct imap_list_element *elt = item; struct fill_env *env = data; - + struct mu_imapenvelope *imapenvelope = env->imapenvelope; + switch (env->n++) { case env_date: @@ -445,8 +868,8 @@ _fill_response (void *item, void *data) { if (mu_scan_datetime (elt->v.string, MU_DATETIME_SCAN_RFC822, - &env->envelope->date, - &env->envelope->tz, NULL)) + &imapenvelope->date, + &imapenvelope->tz, NULL)) rc = MU_ERR_FAILURE; else rc = 0; @@ -454,58 +877,67 @@ _fill_response (void *item, void *data) break; case env_subject: - rc = elt_to_string (elt, &env->envelope->subject); + rc = elt_to_string (elt, &imapenvelope->subject); break; case env_from: - rc = elt_to_address (elt, &env->envelope->from); + rc = elt_to_address (elt, &imapenvelope->from); break; case env_sender: - rc = elt_to_address (elt, &env->envelope->sender); + rc = elt_to_address (elt, &imapenvelope->sender); break; case env_reply_to: - rc = elt_to_address (elt, &env->envelope->reply_to); + rc = elt_to_address (elt, &imapenvelope->reply_to); break; case env_to: - rc = elt_to_address (elt, &env->envelope->to); + rc = elt_to_address (elt, &imapenvelope->to); break; case env_cc: - rc = elt_to_address (elt, &env->envelope->cc); + rc = elt_to_address (elt, &imapenvelope->cc); break; case env_bcc: - rc = elt_to_address (elt, &env->envelope->bcc); + rc = elt_to_address (elt, &imapenvelope->bcc); break; case env_in_reply_to: - rc = elt_to_string (elt, &env->envelope->in_reply_to); + rc = elt_to_string (elt, &imapenvelope->in_reply_to); break; case env_message_id: - rc = elt_to_string (elt, &env->envelope->message_id); + rc = elt_to_string (elt, &imapenvelope->message_id); break; } return rc; } - + static int -_envelope_mapper (union mu_imap_fetch_response *resp, - struct imap_list_element *elt, - struct parse_response_env *parse_env) +parse_envelope (struct imap_list_element *elt, struct mu_imapenvelope **penv) { struct fill_env env; if (elt->type != imap_eltype_list) return MU_ERR_FAILURE; - env.envelope = &resp->envelope; + env.imapenvelope = calloc (1, sizeof (*env.imapenvelope)); + if (!env.imapenvelope) + return ENOMEM; env.n = 0; mu_list_foreach (elt->v.list, _fill_response, &env); + *penv = env.imapenvelope; return 0; } + +static int +_envelope_mapper (union mu_imap_fetch_response *resp, + struct imap_list_element *elt, + struct parse_response_env *parse_env) +{ + return parse_envelope (elt, &resp->envelope.imapenvelope); +} struct mapper_tab { @@ -517,7 +949,7 @@ struct mapper_tab static struct mapper_tab mapper_tab[] = { #define S(s) s, (sizeof (s) - 1) - { S("BODYSTRUCTURE"), }, + { S("BODYSTRUCTURE"), MU_IMAP_FETCH_BODYSTRUCTURE, _bodystructure_mapper }, { S("BODY"), MU_IMAP_FETCH_BODY, _body_mapper }, { S("ENVELOPE"), MU_IMAP_FETCH_ENVELOPE, _envelope_mapper }, { S("FLAGS"), MU_IMAP_FETCH_FLAGS, _flags_mapper }, |