diff options
Diffstat (limited to '')
-rw-r--r-- | bin/.gitignore | 1 | ||||
-rw-r--r-- | bin/Makefile | 4 | ||||
-rw-r--r-- | bin/imbox.c | 306 | ||||
-rw-r--r-- | bin/man1/imbox.1 | 90 |
4 files changed, 0 insertions, 401 deletions
diff --git a/bin/.gitignore b/bin/.gitignore index 20bffec1..78e94de3 100644 --- a/bin/.gitignore +++ b/bin/.gitignore @@ -13,7 +13,6 @@ fbclock glitch hi hnel -imbox modem open order diff --git a/bin/Makefile b/bin/Makefile index 70e0bb7e..749acaf7 100644 --- a/bin/Makefile +++ b/bin/Makefile @@ -51,7 +51,6 @@ BINS_LINUX += psfed BINS_CURL += title -BINS_TLS += imbox BINS_TLS += relay BINS = ${BINS_ANY} ${BINS_BSD} ${BINS_LINUX} ${BINS_CURL} ${BINS_TLS} @@ -81,9 +80,6 @@ hi: hi.c open pbcopy pbpaste: pbd ln -f pbd $@ -imbox: imbox.c - ${CC} ${CFLAGS_tls} ${LDFLAGS_tls} $@.c ${LDLIBS_tls} -o $@ - relay: relay.c ${CC} ${CFLAGS_tls} ${LDFLAGS_tls} $@.c ${LDLIBS_tls} -o $@ diff --git a/bin/imbox.c b/bin/imbox.c deleted file mode 100644 index 9f469831..00000000 --- a/bin/imbox.c +++ /dev/null @@ -1,306 +0,0 @@ -/* Copyright (C) 2019 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 <ctype.h> -#include <err.h> -#include <readpassphrase.h> -#include <regex.h> -#include <stdbool.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sysexits.h> -#include <tls.h> -#include <unistd.h> - -#define ARRAY_LEN(a) (sizeof(a) / sizeof((a)[0])) - -static void compile(regex_t *regex, const char *pattern) { - if (regex->re_nsub) return; - int error = regcomp(regex, pattern, REG_EXTENDED | REG_NEWLINE); - if (!error) return; - char buf[256]; - regerror(error, regex, buf, sizeof(buf)); - errx(EX_SOFTWARE, "regcomp: %s: %s", buf, pattern); -} - -static void mboxrd(const char *headers, const char *body) { - static regex_t fromRegex; - compile(&fromRegex, "^From: .*<([^>]+)>"); - - regmatch_t from[2]; - int error = regexec(&fromRegex, headers, 2, from, 0); - if (error) errx(EX_DATAERR, "missing From header"); - printf( - "From %.*s ", - (int)(from[1].rm_eo - from[1].rm_so), &headers[from[1].rm_so] - ); - - static regex_t dateRegex; - compile(&dateRegex, "^Date: (...), (..) (...) (....) (.{8})"); - - regmatch_t date[6]; - error = regexec(&dateRegex, headers, 6, date, 0); - if (error) errx(EX_DATAERR, "missing Date header"); - printf( - "%.*s %.*s %.*s %.*s %.*s\r\n", - (int)(date[1].rm_eo - date[1].rm_so), &headers[date[1].rm_so], - (int)(date[3].rm_eo - date[3].rm_so), &headers[date[3].rm_so], - (int)(date[2].rm_eo - date[2].rm_so), &headers[date[2].rm_so], - (int)(date[5].rm_eo - date[5].rm_so), &headers[date[5].rm_so], - (int)(date[4].rm_eo - date[4].rm_so), &headers[date[4].rm_so] - ); - - printf("%s", headers); - // FIXME: There seems to sometimes be some garbage data at the end of the - // headers? - - static regex_t quoteRegex; - compile("eRegex, "^>*From "); - regmatch_t match = {0}; - for (const char *ptr = body;; ptr += match.rm_eo) { - regmatch_t match; - error = regexec( - "eRegex, body, 1, &match, (ptr > body ? REG_NOTBOL : 0) - ); - if (error) { - printf("%s", ptr); - break; - } - printf( - "%.*s>%.*s", - (int)match.rm_so, ptr, - (int)(match.rm_eo - match.rm_so), &ptr[match.rm_so] - ); - } - - // FIXME: mbox technically shouldn't use \r\n but everything else does... - printf("\r\n"); -} - -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; -} - -#define ENUM_ATOM \ - X(Unknown, "") \ - X(Untagged, "*") \ - X(Ok, "OK") \ - X(No, "NO") \ - X(Bad, "BAD") \ - X(Bye, "BYE") \ - X(Login, "LOGIN") \ - X(Select, "SELECT") \ - X(Search, "SEARCH") \ - X(Fetch, "FETCH") - -enum Atom { -#define X(id, _) id, - ENUM_ATOM -#undef X -}; - -static const char *Atoms[] = { -#define X(id, str) [id] = str, - ENUM_ATOM -#undef X -}; - -static enum Atom atom(const char *str) { - if (!str) return Unknown; - for (size_t i = 0; i < ARRAY_LEN(Atoms); ++i) { - if (!strcmp(str, Atoms[i])) return i; - } - return Unknown; -} - -static char *readLiteral(FILE *imap, const char *line) { - char *prefix = strrchr(line, '{'); - if (!prefix) errx(EX_PROTOCOL, "no literal prefix"); - - size_t size = strtoul(prefix + 1, NULL, 10); - if (!size) errx(EX_PROTOCOL, "invalid literal size"); - - char *literal = malloc(size); - if (!literal) err(EX_OSERR, "malloc"); - - size_t count = fread(literal, size, 1, imap); - if (!count) errx(EX_PROTOCOL, "could not read literal"); - - return literal; -} - -int main(int argc, char *argv[]) { - const char *host = "imap.fastmail.com"; - const char *port = "993"; - const char *mailbox = "INBOX"; - const char *search = "SUBJECT \"[PATCH\""; - int rppFlags = 0; - - int opt; - while (0 < (opt = getopt(argc, argv, "h:m:p:s:vw"))) { - switch (opt) { - break; case 'h': host = optarg; - break; case 'm': mailbox = optarg; - break; case 'p': port = optarg; - break; case 's': search = optarg; - break; case 'v': verbose = true; - break; case 'w': rppFlags |= RPP_STDIN; - break; default: return EX_USAGE; - } - } - - const char *user = argv[optind]; - if (!user) errx(EX_USAGE, "username required"); - - char buf[1024]; - char *pass = readpassphrase( - (rppFlags & RPP_STDIN ? "" : "Password: "), - buf, sizeof(buf), rppFlags - ); - if (!pass) err(EX_UNAVAILABLE, "readpassphrase"); - - 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"); - - bool login = false; - char *uids = NULL; - - char *line = NULL; - size_t cap = 0; - while (0 < getline(&line, &cap, imap)) { - if (strchr(line, '\r')) *strchr(line, '\r') = '\0'; - - char *rest = line; - enum Atom tag = atom(strsep(&rest, " ")); - if (rest && isdigit(rest[0])) strsep(&rest, " "); - enum Atom resp = atom(strsep(&rest, " ")); - - if (resp == No || resp == Bad) { - errx( - EX_CONFIG, "%s: %s %s", - Atoms[tag], Atoms[resp], (rest ? rest : "") - ); - } else if (resp == Bye) { - errx(EX_UNAVAILABLE, "unexpected BYE: %s", (rest ? rest : "")); - } - - 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[Select], mailbox); - } - break; case Select: { - fprintf( - imap, "%s UID SEARCH CHARSET UTF-8 %s\r\n", - Atoms[Search], search - ); - } - break; case Search: { - if (!uids) errx(EX_PROTOCOL, "no search response"); - for (char *ch = uids; *ch; ++ch) { - if (*ch == ' ') *ch = ','; - } - // FIXME: Grab Content-Encoding as well? - fprintf( - imap, - "%s UID FETCH %s (" - "BODY[HEADER.FIELDS (Date From Subject Message-Id)] " - "BODY[TEXT]" - ")\r\n", - Atoms[Fetch], uids - ); - } - break; case Fetch: { - fprintf(imap, "ayy LOGOUT\r\n"); - fclose(imap); - return EX_OK; - } - break; default:; - } - - switch (resp) { - break; case Search: { - if (!rest) errx(EX_TEMPFAIL, "no messages match"); - uids = strdup(rest); - if (!uids) err(EX_OSERR, "strdup"); - } - break; case Fetch: { - char *headers = readLiteral(imap, rest); - - ssize_t len = getline(&line, &cap, imap); - if (len <= 0) errx(EX_PROTOCOL, "unexpected eof"); - - char *body = readLiteral(imap, line); - - len = getline(&line, &cap, imap); - if (len <= 0) errx(EX_PROTOCOL, "unexpected eof"); - if (strcmp(line, ")\r\n")) { - errx(EX_PROTOCOL, "trailing fetch data"); - } - - mboxrd(headers, body); - free(headers); - free(body); - } - break; default:; - } - } -} diff --git a/bin/man1/imbox.1 b/bin/man1/imbox.1 deleted file mode 100644 index 791c9968..00000000 --- a/bin/man1/imbox.1 +++ /dev/null @@ -1,90 +0,0 @@ -.Dd December 20, 2019 -.Dt IMBOX 1 -.Os -. -.Sh NAME -.Nm imbox -.Nd IMAP to mboxrd -. -.Sh SYNOPSIS -.Nm -.Op Fl vw -.Op Fl h Ar host -.Op Fl m Ar mailbox -.Op Fl p Ar port -.Op Fl s Ar search -.Ar user -. -.Sh DESCRIPTION -The -.Nm -utility exports messages from an IMAP mailbox -to the mboxrd format on standard output. -IMAP login is performed as -.Ar user -with a password read from -.Pa /dev/tty , -or standard input if -.Fl w -is used. -. -.Pp -The arguments are as follows: -.Bl -tag -width Ds -.It Fl h Ar host -Connect to -.Ar host . -The default host is -.Li imap.fastmail.com . -TLS without STARTTLS is assumed. -. -.It Fl m Ar mailbox -Export messages from -.Ar mailbox . -The default mailbox is INBOX. -. -.It Fl p Ar port -Connect to -.Ar port . -The default port is 993. -. -.It Fl s Ar search -Export messages matching -.Ar search . -The default search is -.Ql SUBJECT \(dq[PATCH\(dq . -. -.It Fl v -Log IMAP protocol to standard error. -. -.It Fl w -Read the IMAP password from standard input. -.El -. -.Sh EXAMPLES -.Bd -literal -imbox june@causal.agency | git am -.Ed -. -.Sh STANDARDS -.Bl -item -.It -.Rs -.%A M. Crispin -.%Q University of Washington -.%T INTERNET MESSAGE ACCESS PROTOCOL - VERSION 4rev1 -.%I IETF -.%N RFC 3501 -.%D March 2003 -.%U https://tools.ietf.org/html/rfc3501 -.Re -.It -.Rs -.%A E. Hall -.%T The application/mbox Media Type -.%I IETF -.%N RFC 4155 -.%D September 2005 -.%U https://tools.ietf.org/html/rfc4155 -.Re -.El |