/* 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 "imap.h" #define ENV_PASSWORD "BUBGER_IMAP_PASSWORD" static void checkValidity(uint32_t validity) { FILE *file = fopen("UIDVALIDITY", "r"); if (file) { uint32_t previous; int n = fscanf(file, "%" SCNu32, &previous); if (n < 1) errx(EX_DATAERR, "invalid UIDVALIDITY file"); if (validity != previous) { errx(EX_TEMPFAIL, "UIDVALIDITY changed; fresh export required"); } } else { FILE *file = fopen("UIDVALIDITY", "w"); if (!file) err(EX_CANTCREAT, "UIDVALIDITY"); int n = fprintf(file, "%" PRIu32 "\n", validity); if (n < 0) err(EX_IOERR, "UIDVALIDITY"); int error = fclose(file); if (error) err(EX_IOERR, "UIDVALIDITY"); } } int main(int argc, char *argv[]) { const char *host = NULL; const char *port = "imaps"; const char *user = NULL; const char *passPath = NULL; const char *mailbox = "Archive"; const char *algo = "REFERENCES"; const char *search = "ALL"; const char *title = NULL; const char *headPath = NULL; for (int opt; 0 < (opt = getopt(argc, argv, "C:a:h:p:s:t:vw:"));) { switch (opt) { break; case 'C': { int error = chdir(optarg); if (error) err(EX_NOINPUT, "%s", optarg); } break; case 'a': algo = optarg; break; case 'h': headPath = optarg; break; case 'p': port = optarg; break; case 's': search = optarg; break; case 't': title = optarg; break; case 'v': imapVerbose = true; break; case 'w': passPath = optarg; } } if (optind < argc) host = argv[optind++]; if (optind < argc) user = argv[optind++]; if (optind < argc) mailbox = argv[optind++]; if (!host) errx(EX_USAGE, "host required"); if (!user) errx(EX_USAGE, "user required"); if (!title) title = mailbox; char *pass = NULL; if (passPath) { FILE *file = fopen(passPath, "r"); if (!file) err(EX_NOINPUT, "%s", passPath); size_t cap = 0; ssize_t len = getline(&pass, &cap, file); if (len < 0) err(EX_IOERR, "%s", passPath); if (len && pass[len - 1] == '\n') pass[len - 1] = '\0'; fclose(file); } else { pass = getenv(ENV_PASSWORD); if (!pass) errx(EX_CONFIG, ENV_PASSWORD " unset"); } enum Atom login = 0; enum Atom examine = atom("examine"); FILE *imap = imapOpen(host, port); for (struct Resp resp; resp = imapResp(imap), resp.resp != AtomBye;) { if (resp.resp == AtomNo || resp.resp == AtomBad) { errx(EX_CONFIG, "%s %s", Atoms[resp.resp], resp.text); } if (!login) { login = atom("login"); fprintf( imap, "%s LOGIN \"%s\" \"%s\"\r\n", Atoms[login], user, pass ); } if (resp.tag == login) { fprintf(imap, "%s EXAMINE \"%s\"\r\n", Atoms[examine], mailbox); } if ( resp.resp == AtomOk && resp.code.len > 1 && resp.code.ptr[0].type == Atom && resp.code.ptr[0].atom == AtomUIDValidity ) { if (resp.code.ptr[1].type != Number) { errx(EX_PROTOCOL, "invalid UIDVALIDITY"); } checkValidity(resp.code.ptr[1].number); } } fclose(imap); }