aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorC. McEnroe <june@causal.agency>2020-04-08 19:08:57 -0400
committerC. McEnroe <june@causal.agency>2020-04-08 19:08:57 -0400
commit1e24334b33092aa6303e7f3ae3957a0c7fc2240c (patch)
tree163206c0023e5cf4c4fc96fc4be784af5aa3bc60
parentCall fopencookie with a+ (diff)
downloadnotemap-1e24334b33092aa6303e7f3ae3957a0c7fc2240c.tar.gz
notemap-1e24334b33092aa6303e7f3ae3957a0c7fc2240c.zip
Use a real IMAP parser
-rw-r--r--.gitignore1
-rw-r--r--Makefile10
-rw-r--r--imap.c222
-rw-r--r--imap.h130
-rw-r--r--notemap.c304
5 files changed, 474 insertions, 193 deletions
diff --git a/.gitignore b/.gitignore
index d0b849a..7dbdb7e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
+*.o
config.mk
notemap
diff --git a/Makefile b/Makefile
index 3356cf7..caaa9ac 100644
--- a/Makefile
+++ b/Makefile
@@ -9,10 +9,16 @@ LDLIBS = -lcrypto -ltls
-include config.mk
-notemap:
+OBJS += imap.o
+OBJS += notemap.o
+
+notemap: ${OBJS}
+ ${CC} ${LDFLAGS} ${OBJS} ${LDLIBS} -o $@
+
+${OBJS}: compat.h imap.h
clean:
- rm -f notemap
+ rm -f notemap ${OBJS}
install: notemap notemap.1
install -d ${PREFIX}/bin ${MANDIR}/man1
diff --git a/imap.c b/imap.c
new file mode 100644
index 0000000..6894319
--- /dev/null
+++ b/imap.c
@@ -0,0 +1,222 @@
+/* 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 "compat.h"
+
+#include <err.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <tls.h>
+
+#include "imap.h"
+
+const char *Atoms[AtomCap] = {
+#define X(id, str) [id] = str,
+ ENUM_ATOM
+#undef X
+};
+
+bool imapVerbose;
+
+static int imapRead(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 (imapVerbose) fprintf(stderr, "%.*s", (int)ret, ptr);
+ return ret;
+}
+
+static int imapWrite(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 (imapVerbose) fprintf(stderr, "%.*s", (int)ret, ptr);
+ return ret;
+}
+
+static int imapClose(void *_tls) {
+ struct tls *tls = _tls;
+ int error = tls_close(tls);
+ if (error) errx(EX_IOERR, "tls_close: %s", tls_error(tls));
+ return error;
+}
+
+FILE *imapOpen(const char *host, const char *port) {
+ 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, imapRead, imapWrite, NULL, imapClose);
+ if (!imap) err(EX_SOFTWARE, "funopen");
+
+ setlinebuf(imap);
+ return imap;
+}
+
+static size_t cap;
+static char *buf;
+static char *ptr;
+
+static void imapLine(FILE *imap) {
+ ssize_t len = getline(&buf, &cap, imap);
+ if (len < 0) errx(EX_PROTOCOL, "unexpected eof");
+ if (len < 1 || buf[len - 1] != '\n') errx(EX_PROTOCOL, "missing LF");
+ if (len < 2 || buf[len - 2] != '\r') errx(EX_PROTOCOL, "missing CR");
+ buf[len - 2] = '\0';
+ ptr = buf;
+}
+
+static struct Data parseAtom(void) {
+ size_t len = strcspn(ptr, " ()[]{\"");
+ struct Data data = {
+ .type = Atom,
+ .atom = atomn(ptr, len),
+ };
+ ptr += len;
+ return data;
+}
+
+static struct Data parseNumber(void) {
+ return (struct Data) {
+ .type = Number,
+ .number = strtoull(ptr, &ptr, 10),
+ };
+}
+
+static struct Data parseQuoted(void) {
+ ptr++;
+ size_t len = strcspn(ptr, "\"");
+ if (ptr[len] != '"') errx(EX_PROTOCOL, "missing quoted string delimiter");
+ struct Data data = {
+ .type = String,
+ .string = strndup(ptr, len),
+ };
+ if (!data.string) err(EX_OSERR, "strndup");
+ ptr += len + 1;
+ return data;
+}
+
+static struct Data parseLiteral(FILE *imap) {
+ ptr++;
+ size_t len = strtoull(ptr, &ptr, 10);
+ if (*ptr != '}') errx(EX_PROTOCOL, "missing literal prefix delimiter");
+ struct Data data = {
+ .type = String,
+ .string = malloc(len + 1),
+ };
+ if (!data.string) err(EX_OSERR, "malloc");
+ size_t n = fread(data.string, len, 1, imap);
+ if (!n) errx(EX_PROTOCOL, "truncated literal");
+ imapLine(imap);
+ data.string[len] = '\0';
+ return data;
+}
+
+static struct Data parseData(FILE *imap);
+
+static struct Data parseList(FILE *imap, char close) {
+ if (*ptr) ptr++;
+ struct Data data = { .type = List };
+ while (*ptr != close) {
+ if (data.list.len == data.list.cap) {
+ if (data.list.cap) {
+ data.list.cap *= 2;
+ } else {
+ data.list.cap = 4;
+ }
+ data.list.ptr = realloc(
+ data.list.ptr, sizeof(*data.list.ptr) * data.list.cap
+ );
+ if (!data.list.ptr) err(EX_OSERR, "realloc");
+ }
+ data.list.ptr[data.list.len++] = parseData(imap);
+ }
+ if (*ptr) ptr++;
+ return data;
+}
+
+static struct Data parseData(FILE *imap) {
+ if (*ptr == ' ') ptr++;
+ if (*ptr == '"') return parseQuoted();
+ if (*ptr == '{') return parseLiteral(imap);
+ if (*ptr == '(') return parseList(imap, ')');
+ if (*ptr == '[') return parseList(imap, ']');
+ if (*ptr >= '0' && *ptr <= '9') return parseNumber();
+ if (*ptr) return parseAtom();
+ errx(EX_PROTOCOL, "unexpected eof");
+}
+
+struct Resp imapResp(FILE *imap) {
+ struct Data data;
+ struct Resp resp = {0};
+ imapLine(imap);
+
+ data = parseData(imap);
+ if (data.type != Atom) errx(EX_PROTOCOL, "expected tag atom");
+ resp.tag = data.atom;
+ if (resp.tag == AtomContinue) {
+ if (*ptr == ' ') ptr++;
+ resp.text = ptr;
+ return resp;
+ }
+
+ data = parseData(imap);
+ if (data.type == Number) {
+ resp.number = data.number;
+ data = parseData(imap);
+ }
+ if (data.type != Atom) errx(EX_PROTOCOL, "expected response atom");
+ resp.resp = data.atom;
+
+ if (
+ resp.resp == AtomOk ||
+ resp.resp == AtomNo ||
+ resp.resp == AtomBad ||
+ resp.resp == AtomPreauth ||
+ resp.resp == AtomBye
+ ) {
+ if (*ptr == ' ') ptr++;
+ if (*ptr == '[') {
+ data = parseList(imap, ']');
+ resp.code = data.list;
+ }
+ if (*ptr == ' ') ptr++;
+ resp.text = ptr;
+ } else {
+ data = parseList(imap, '\0');
+ resp.data = data.list;
+ }
+
+ return resp;
+}
diff --git a/imap.h b/imap.h
new file mode 100644
index 0000000..04cf5a0
--- /dev/null
+++ b/imap.h
@@ -0,0 +1,130 @@
+/* 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 <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#define ENUM_ATOM \
+ X(AtomNil, "NIL") \
+ X(AtomOk, "OK") \
+ X(AtomNo, "NO") \
+ X(AtomBad, "BAD") \
+ X(AtomPreauth, "PREAUTH") \
+ X(AtomBye, "BYE") \
+ X(AtomAlert, "ALERT") \
+ X(AtomBadCharset, "BADCHARSET") \
+ X(AtomCapability, "CAPABILITY") \
+ X(AtomParse, "PARSE") \
+ X(AtomPermanentFlags, "PERMANENTFLAGS") \
+ X(AtomReadOnly, "READ-ONLY") \
+ X(AtomReadWrite, "READ-WRITE") \
+ X(AtomTryCreate, "TRYCREATE") \
+ X(AtomUIDNext, "UIDNEXT") \
+ X(AtomUIDValidity, "UIDVALIDITY") \
+ X(AtomUnseen, "UNSEEN") \
+ X(AtomList, "LIST") \
+ X(AtomLSub, "LSUB") \
+ X(AtomStatus, "STATUS") \
+ X(AtomSearch, "SEARCH") \
+ X(AtomFlags, "FLAGS") \
+ X(AtomExists, "EXISTS") \
+ X(AtomRecent, "RECENT") \
+ X(AtomExpunge, "EXPUNGE") \
+ X(AtomFetch, "FETCH") \
+ X(AtomUntagged, "*") \
+ X(AtomContinue, "+")
+
+enum Atom {
+#define X(id, str) id,
+ ENUM_ATOM
+#undef X
+ AtomCap = 1024,
+};
+
+extern const char *Atoms[AtomCap];
+
+static inline enum Atom atomn(const char *str, size_t len) {
+ enum Atom i;
+ for (i = 0; i < AtomCap; ++i) {
+ if (!Atoms[i]) break;
+ if (strlen(Atoms[i]) != len) continue;
+ if (!strncasecmp(Atoms[i], str, len)) return i;
+ }
+ if (i == AtomCap) errx(EX_SOFTWARE, "atom capacity exceeded");
+ Atoms[i] = strndup(str, len);
+ if (!Atoms[i]) err(EX_OSERR, "strndup");
+ return i;
+}
+
+static inline enum Atom atom(const char *str) {
+ return atomn(str, strlen(str));
+}
+
+struct Data {
+ enum Type {
+ Atom,
+ Number,
+ String,
+ List,
+ } type;
+ union {
+ enum Atom atom;
+ uint32_t number;
+ char *string;
+ struct List {
+ size_t cap;
+ size_t len;
+ struct Data *ptr;
+ } list;
+ };
+};
+
+static inline void dataFree(struct Data data) {
+ if (data.type == String) free(data.string);
+ if (data.type == List) {
+ for (size_t i = 0; i < data.list.len; ++i) {
+ dataFree(data.list.ptr[i]);
+ }
+ free(data.list.ptr);
+ }
+}
+
+struct Resp {
+ enum Atom tag;
+ uint32_t number;
+ enum Atom resp;
+ struct List code;
+ struct List data;
+ const char *text;
+};
+
+static inline void respFree(struct Resp resp) {
+ for (size_t i = 0; i < resp.code.len; ++i) {
+ dataFree(resp.code.ptr[i]);
+ }
+ for (size_t i = 0; i < resp.data.len; ++i) {
+ dataFree(resp.data.ptr[i]);
+ }
+}
+
+extern bool imapVerbose;
+FILE *imapOpen(const char *host, const char *port);
+struct Resp imapResp(FILE *imap);
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 <ctype.h>
#include <err.h>
+#include <inttypes.h>
#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -26,13 +27,14 @@
#include <sys/wait.h>
#include <sysexits.h>
#include <time.h>
-#include <tls.h>
#include <unistd.h>
#ifndef NO_READPASSPHRASE_H
#include <readpassphrase.h>
#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(&note, " ");
- 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(&note, " ");
+ 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;