/* 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 . */ #include #include #include #include #include #include #include #include "archive.h" #include "imap.h" bool exportFetch(FILE *imap, enum Atom tag, struct List threads) { struct List uids = {0}; listFlatten(&uids, threads); for (size_t i = uids.len - 1; i < uids.len; --i) { uint32_t uid = dataCheck(uids.ptr[i], Number).number; int error = 0 || access(pathUID(uid, "atom"), F_OK) || access(pathUID(uid, "html"), F_OK) || access(pathUID(uid, "mbox"), F_OK); if (!error) uids.ptr[i] = uids.ptr[--uids.len]; } if (!uids.len) { listFree(uids); return false; } fprintf(imap, "%s UID FETCH ", Atoms[tag]); for (size_t i = 0; i < uids.len; ++i) { fprintf(imap, "%s%" PRIu32, (i ? "," : ""), uids.ptr[i].number); } fprintf( imap, " (UID ENVELOPE BODYSTRUCTURE" " BODY[HEADER.FIELDS (" MBOX_HEADERS ")] BODY[TEXT])\r\n" ); return true; } static void exportMbox( uint32_t uid, const struct Envelope *envelope, const char *header, const char *body ) { const char *path = pathUID(uid, "mbox"); FILE *file = fopen(path, "w"); if (!file) err(EX_CANTCREAT, "%s", path); int error = 0 || mboxFrom(file) || mboxHeader(file, header) || mboxBody(file, body) || fclose(file); if (error) err(EX_IOERR, "%s", path); const char *msg = pathMessage(envelope->messageID, "mbox"); unlink(msg); error = link(path, msg); if (error) err(EX_CANTCREAT, "%s", msg); } static void exportAtom( uint32_t uid, const struct Envelope *envelope, const struct BodyPart *structure, const char *body ) { const char *path = pathUID(uid, "atom"); FILE *file = fopen(path, "w"); if (!file) err(EX_CANTCREAT, "%s", path); int error = atomEntryOpen(file, envelope); if (error) err(EX_IOERR, "%s", path); if ( !structure->multipart && !strcmp(structure->type, "TEXT") && !strcmp(structure->subtype, "PLAIN") ) { error = 0 || atomContentOpen(file) || decodeContent(file, escapeXML, structure, body) || atomContentClose(file); if (error) err(EX_IOERR, "%s", path); } error = atomEntryClose(file) || fclose(file); if (error) err(EX_IOERR, "%s", path); } static void exportFetchParts( FILE *imap, struct List *parts, const struct BodyPart *structure ) { if (structure->multipart) { for (size_t i = 0; i < structure->parts.len; ++i) { struct Data part = { .type = Number, .number = 1 + i }; listPush(parts, part); exportFetchParts(imap, parts, &structure->parts.ptr[i]); parts->len--; } } else if ( structure->message.structure && structure->message.structure->multipart ) { exportFetchParts(imap, parts, structure->message.structure); } else { fprintf(imap, " BODY["); for (size_t i = 0; i < parts->len; ++i) { fprintf(imap, "%s%" PRIu32, (i ? "." : ""), parts->ptr[i].number); } if (structure->message.structure) { fprintf(imap, ".TEXT"); } fprintf(imap, "]"); } } bool exportData(FILE *imap, enum Atom tag, struct List items) { uint32_t uid = 0; struct Envelope envelope = {0}; struct BodyPart structure = {0}; const char *bodyHeader = NULL; const char *bodyText = NULL; struct Data bodyParts = {0}; for (size_t i = 0; i + 1 < items.len; i += 2) { enum Atom name = dataCheck(items.ptr[i], Atom).atom; struct Data data = items.ptr[i + 1]; if (name == AtomUID) { uid = dataCheck(data, Number).number; } else if (name == AtomEnvelope) { parseEnvelope(&envelope, dataCheck(data, List).list); } else if (name == AtomBodyStructure) { parseBodyPart(&structure, dataCheck(data, List).list); } if (name != AtomBody) continue; struct List section = dataCheck(data, List).list; if (!section.len) { errx(EX_PROTOCOL, "missing body data item section"); } i++; if (i + 1 >= items.len) { errx(EX_PROTOCOL, "missing body data item value"); } data = items.ptr[i + 1]; if (section.ptr[0].type == Atom) { name = section.ptr[0].atom; if (name == AtomHeaderFields) { bodyHeader = dataCheck(data, String).string; } else if (name == AtomText) { bodyText = dataCheck(data, String).string; } continue; } data = dataTake(&items.ptr[i + 1]); struct Data *dest = &bodyParts; for (size_t i = 0; i < section.len; ++i) { if (section.ptr[i].type != Number) continue; uint32_t num = section.ptr[i].number; *dest = (struct Data) { .type = List }; while (dest->list.len < num) { listPush(&dest->list, (struct Data) {0}); } dest = &dest->list.ptr[num - 1]; *dest = data; } } if (!uid) { errx(EX_PROTOCOL, "missing UID data item"); } if (!structure.subtype) { errx(EX_PROTOCOL, "missing BODYSTRUCTURE data item"); } bool fetch = false; if (envelope.subject) { if (!bodyHeader) { errx(EX_PROTOCOL, "missing BODY[HEADER.FIELDS] data item"); } if (!bodyText) { errx(EX_PROTOCOL, "missing BODY[TEXT] data item"); } exportMbox(uid, &envelope, bodyHeader, bodyText); exportAtom(uid, &envelope, &structure, bodyText); if (structure.multipart) { fetch = true; fprintf( imap, "%s UID FETCH %" PRIu32 " (UID BODYSTRUCTURE", Atoms[tag], uid ); struct List parts = {0}; exportFetchParts(imap, &parts, &structure); listFree(parts); fprintf(imap, ")\r\n"); } } else { // TODO: Correlate body parts to body data. } envelopeFree(envelope); bodyPartFree(structure); dataFree(bodyParts); return fetch; }