/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 2011-2021 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #include #include #include #include #include #include int mu_imap_fetch (mu_imap_t imap, int uid, mu_msgset_t msgset, const char *items) { char const *argv[3]; int i; static struct imap_command com; i = 0; if (uid) argv[i++] = "UID"; argv[i++] = "FETCH"; argv[i++] = "\\"; com.session_state = MU_IMAP_SESSION_SELECTED; com.capa = NULL; com.rx_state = MU_IMAP_CLIENT_FETCH_RX; com.argc = i; com.argv = argv; com.extra = items; com.msgset = msgset; com.tagged_handler = NULL; com.untagged_handler = NULL; return mu_imap_gencom (imap, &com); } static void _free_fetch_response (void *ptr) { union mu_imap_fetch_response *resp = ptr; switch (resp->type) { case MU_IMAP_FETCH_BODY: free (resp->body.partv); free (resp->body.section); mu_list_destroy (&resp->body.fields); free (resp->body.text); break; case MU_IMAP_FETCH_BODYSTRUCTURE: mu_bodystructure_free (resp->bodystructure.bs); break; case MU_IMAP_FETCH_ENVELOPE: mu_message_imapenvelope_free (resp->envelope.imapenvelope); break; case MU_IMAP_FETCH_FLAGS: case MU_IMAP_FETCH_INTERNALDATE: case MU_IMAP_FETCH_RFC822_SIZE: case MU_IMAP_FETCH_UID: break; } free (resp); } static int alloc_response (union mu_imap_fetch_response **resp, int type) { static size_t sizetab[] = { sizeof (struct mu_imap_fetch_body), sizeof (struct mu_imap_fetch_bodystructure), sizeof (struct mu_imap_fetch_envelope), sizeof (struct mu_imap_fetch_flags), sizeof (struct mu_imap_fetch_internaldate), sizeof (struct mu_imap_fetch_rfc822_size), sizeof (struct mu_imap_fetch_uid) }; union mu_imap_fetch_response *p; if (type < 0 || type >= MU_ARRAY_SIZE (sizetab)) return EINVAL; p = calloc (1, sizetab[type]); if (!p) return ENOMEM; p->type = type; *resp = p; return 0; } enum parse_response_state { resp_kw, resp_val, resp_body, resp_body_section, resp_skip, resp_body_hlist, resp_body_end }; struct parse_response_env; typedef int (*mapper_fn) (union mu_imap_fetch_response *resp, struct imap_list_element *elt, struct parse_response_env *env); struct parse_response_env { mu_list_t result; struct imap_list_element *elt; enum parse_response_state state; union mu_imap_fetch_response *resp; mapper_fn mapper; const char *section; mu_list_t hlist; int status; }; static int _uid_mapper (union mu_imap_fetch_response *resp, struct imap_list_element *elt, struct parse_response_env *parse_env) { char *p; size_t uid; if (elt->type != imap_eltype_string) return MU_ERR_FAILURE; uid = strtoul (elt->v.string, &p, 0); if (*p) return MU_ERR_FAILURE; resp->uid.uid = uid; return 0; } static int _size_mapper (union mu_imap_fetch_response *resp, struct imap_list_element *elt, struct parse_response_env *parse_env) { char *p; size_t size; if (elt->type != imap_eltype_string) return MU_ERR_FAILURE; size = strtoul (elt->v.string, &p, 0); if (*p) return MU_ERR_FAILURE; resp->rfc822_size.size = size; return 0; } static int _body_mapper (union mu_imap_fetch_response *resp, struct imap_list_element *elt, struct parse_response_env *parse_env) { const char *section, *p; size_t partc = 0; size_t *partv = NULL; if (elt->type != imap_eltype_string) return MU_ERR_FAILURE; section = parse_env->section; if (section) { p = section; while (mu_isdigit (*p)) { partc++; p = strchr (p, '.'); if (p) { p++; continue; } break; } } else p = NULL; if (p) { resp->body.section = strdup (p); if (!resp->body.section) { free (resp); return ENOMEM; } } if (partc) { size_t i; partv = calloc (partc, sizeof (partv[0])); for (i = 0, p = section; i < partc; i++) { char *q; partv[i] = strtoul (p, &q, 10); p = q + 1; } } resp->body.partc = partc; resp->body.partv = partv; resp->body.fields = parse_env->hlist; parse_env->hlist = NULL; resp->body.text = strdup (elt->v.string); if (!resp->body.text) { free (resp->body.section); free (resp->body.partv); free (resp); return ENOMEM; } return 0; } static int _rfc822_mapper (union mu_imap_fetch_response *resp, struct imap_list_element *elt, struct parse_response_env *parse_env) { if (elt->type != imap_eltype_string) return MU_ERR_FAILURE; resp->body.partc = 0; resp->body.partv = NULL; resp->body.section = strdup (parse_env->section); if (!resp->body.section) { free (resp); return ENOMEM; } resp->body.text = strdup (elt->v.string); if (!resp->body.text) { free (resp->body.section); free (resp->body.partv); free (resp); return ENOMEM; } return 0; } static int _rfc822_header_mapper (union mu_imap_fetch_response *resp, struct imap_list_element *elt, struct parse_response_env *parse_env) { parse_env->section = "HEADER"; return _rfc822_mapper (resp, elt, parse_env); } static int _rfc822_text_mapper (union mu_imap_fetch_response *resp, struct imap_list_element *elt, struct parse_response_env *parse_env) { parse_env->section = "TEXT"; return _rfc822_mapper (resp, elt, parse_env); } static int _flags_mapper (union mu_imap_fetch_response *resp, struct imap_list_element *elt, struct parse_response_env *parse_env) { if (elt->type != imap_eltype_list) return MU_ERR_FAILURE; if (_mu_imap_collect_flags (elt, &resp->flags.flags)) return MU_ERR_FAILURE; return 0; } static int _date_mapper (union mu_imap_fetch_response *resp, struct imap_list_element *elt, struct parse_response_env *parse_env) { struct tm tm; struct mu_timezone tz; if (elt->type != imap_eltype_string) return MU_ERR_FAILURE; if (mu_scan_datetime (elt->v.string, MU_DATETIME_INTERNALDATE, &tm, &tz, NULL)) return MU_ERR_FAILURE; mu_datetime_tz_utc (&tz); resp->internaldate.tm = tm; resp->internaldate.tz = tz; return 0; } 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 = malloc (sizeof (*param)); if (!param) return ENOMEM; param->lang = param->cset = NULL; param->value = strdup (val->v.string); if (!param->value) { free (param); return ENOMEM; } } return mu_assoc_install (assoc, key->v.string, param); } 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 < 7) 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_imapenvelope *imapenvelope; size_t n; }; enum env_index { env_date, env_subject, env_from, env_sender, env_reply_to, env_to, env_cc, env_bcc, env_in_reply_to, env_message_id }; static int elt_to_string (struct imap_list_element *elt, char **pstr) { char *p; if (_mu_imap_list_element_is_nil (elt)) p = NULL; else if (elt->type != imap_eltype_string) return EINVAL; else { p = strdup (elt->v.string); if (!p) return ENOMEM; } *pstr = p; return 0; } struct addr_env { mu_address_t addr; size_t n; }; static int _fill_subaddr (void *item, void *data) { struct addr_env *addr_env = data; struct imap_list_element *elt = item, *arg; const char *domain = NULL, *local = NULL, *personal = NULL; if (elt->type != imap_eltype_list) return 0; arg = _mu_imap_list_at (elt->v.list, 0); if (arg && arg->type == imap_eltype_string) personal = arg->v.string; arg = _mu_imap_list_at (elt->v.list, 2); if (arg && arg->type == imap_eltype_string) local = arg->v.string; arg = _mu_imap_list_at (elt->v.list, 3); if (arg && arg->type == imap_eltype_string) domain = arg->v.string; if (domain && local) { if (!addr_env->addr) { int rc = mu_address_create_null (&addr_env->addr); if (rc) return rc; } mu_address_set_local_part (addr_env->addr, addr_env->n, local); mu_address_set_domain (addr_env->addr, addr_env->n, domain); mu_address_set_personal (addr_env->addr, addr_env->n, personal); addr_env->n++; } return 0; } static int elt_to_address (struct imap_list_element *elt, mu_address_t *paddr) { if (_mu_imap_list_element_is_nil (elt)) *paddr = NULL; else if (elt->type != imap_eltype_list) return EINVAL; else { struct addr_env addr_env; addr_env.addr = NULL; addr_env.n = 1; mu_list_foreach (elt->v.list, _fill_subaddr, &addr_env); *paddr = addr_env.addr; } return 0; } static int _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: if (elt->type == imap_eltype_string) { if (mu_scan_datetime (elt->v.string, MU_DATETIME_SCAN_RFC822, &imapenvelope->date, &imapenvelope->tz, NULL)) rc = MU_ERR_FAILURE; else rc = 0; } else if (_mu_imap_list_element_is_nil (elt)) rc = 0; else rc = MU_ERR_FAILURE; break; case env_subject: rc = elt_to_string (elt, &imapenvelope->subject); break; case env_from: rc = elt_to_address (elt, &imapenvelope->from); break; case env_sender: rc = elt_to_address (elt, &imapenvelope->sender); break; case env_reply_to: rc = elt_to_address (elt, &imapenvelope->reply_to); break; case env_to: rc = elt_to_address (elt, &imapenvelope->to); break; case env_cc: rc = elt_to_address (elt, &imapenvelope->cc); break; case env_bcc: rc = elt_to_address (elt, &imapenvelope->bcc); break; case env_in_reply_to: rc = elt_to_string (elt, &imapenvelope->in_reply_to); break; case env_message_id: rc = elt_to_string (elt, &imapenvelope->message_id); break; } return rc; } static int 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.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 { char *name; size_t size; int type; mapper_fn mapper; }; static struct mapper_tab mapper_tab[] = { #define S(s) s, (sizeof (s) - 1) { 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 }, { S("INTERNALDATE"), MU_IMAP_FETCH_INTERNALDATE, _date_mapper }, { S("RFC822"), MU_IMAP_FETCH_BODY, _body_mapper}, { S("RFC822.HEADER"), MU_IMAP_FETCH_BODY, _rfc822_header_mapper }, { S("RFC822.SIZE"), MU_IMAP_FETCH_RFC822_SIZE, _size_mapper }, { S("RFC822.TEXT"), MU_IMAP_FETCH_BODY, _rfc822_text_mapper }, { S("UID"), MU_IMAP_FETCH_UID, _uid_mapper }, #undef S { NULL } }; static int _extract_string (void **itmv, size_t itmc, void *call_data) { struct imap_list_element *elt = itmv[0]; if (elt->type != imap_eltype_string) return MU_LIST_MAP_SKIP; itmv[0] = elt->v.string; return 0; } static int _fetch_fold (void *item, void *data) { int rc; struct imap_list_element *elt = item; struct parse_response_env *env = data; switch (env->state) { case resp_kw: { char *kw; size_t kwlen; struct mapper_tab *mt; if (elt->type != imap_eltype_string) { env->status = MU_ERR_FAILURE; return MU_ERR_FAILURE; } kw = elt->v.string; kwlen = strlen (kw); for (mt = mapper_tab; mt->name; mt++) { if (mt->size == kwlen && memcmp (mt->name, kw, kwlen) == 0) break; } if (!mt->name) { mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_TRACE9, ("ignoring unknown FETCH item '%s'", kw)); env->state = resp_skip; return 0; } env->mapper = mt->mapper; rc = alloc_response (&env->resp, mt->type); if (rc) { env->status = rc; return MU_ERR_FAILURE; } env->state = mt->type == MU_IMAP_FETCH_BODY ? resp_body : resp_val; break; } case resp_body: if (_mu_imap_list_element_is_string (elt, "[")) { env->state = resp_body_section; break; } else { env->mapper = _bodystructure_mapper; _free_fetch_response (env->resp); rc = alloc_response (&env->resp, MU_IMAP_FETCH_BODYSTRUCTURE); if (rc) { env->status = rc; return MU_ERR_FAILURE; } env->state = resp_val; } /* fall through */ case resp_val: if (env->mapper) { int rc = env->mapper (env->resp, elt, env); if (rc) _free_fetch_response (env->resp); else mu_list_append (env->result, env->resp); } env->resp = NULL; mu_list_destroy (&env->hlist); env->state = resp_kw; break; case resp_body_section: if (elt->type != imap_eltype_string) { env->status = MU_ERR_PARSE; return MU_ERR_FAILURE; } else if (strncmp (elt->v.string, "HEADER.FIELDS", 13) == 0) env->state = resp_body_hlist; else if (strcmp (elt->v.string, "]") == 0) { env->section = NULL; env->state = resp_val; break; } else env->state = resp_body_end; env->section = elt->v.string; break; case resp_skip: mu_list_destroy (&env->hlist); env->state = resp_kw; break; case resp_body_hlist: if (elt->type != imap_eltype_list) { env->status = MU_ERR_PARSE; return MU_ERR_FAILURE; } mu_list_map (elt->v.list, _extract_string, NULL, 1, &env->hlist); env->state = resp_body_end; break; case resp_body_end: if (_mu_imap_list_element_is_string (elt, "]")) env->state = resp_val; else { env->status = MU_ERR_PARSE; return MU_ERR_FAILURE; } } return 0; } int _mu_imap_parse_fetch_response (mu_list_t input, mu_list_t *result_list) { mu_list_t result; int status; struct parse_response_env env; status = mu_list_create (&result); if (status) { mu_debug (MU_DEBCAT_MAILBOX, MU_DEBUG_ERROR, ("mu_list_create: %s", mu_strerror (status))); return 1; } mu_list_set_destroy_item (result, _free_fetch_response); memset (&env, 0, sizeof (env)); env.result = result; mu_list_foreach (input, _fetch_fold, &env); if (env.status) mu_list_destroy (&result); else *result_list = result; mu_list_destroy (&env.hlist); return env.status; }