From 1e24334b33092aa6303e7f3ae3957a0c7fc2240c Mon Sep 17 00:00:00 2001 From: "C. McEnroe" Date: Wed, 8 Apr 2020 19:08:57 -0400 Subject: Use a real IMAP parser --- notemap.c | 304 +++++++++++++++++++++++--------------------------------------- 1 file changed, 113 insertions(+), 191 deletions(-) (limited to 'notemap.c') diff --git a/notemap.c b/notemap.c index 3d5d853..dfe9872 100644 --- a/notemap.c +++ b/notemap.c @@ -16,9 +16,10 @@ #include "compat.h" -#include #include +#include #include +#include #include #include #include @@ -26,13 +27,14 @@ #include #include #include -#include #include #ifndef NO_READPASSPHRASE_H #include #endif +#include "imap.h" + #if !defined(DIG_PATH) && !defined(DRILL_PATH) # ifdef __FreeBSD__ # define DRILL_PATH "/usr/bin/drill" @@ -43,37 +45,6 @@ typedef unsigned char byte; -static bool verbose; - -int tlsRead(void *_tls, char *ptr, int len) { - struct tls *tls = _tls; - ssize_t ret; - do { - ret = tls_read(tls, ptr, len); - } while (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT); - if (ret < 0) errx(EX_IOERR, "tls_read: %s", tls_error(tls)); - if (verbose) fprintf(stderr, "%.*s", (int)ret, ptr); - return ret; -} - -int tlsWrite(void *_tls, const char *ptr, int len) { - struct tls *tls = _tls; - ssize_t ret; - do { - ret = tls_write(tls, ptr, len); - } while (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT); - if (ret < 0) errx(EX_IOERR, "tls_write: %s", tls_error(tls)); - if (verbose) fprintf(stderr, "%.*s", (int)ret, ptr); - return ret; -} - -int tlsClose(void *_tls) { - struct tls *tls = _tls; - int error = tls_close(tls); - if (error) errx(EX_IOERR, "tls_close: %s", tls_error(tls)); - return error; -} - static void lookup(const char **host, const char **port, const char *domain) { static char buf[1024]; snprintf(buf, sizeof(buf), "_imaps._tcp.%s", domain); @@ -166,46 +137,9 @@ static bool uuidCheck(const char *uuid) { return true; } -#define ENUM_ATOM \ - X(Unknown, "") \ - X(Untagged, "*") \ - X(Ok, "OK") \ - X(No, "NO") \ - X(Bad, "BAD") \ - X(Bye, "BYE") \ - X(Login, "LOGIN") \ - X(Search, "SEARCH") \ - X(Fetch, "FETCH") \ - X(Append, "APPEND") \ - X(Next, "next") - -enum Atom { -#define X(id, _) id, - ENUM_ATOM -#undef X - AtomsLen, -}; - -static const char *Atoms[AtomsLen] = { -#define X(id, str) [id] = str, - ENUM_ATOM -#undef X -}; - -static enum Atom atom(const char *str) { - if (!str) return Unknown; - for (enum Atom i = 0; i < AtomsLen; ++i) { - if (!strcmp(str, Atoms[i])) return i; - } - return Unknown; -} - #define DATE_FORMAT "%a, %e %b %Y %H:%M:%S %z" -static void append( - FILE *imap, const char *mailbox, - const char *from, const char *uuid, const char *path -) { +static char *format(const char *from, const char *uuid, const char *path) { FILE *note = fopen(path, "r"); if (!note) err(EX_NOINPUT, "%s", path); @@ -277,15 +211,7 @@ static void append( fclose(msg); buf[max - 1] = '\0'; - fprintf( - imap, "%s APPEND %s (\\Seen) {%zu}\r\n", - Atoms[Append], mailbox, strlen(buf) - ); - if (fgetc(imap) == '+') { - ungetc('+', imap); - fprintf(imap, "%s\r\n", buf); - } - free(buf); + return buf; } int main(int argc, char *argv[]) { @@ -309,7 +235,7 @@ int main(int argc, char *argv[]) { break; case 'm': path = optarg; break; case 'p': port = optarg; break; case 'u': user = optarg; - break; case 'v': verbose = true; + break; case 'v': imapVerbose = true; break; case 'w': rppFlags |= RPP_STDIN; break; default: return EX_USAGE; } @@ -346,136 +272,109 @@ int main(int argc, char *argv[]) { FILE *map = fopen(path, "r"); if (!map) err(EX_NOINPUT, "%s", path); - struct tls *client = tls_client(); - if (!client) errx(EX_SOFTWARE, "tls_client"); - - struct tls_config *config = tls_config_new(); - if (!config) errx(EX_SOFTWARE, "tls_config_new"); - - int error = tls_configure(client, config); - if (error) errx(EX_SOFTWARE, "tls_configure: %s", tls_error(client)); - tls_config_free(config); - - error = tls_connect(client, host, port); - if (error) errx(EX_NOHOST, "tls_connect: %s", tls_error(client)); - - FILE *imap = funopen(client, tlsRead, tlsWrite, NULL, tlsClose); - if (!imap) err(EX_SOFTWARE, "funopen"); - setlinebuf(imap); - - char *line = NULL; - size_t lineCap = 0; - + size_t cap = 0; char *entry = NULL; - size_t entryCap = 0; char *uuid = NULL; char *note = NULL; - int seq = 0; - - bool login = false; - while (0 < getline(&line, &lineCap, imap)) { - char *cr = strchr(line, '\r'); - if (cr) *cr = '\0'; - - char *rest = line; - enum Atom tag = atom(strsep(&rest, " ")); - if (rest && isdigit(rest[0])) { - strsep(&rest, " "); + uint32_t seq = 0; + char *message = NULL; + + enum Atom login = 0; + enum Atom next = atom("next"); + enum Atom create = atom("create"); + enum Atom replace = atom("replace"); + enum Atom envelope = atom("ENVELOPE"); + 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); } - enum Atom resp = atom(strsep(&rest, " ")); - if (resp == No || resp == Bad || resp == Bye) { - errx( - EX_CONFIG, "%s: %s %s", - Atoms[tag], Atoms[resp], (rest ? rest : "") + if (!login) { + login = atom("login"); + fprintf( + imap, "%s LOGIN \"%s\" \"%s\"\r\n", + Atoms[login], user, pass ); } - switch (tag) { - break; case Untagged: { - if (login) break; - fprintf( - imap, "%s LOGIN \"%s\" \"%s\"\r\n", - Atoms[Login], user, pass - ); - login = true; - } - - break; case Login: { - fprintf(imap, "%s SELECT \"%s\"\r\n", Atoms[Next], mailbox); - } - - break; case Next: Next: { - ssize_t len; - len = getline(&entry, &entryCap, map); - if (ferror(map)) err(EX_IOERR, "%s", path); - if (len < 1) goto done; - if (entry[len - 1] == '\n') entry[len - 1] = '\0'; - - note = entry; - uuid = strsep(¬e, " "); - if (!note || !uuid || !uuidCheck(uuid)) { - errx(EX_CONFIG, "invalid map entry: %s", entry); - } - - if (argc) { - int i; - for (i = 0; i < argc; ++i) { - if (!argv[i]) continue; - if (strcmp(argv[i], note)) continue; - argv[i] = NULL; - break; - } - if (i == argc) goto Next; - } + if (resp.tag == login) { + fprintf(imap, "%s SELECT \"%s\"\r\n", Atoms[next], mailbox); + } - fprintf( - imap, - "%s SEARCH HEADER X-Universally-Unique-Identifier %s\r\n", - Atoms[Search], uuid - ); + ssize_t len; + if (resp.tag == next) { +next: + len = getline(&entry, &cap, map); + if (ferror(map)) err(EX_IOERR, "%s", path); + if (len < 1) { + fprintf(imap, "ayy LOGOUT\r\n"); + continue; } + if (entry[len - 1] == '\n') entry[len - 1] = '\0'; - break; case Search: { - if (!seq) goto Fetch; - fprintf(imap, "%s FETCH %d ENVELOPE\r\n", Atoms[Fetch], seq); + note = entry; + uuid = strsep(¬e, " "); + if (!note || !uuid || !uuidCheck(uuid)) { + errx(EX_CONFIG, "invalid map entry: %s", entry); } - break; case Fetch: Fetch: { - append(imap, mailbox, user, uuid, note); + if (argc) { + int i; + for (i = 0; i < argc; ++i) { + if (!argv[i]) continue; + if (strcmp(argv[i], note)) continue; + argv[i] = NULL; + break; + } + if (i == argc) goto next; } - break; case Append: { - printf("%c %s\n", (seq ? '~' : '+'), note); - if (!seq) goto Next; - fprintf( - imap, "%s STORE %d +FLAGS.SILENT (\\Deleted)\r\n", - Atoms[Next], seq - ); - } + fprintf( + imap, + "%s SEARCH HEADER X-Universally-Unique-Identifier \"%s\"\r\n", + Atoms[AtomSearch], uuid + ); - break; default:; + respFree(resp); + continue; } - if (resp == Search) { - if (rest) { - seq = strtol(rest, &rest, 10); - if (*rest) { - errx(EX_CONFIG, "multiple messages matching %s", uuid); + if (resp.resp == AtomSearch) { + if (resp.data.len > 1) { + errx(EX_CONFIG, "multiple messages matching %s", uuid); + } + if (resp.data.len) { + if (resp.data.ptr[0].type != Number) { + errx(EX_PROTOCOL, "invalid search result"); } + seq = resp.data.ptr[0].number; + fprintf( + imap, "%s FETCH %" PRIu32 " ENVELOPE\r\n", + Atoms[AtomFetch], seq + ); } else { - seq = 0; + message = format(user, uuid, note); + fprintf( + imap, "%s APPEND %s (\\Seen) {%zu}\r\n", + Atoms[create], mailbox, strlen(message) + ); } } - if (resp == Fetch) { - if (strncmp(rest, "(ENVELOPE", 9)) continue; - + if ( + resp.resp == AtomFetch && + resp.data.len && + resp.data.ptr[0].type == List && + resp.data.ptr[0].list.len > 1 && + resp.data.ptr[0].list.ptr[0].type == Atom && + resp.data.ptr[0].list.ptr[0].atom == envelope && + resp.data.ptr[0].list.ptr[1].type == List + ) { + struct List envelope = resp.data.ptr[0].list.ptr[1].list; struct tm date = {0}; - rest = strptime( - rest, "(ENVELOPE (\"" DATE_FORMAT "\"", &date - ); - if (!rest) errx(EX_PROTOCOL, "invalid envelope date"); + char *rest = strptime(envelope.ptr[0].string, DATE_FORMAT, &date); + if (!rest) errx(EX_PROTOCOL, "invalid envelope date format"); struct stat status; int error = stat(note, &status); @@ -488,13 +387,36 @@ int main(int argc, char *argv[]) { note ); } else if (status.st_mtime == mktime(&date)) { - goto Next; + goto next; } + + message = format(user, uuid, note); + fprintf( + imap, "%s APPEND %s (\\Seen) {%zu}\r\n", + Atoms[replace], mailbox, strlen(message) + ); + } + + if (resp.tag == AtomContinue) { + fprintf(imap, "%s\r\n", message); + free(message); + } + + if (resp.tag == create) { + printf("+ %s\n", note); + goto next; } - } -done: - fprintf(imap, "ayy LOGOUT\r\n"); + if (resp.tag == replace) { + printf("~ %s\n", note); + fprintf( + imap, "%s STORE %" PRIu32 " +FLAGS (\\Deleted)\r\n", + Atoms[next], seq + ); + } + + respFree(resp); + } fclose(imap); int ret = EX_OK; -- cgit 1.4.1