/* Copyright (C) 2020 C. McEnroe * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * 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, see . * * Additional permission under GNU GPL version 3 section 7: * * If you modify this Program, or any covered work, by linking or * combining it with OpenSSL (or a modified version of that library), * containing parts covered by the terms of the OpenSSL License and the * original SSLeay license, the licensors of this Program grant you * additional permission to convey the resulting work. Corresponding * Source for a non-source form of such a combination shall include the * source code for the parts of OpenSSL used as well as that of the * covered work. */ #include #include #include #include #include #include #include "archive.h" #include "imap.h" static struct Address parseAddress(struct List list) { if (list.len < 4) { errx(EX_PROTOCOL, "missing address structure fields"); } struct Address addr = {0}; if (list.ptr[0].type == String) { addr.name = decodeHeader(list.ptr[0].string); } if (list.ptr[2].type == String) addr.mailbox = list.ptr[2].string; if (list.ptr[3].type == String) addr.host = list.ptr[3].string; return addr; } static struct AddressList parseAddressList(struct List list) { struct Address *addrs = calloc(list.len, sizeof(*addrs)); if (!addrs) err(EX_OSERR, "calloc"); for (size_t i = 0; i < list.len; ++i) { addrs[i] = parseAddress(dataCheck(list.ptr[i], List).list); } return (struct AddressList) { list.len, addrs }; } static char *parseID(char *id) { while (isspace(id[0])) id++; size_t len = strlen(id); if (id[0] != '<' || !len || id[len - 1] != '>') { errx(EX_PROTOCOL, "invalid message ID"); } id[len - 1] = '\0'; return &id[1]; } void parseEnvelope(struct Envelope *envelope, struct List list) { enum { Date, Subject, From, Sender, ReplyTo, To, Cc, Bcc, InReplyTo, MessageID, EnvelopeLen, }; if (list.len < EnvelopeLen) { errx(EX_PROTOCOL, "missing envelope structure fields"); } struct tm time; const char *date = dataCheck(list.ptr[Date], String).string; envelope->date = date; if (isalpha(date[0])) { date = strptime(date, "%a, %e %b %Y %H:%M:%S ", &time); } else { date = strptime(date, "%e %b %Y %H:%M:%S ", &time); } if (date && (date[0] == '+' || date[0] == '-')) { date = strptime(date, "%z", &time); } else if (date) { date = strptime(date, "%Z", &time); } if (!date) errx(EX_PROTOCOL, "invalid envelope date format"); envelope->time = mktime(&time); envelope->subject = decodeHeader( dataCheck(list.ptr[Subject], String).string ); for (size_t i = From; i <= Bcc; ++i) { if (list.ptr[i].type == List) continue; if (list.ptr[i].type == Atom && list.ptr[i].atom == AtomNil) { list.ptr[i].type = List; list.ptr[i].list = (struct List) {0}; continue; } errx(EX_PROTOCOL, "invalid envelope address field"); } for (size_t i = From; i <= ReplyTo; ++i) { if (!list.ptr[i].list.len || list.ptr[i].list.ptr[0].type != List) { errx(EX_PROTOCOL, "invalid envelope address field"); } } envelope->from = parseAddress(list.ptr[From].list.ptr[0].list); envelope->sender = parseAddress(list.ptr[Sender].list.ptr[0].list); envelope->replyTo = parseAddress(list.ptr[ReplyTo].list.ptr[0].list); envelope->to = parseAddressList(list.ptr[To].list); envelope->cc = parseAddressList(list.ptr[Cc].list); envelope->bcc = parseAddressList(list.ptr[Bcc].list); if (list.ptr[InReplyTo].type == String) { envelope->inReplyTo = parseID(list.ptr[InReplyTo].string); } envelope->messageID = parseID(dataCheck(list.ptr[MessageID], String).string); } static void parseDisposition(struct BodyPart *part, struct List list) { if (list.len < 2) errx(EX_PROTOCOL, "missing disposition fields"); part->disposition.type = dataCheck(list.ptr[0], String).string; if (list.ptr[1].type == List) { part->disposition.params = list.ptr[1].list; } } static void parseNonMultipart(struct BodyPart *part, struct List list) { enum { Type, Subtype, Params, ContentID, Description, Encoding, Size, BasicLen }; if (list.len < BasicLen) errx(EX_PROTOCOL, "missing body part fields"); part->multipart = false; part->type = dataCheck(list.ptr[Type], String).string; part->subtype = dataCheck(list.ptr[Subtype], String).string; if (list.ptr[Params].type == List) { part->params = list.ptr[Params].list; } if (list.ptr[ContentID].type == String) { part->contentID = list.ptr[ContentID].string; } if (list.ptr[Description].type == String) { part->description = list.ptr[Description].string; } part->encoding = dataCheck(list.ptr[Encoding], String).string; part->size = dataCheck(list.ptr[Size], Number).number; list.len -= BasicLen; list.ptr += BasicLen; if (bodyPartType(part, "message", "rfc822")) { enum { Envelope, BodyStructure, Lines, MessageLen }; if (list.len < MessageLen) { errx(EX_PROTOCOL, "missing body part message fields"); } part->message.envelope = calloc(1, sizeof(*part->message.envelope)); part->message.structure = calloc(1, sizeof(*part->message.structure)); if (!part->message.envelope || !part->message.structure) { err(EX_OSERR, "calloc"); } parseEnvelope( part->message.envelope, dataCheck(list.ptr[Envelope], List).list ); parseBodyPart( part->message.structure, dataCheck(list.ptr[BodyStructure], List).list ); part->message.lines = dataCheck(list.ptr[Lines], Number).number; list.len -= MessageLen; list.ptr += MessageLen; } if (!strcasecmp(part->type, "text")) { if (!list.len) errx(EX_PROTOCOL, "missing body part text lines"); part->text.lines = dataCheck(list.ptr[0], Number).number; list.len--; list.ptr++; } enum { MD5, Disposition, Language, Location }; if (MD5 < list.len && list.ptr[MD5].type == String) { part->md5 = list.ptr[MD5].string; } if (Disposition < list.len && list.ptr[Disposition].type == List) { parseDisposition(part, list.ptr[Disposition].list); } if (Language < list.len) { part->language = list.ptr[Language]; } if (Location < list.len && list.ptr[Location].type == List) { part->location = list.ptr[Location].list; } } static void parseMultipart(struct BodyPart *part, struct List list) { part->multipart = true; for ( part->parts.len = 0; part->parts.len < list.len && list.ptr[part->parts.len].type == List; part->parts.len++ ); part->parts.ptr = calloc(part->parts.len, sizeof(*part->parts.ptr)); if (!part->parts.ptr) err(EX_OSERR, "calloc"); for (size_t i = 0; i < part->parts.len; ++i) { parseBodyPart(&part->parts.ptr[i], list.ptr[i].list); } list.len -= part->parts.len; list.ptr += part->parts.len; if (!list.len) errx(EX_PROTOCOL, "missing multipart subtype"); part->subtype = dataCheck(list.ptr[0], String).string; list.len--; list.ptr++; enum { Params, Disposition, Language, Location }; if (Params < list.len && list.ptr[Params].type == List) { part->params = list.ptr[Params].list; } if (Disposition < list.len && list.ptr[Disposition].type == List) { parseDisposition(part, list.ptr[Disposition].list); } if (Language < list.len) { part->language = list.ptr[Language]; } if (Location < list.len && list.ptr[Location].type == List) { part->location = list.ptr[Location].list; } } void parseBodyPart(struct BodyPart *part, struct List list) { if (!list.len) errx(EX_PROTOCOL, "empty body part"); if (list.ptr[0].type != List) { parseNonMultipart(part, list); } else { parseMultipart(part, list); } }