diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 13 | ||||
-rw-r--r-- | archive.c | 137 |
3 files changed, 152 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1edae8a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +bubger diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cd57ae9 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +CFLAGS += -std=c11 -Wall -Wextra -Wpedantic +LDLIBS = -ltls + +OBJS += archive.o +OBJS += imap.o + +bubger: ${OBJS} + ${CC} ${LDFLAGS} ${OBJS} ${LDLIBS} -o $@ + +${OBJS}: imap.h + +clean: + rm -f bubger ${OBJS} diff --git a/archive.c b/archive.c new file mode 100644 index 0000000..4facd57 --- /dev/null +++ b/archive.c @@ -0,0 +1,137 @@ +/* Copyright (C) 2020 C. McEnroe <june@causal.agency> + * + * 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 <https://www.gnu.org/licenses/>. + */ + +#include <err.h> +#include <inttypes.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <sysexits.h> +#include <unistd.h> + +#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); +} |