grep: rip out pessimization to use fixmatch()
[gitweb.git] / imap-send.c
index a7f7dfd22e72c00126855deb1915e3dc5fd49903..de8114bac010ef095cf9de17671566e248366dcb 100644 (file)
  */
 
 #include "cache.h"
+#include "exec_cmd.h"
+#include "run-command.h"
 #ifdef NO_OPENSSL
 typedef void *SSL;
 #endif
 
-typedef struct store_conf {
+struct store_conf {
        char *name;
        const char *path; /* should this be here? its interpretation is driver-specific */
        char *map_inbox;
        char *trash;
        unsigned max_size; /* off_t is overkill */
        unsigned trash_remote_new:1, trash_only_new:1;
-} store_conf_t;
+};
 
-typedef struct string_list {
+struct string_list {
        struct string_list *next;
        char string[1];
-} string_list_t;
+};
 
-typedef struct channel_conf {
+struct channel_conf {
        struct channel_conf *next;
        char *name;
-       store_conf_t *master, *slave;
+       struct store_conf *master, *slave;
        char *master_name, *slave_name;
        char *sync_state;
-       string_list_t *patterns;
+       struct string_list *patterns;
        int mops, sops;
        unsigned max_messages; /* for slave only */
-} channel_conf_t;
+};
 
-typedef struct group_conf {
+struct group_conf {
        struct group_conf *next;
        char *name;
-       string_list_t *channels;
-} group_conf_t;
+       struct string_list *channels;
+};
 
 /* For message->status */
 #define M_RECENT       (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
 #define M_DEAD         (1<<1) /* expunged */
 #define M_FLAGS        (1<<2) /* flags fetched */
 
-typedef struct message {
+struct message {
        struct message *next;
-       /* string_list_t *keywords; */
+       /* struct string_list *keywords; */
        size_t size; /* zero implies "not fetched" */
        int uid;
        unsigned char flags, status;
-} message_t;
+};
 
-typedef struct store {
-       store_conf_t *conf; /* foreign */
+struct store {
+       struct store_conf *conf; /* foreign */
 
        /* currently open mailbox */
        const char *name; /* foreign! maybe preset? */
        char *path; /* own */
-       message_t *msgs; /* own */
+       struct message *msgs; /* own */
        int uidvalidity;
        unsigned char opts; /* maybe preset? */
        /* note that the following do _not_ reflect stats from msgs, but mailbox totals */
        int count; /* # of messages */
        int recent; /* # of recent messages - don't trust this beyond the initial read */
-} store_t;
+};
 
-typedef struct {
+struct msg_data {
        char *data;
        int len;
        unsigned char flags;
        unsigned int crlf:1;
-} msg_data_t;
+};
+
+static const char imap_send_usage[] = "git imap-send < <mbox>";
 
+#undef DRV_OK
 #define DRV_OK          0
 #define DRV_MSG_BAD     -1
 #define DRV_BOX_BAD     -2
@@ -99,13 +104,16 @@ typedef struct {
 
 static int Verbose, Quiet;
 
+__attribute__((format (printf, 1, 2)))
 static void imap_info(const char *, ...);
+__attribute__((format (printf, 1, 2)))
 static void imap_warn(const char *, ...);
 
 static char *next_arg(char **);
 
-static void free_generic_messages(message_t *);
+static void free_generic_messages(struct message *);
 
+__attribute__((format (printf, 3, 4)))
 static int nfsnprintf(char *buf, int blen, const char *fmt, ...);
 
 static int nfvasprintf(char **strp, const char *fmt, va_list ap)
@@ -115,17 +123,14 @@ static int nfvasprintf(char **strp, const char *fmt, va_list ap)
 
        len = vsnprintf(tmp, sizeof(tmp), fmt, ap);
        if (len < 0)
-               die("Fatal: Out of memory\n");
+               die("Fatal: Out of memory");
        if (len >= sizeof(tmp))
-               die("imap command overflow !\n");
+               die("imap command overflow!");
        *strp = xmemdupz(tmp, len);
        return len;
 }
 
-static void arc4_init(void);
-static unsigned char arc4_getbyte(void);
-
-typedef struct imap_server_conf {
+struct imap_server_conf {
        char *name;
        char *tunnel;
        char *host;
@@ -134,58 +139,59 @@ typedef struct imap_server_conf {
        char *pass;
        int use_ssl;
        int ssl_verify;
-} imap_server_conf_t;
+       int use_html;
+};
 
-typedef struct imap_store_conf {
-       store_conf_t gen;
-       imap_server_conf_t *server;
+struct imap_store_conf {
+       struct store_conf gen;
+       struct imap_server_conf *server;
        unsigned use_namespace:1;
-} imap_store_conf_t;
+};
 
-#define NIL    (void*)0x1
-#define LIST   (void*)0x2
+#define NIL    (void *)0x1
+#define LIST   (void *)0x2
 
-typedef struct _list {
-       struct _list *next, *child;
+struct imap_list {
+       struct imap_list *next, *child;
        char *val;
        int len;
-} list_t;
+};
 
-typedef struct {
-       int fd;
+struct imap_socket {
+       int fd[2];
        SSL *ssl;
-} Socket_t;
+};
 
-typedef struct {
-       Socket_t sock;
+struct imap_buffer {
+       struct imap_socket sock;
        int bytes;
        int offset;
        char buf[1024];
-} buffer_t;
+};
 
 struct imap_cmd;
 
-typedef struct imap {
+struct imap {
        int uidnext; /* from SELECT responses */
-       list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
+       struct imap_list *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
        unsigned caps, rcaps; /* CAPABILITY results */
        /* command queue */
        int nexttag, num_in_progress, literal_pending;
        struct imap_cmd *in_progress, **in_progress_append;
-       buffer_t buf; /* this is BIG, so put it last */
-} imap_t;
+       struct imap_buffer buf; /* this is BIG, so put it last */
+};
 
-typedef struct imap_store {
-       store_t gen;
+struct imap_store {
+       struct store gen;
        int uidvalidity;
-       imap_t *imap;
+       struct imap *imap;
        const char *prefix;
        unsigned /*currentnc:1,*/ trashnc:1;
-} imap_store_t;
+};
 
 struct imap_cmd_cb {
-       int (*cont)(imap_store_t *ctx, struct imap_cmd *cmd, const char *prompt);
-       void (*done)(imap_store_t *ctx, struct imap_cmd *cmd, int response);
+       int (*cont)(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt);
+       void (*done)(struct imap_store *ctx, struct imap_cmd *cmd, int response);
        void *ctx;
        char *data;
        int dlen;
@@ -222,7 +228,7 @@ static const char *cap_list[] = {
 #define RESP_NO    1
 #define RESP_BAD   2
 
-static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd);
+static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd);
 
 
 static const char *Flags[] = {
@@ -236,11 +242,11 @@ static const char *Flags[] = {
 #ifndef NO_OPENSSL
 static void ssl_socket_perror(const char *func)
 {
-       fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), 0));
+       fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), NULL));
 }
 #endif
 
-static void socket_perror(const char *func, Socket_t *sock, int ret)
+static void socket_perror(const char *func, struct imap_socket *sock, int ret)
 {
 #ifndef NO_OPENSSL
        if (sock->ssl) {
@@ -265,13 +271,17 @@ static void socket_perror(const char *func, Socket_t *sock, int ret)
        }
 }
 
-static int ssl_socket_connect(Socket_t *sock, int use_tls_only, int verify)
+static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
 {
 #ifdef NO_OPENSSL
        fprintf(stderr, "SSL requested but SSL support not compiled in\n");
        return -1;
+#else
+#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
+       const SSL_METHOD *meth;
 #else
        SSL_METHOD *meth;
+#endif
        SSL_CTX *ctx;
        int ret;
 
@@ -302,8 +312,12 @@ static int ssl_socket_connect(Socket_t *sock, int use_tls_only, int verify)
                ssl_socket_perror("SSL_new");
                return -1;
        }
-       if (!SSL_set_fd(sock->ssl, sock->fd)) {
-               ssl_socket_perror("SSL_set_fd");
+       if (!SSL_set_rfd(sock->ssl, sock->fd[0])) {
+               ssl_socket_perror("SSL_set_rfd");
+               return -1;
+       }
+       if (!SSL_set_wfd(sock->ssl, sock->fd[1])) {
+               ssl_socket_perror("SSL_set_wfd");
                return -1;
        }
 
@@ -317,7 +331,7 @@ static int ssl_socket_connect(Socket_t *sock, int use_tls_only, int verify)
 #endif
 }
 
-static int socket_read(Socket_t *sock, char *buf, int len)
+static int socket_read(struct imap_socket *sock, char *buf, int len)
 {
        ssize_t n;
 #ifndef NO_OPENSSL
@@ -325,16 +339,17 @@ static int socket_read(Socket_t *sock, char *buf, int len)
                n = SSL_read(sock->ssl, buf, len);
        else
 #endif
-               n = xread(sock->fd, buf, len);
+               n = xread(sock->fd[0], buf, len);
        if (n <= 0) {
                socket_perror("read", sock, n);
-               close(sock->fd);
-               sock->fd = -1;
+               close(sock->fd[0]);
+               close(sock->fd[1]);
+               sock->fd[0] = sock->fd[1] = -1;
        }
        return n;
 }
 
-static int socket_write(Socket_t *sock, const char *buf, int len)
+static int socket_write(struct imap_socket *sock, const char *buf, int len)
 {
        int n;
 #ifndef NO_OPENSSL
@@ -342,16 +357,17 @@ static int socket_write(Socket_t *sock, const char *buf, int len)
                n = SSL_write(sock->ssl, buf, len);
        else
 #endif
-               n = write_in_full(sock->fd, buf, len);
+               n = write_in_full(sock->fd[1], buf, len);
        if (n != len) {
                socket_perror("write", sock, n);
-               close(sock->fd);
-               sock->fd = -1;
+               close(sock->fd[0]);
+               close(sock->fd[1]);
+               sock->fd[0] = sock->fd[1] = -1;
        }
        return n;
 }
 
-static void socket_shutdown(Socket_t *sock)
+static void socket_shutdown(struct imap_socket *sock)
 {
 #ifndef NO_OPENSSL
        if (sock->ssl) {
@@ -359,11 +375,12 @@ static void socket_shutdown(Socket_t *sock)
                SSL_free(sock->ssl);
        }
 #endif
-       close(sock->fd);
+       close(sock->fd[0]);
+       close(sock->fd[1]);
 }
 
 /* simple line buffering */
-static int buffer_gets(buffer_t * b, char **s)
+static int buffer_gets(struct imap_buffer *b, char **s)
 {
        int n;
        int start = b->offset;
@@ -465,9 +482,9 @@ static char *next_arg(char **s)
        return ret;
 }
 
-static void free_generic_messages(message_t *msgs)
+static void free_generic_messages(struct message *msgs)
 {
-       message_t *tmsg;
+       struct message *tmsg;
 
        for (; msgs; msgs = tmsg) {
                tmsg = msgs->next;
@@ -482,62 +499,16 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
 
        va_start(va, fmt);
        if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
-               die("Fatal: buffer too small. Please report a bug.\n");
+               die("Fatal: buffer too small. Please report a bug.");
        va_end(va);
        return ret;
 }
 
-static struct {
-       unsigned char i, j, s[256];
-} rs;
-
-static void arc4_init(void)
-{
-       int i, fd;
-       unsigned char j, si, dat[128];
-
-       if ((fd = open("/dev/urandom", O_RDONLY)) < 0 && (fd = open("/dev/random", O_RDONLY)) < 0) {
-               fprintf(stderr, "Fatal: no random number source available.\n");
-               exit(3);
-       }
-       if (read_in_full(fd, dat, 128) != 128) {
-               fprintf(stderr, "Fatal: cannot read random number source.\n");
-               exit(3);
-       }
-       close(fd);
-
-       for (i = 0; i < 256; i++)
-               rs.s[i] = i;
-       for (i = j = 0; i < 256; i++) {
-               si = rs.s[i];
-               j += si + dat[i & 127];
-               rs.s[i] = rs.s[j];
-               rs.s[j] = si;
-       }
-       rs.i = rs.j = 0;
-
-       for (i = 0; i < 256; i++)
-               arc4_getbyte();
-}
-
-static unsigned char arc4_getbyte(void)
-{
-       unsigned char si, sj;
-
-       rs.i++;
-       si = rs.s[rs.i];
-       rs.j += si;
-       sj = rs.s[rs.j];
-       rs.s[rs.i] = sj;
-       rs.s[rs.j] = si;
-       return rs.s[(si + sj) & 0xff];
-}
-
-static struct imap_cmd *v_issue_imap_cmd(imap_store_t *ctx,
+static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
                                         struct imap_cmd_cb *cb,
                                         const char *fmt, va_list ap)
 {
-       imap_t *imap = ctx->imap;
+       struct imap *imap = ctx->imap;
        struct imap_cmd *cmd;
        int n, bufl;
        char buf[1024];
@@ -577,8 +548,7 @@ static struct imap_cmd *v_issue_imap_cmd(imap_store_t *ctx,
                        n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen);
                        free(cmd->cb.data);
                        if (n != cmd->cb.dlen ||
-                           (n = socket_write(&imap->buf.sock, "\r\n", 2)) != 2)
-                       {
+                           socket_write(&imap->buf.sock, "\r\n", 2) != 2) {
                                free(cmd->cmd);
                                free(cmd);
                                return NULL;
@@ -595,7 +565,8 @@ static struct imap_cmd *v_issue_imap_cmd(imap_store_t *ctx,
        return cmd;
 }
 
-static struct imap_cmd *issue_imap_cmd(imap_store_t *ctx,
+__attribute__((format (printf, 3, 4)))
+static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
                                       struct imap_cmd_cb *cb,
                                       const char *fmt, ...)
 {
@@ -608,7 +579,8 @@ static struct imap_cmd *issue_imap_cmd(imap_store_t *ctx,
        return ret;
 }
 
-static int imap_exec(imap_store_t *ctx, struct imap_cmd_cb *cb,
+__attribute__((format (printf, 3, 4)))
+static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,
                     const char *fmt, ...)
 {
        va_list ap;
@@ -623,7 +595,8 @@ static int imap_exec(imap_store_t *ctx, struct imap_cmd_cb *cb,
        return get_cmd_result(ctx, cmdp);
 }
 
-static int imap_exec_m(imap_store_t *ctx, struct imap_cmd_cb *cb,
+__attribute__((format (printf, 3, 4)))
+static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb,
                       const char *fmt, ...)
 {
        va_list ap;
@@ -642,19 +615,19 @@ static int imap_exec_m(imap_store_t *ctx, struct imap_cmd_cb *cb,
        }
 }
 
-static int is_atom(list_t *list)
+static int is_atom(struct imap_list *list)
 {
        return list && list->val && list->val != NIL && list->val != LIST;
 }
 
-static int is_list(list_t *list)
+static int is_list(struct imap_list *list)
 {
        return list && list->val == LIST;
 }
 
-static void free_list(list_t *list)
+static void free_list(struct imap_list *list)
 {
-       list_t *tmp;
+       struct imap_list *tmp;
 
        for (; list; list = tmp) {
                tmp = list->next;
@@ -666,9 +639,9 @@ static void free_list(list_t *list)
        }
 }
 
-static int parse_imap_list_l(imap_t *imap, char **sp, list_t **curp, int level)
+static int parse_imap_list_l(struct imap *imap, char **sp, struct imap_list **curp, int level)
 {
-       list_t *cur;
+       struct imap_list *cur;
        char *s = *sp, *p;
        int n, bytes;
 
@@ -737,11 +710,10 @@ static int parse_imap_list_l(imap_t *imap, char **sp, list_t **curp, int level)
                                if (level && *s == ')')
                                        break;
                        cur->len = s - p;
-                       if (cur->len == 3 && !memcmp("NIL", p, 3)) {
+                       if (cur->len == 3 && !memcmp("NIL", p, 3))
                                cur->val = NIL;
-                       } else {
+                       else
                                cur->val = xmemdupz(p, cur->len);
-                       }
                }
 
                if (!level)
@@ -753,14 +725,14 @@ static int parse_imap_list_l(imap_t *imap, char **sp, list_t **curp, int level)
        *curp = NULL;
        return 0;
 
-  bail:
+bail:
        *curp = NULL;
        return -1;
 }
 
-static list_t *parse_imap_list(imap_t *imap, char **sp)
+static struct imap_list *parse_imap_list(struct imap *imap, char **sp)
 {
-       list_t *head;
+       struct imap_list *head;
 
        if (!parse_imap_list_l(imap, sp, &head, 0))
                return head;
@@ -768,12 +740,12 @@ static list_t *parse_imap_list(imap_t *imap, char **sp)
        return NULL;
 }
 
-static list_t *parse_list(char **sp)
+static struct imap_list *parse_list(char **sp)
 {
        return parse_imap_list(NULL, sp);
 }
 
-static void parse_capability(imap_t *imap, char *cmd)
+static void parse_capability(struct imap *imap, char *cmd)
 {
        char *arg;
        unsigned i;
@@ -786,10 +758,10 @@ static void parse_capability(imap_t *imap, char *cmd)
        imap->rcaps = imap->caps;
 }
 
-static int parse_response_code(imap_store_t *ctx, struct imap_cmd_cb *cb,
+static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb,
                               char *s)
 {
-       imap_t *imap = ctx->imap;
+       struct imap *imap = ctx->imap;
        char *arg, *p;
 
        if (*s != '[')
@@ -821,8 +793,7 @@ static int parse_response_code(imap_store_t *ctx, struct imap_cmd_cb *cb,
                fprintf(stderr, "*** IMAP ALERT *** %s\n", p);
        } else if (cb && cb->ctx && !strcmp("APPENDUID", arg)) {
                if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg)) ||
-                   !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg)))
-               {
+                   !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg))) {
                        fprintf(stderr, "IMAP error: malformed APPENDUID status\n");
                        return RESP_BAD;
                }
@@ -830,9 +801,9 @@ static int parse_response_code(imap_store_t *ctx, struct imap_cmd_cb *cb,
        return RESP_OK;
 }
 
-static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd)
+static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
 {
-       imap_t *imap = ctx->imap;
+       struct imap *imap = ctx->imap;
        struct imap_cmd *cmdp, **pcmdp, *ncmdp;
        char *cmd, *arg, *arg1, *p;
        int n, resp, resp2, tag;
@@ -902,7 +873,7 @@ static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd)
                                        goto gottag;
                        fprintf(stderr, "IMAP error: unexpected tag %s\n", arg);
                        return RESP_BAD;
-                 gottag:
+               gottag:
                        if (!(*pcmdp = cmdp->next))
                                imap->in_progress_append = pcmdp;
                        imap->num_in_progress--;
@@ -915,7 +886,7 @@ static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd)
                                if (!strcmp("NO", arg)) {
                                        if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp(cmd, "[TRYCREATE]", 11))) { /* SELECT, APPEND or UID COPY */
                                                p = strchr(cmdp->cmd, '"');
-                                               if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", strchr(p + 1, '"') - p + 1, p)) {
+                                               if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", (int)(strchr(p + 1, '"') - p + 1), p)) {
                                                        resp = RESP_BAD;
                                                        goto normal;
                                                }
@@ -944,7 +915,7 @@ static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd)
                        }
                        if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp)
                                resp = resp2;
-                 normal:
+               normal:
                        if (cmdp->cb.done)
                                cmdp->cb.done(ctx, cmdp, resp);
                        free(cmdp->cb.data);
@@ -957,11 +928,11 @@ static int get_cmd_result(imap_store_t *ctx, struct imap_cmd *tcmd)
        /* not reached */
 }
 
-static void imap_close_server(imap_store_t *ictx)
+static void imap_close_server(struct imap_store *ictx)
 {
-       imap_t *imap = ictx->imap;
+       struct imap *imap = ictx->imap;
 
-       if (imap->buf.sock.fd != -1) {
+       if (imap->buf.sock.fd[0] != -1) {
                imap_exec(ictx, NULL, "LOGOUT");
                socket_shutdown(&imap->buf.sock);
        }
@@ -971,57 +942,95 @@ static void imap_close_server(imap_store_t *ictx)
        free(imap);
 }
 
-static void imap_close_store(store_t *ctx)
+static void imap_close_store(struct store *ctx)
 {
-       imap_close_server((imap_store_t *)ctx);
+       imap_close_server((struct imap_store *)ctx);
        free_generic_messages(ctx->msgs);
        free(ctx);
 }
 
-static store_t *imap_open_store(imap_server_conf_t *srvc)
+static struct store *imap_open_store(struct imap_server_conf *srvc)
 {
-       imap_store_t *ctx;
-       imap_t *imap;
+       struct imap_store *ctx;
+       struct imap *imap;
        char *arg, *rsp;
-       struct hostent *he;
-       struct sockaddr_in addr;
-       int s, a[2], preauth;
-       pid_t pid;
+       int s = -1, preauth;
 
        ctx = xcalloc(sizeof(*ctx), 1);
 
        ctx->imap = imap = xcalloc(sizeof(*imap), 1);
-       imap->buf.sock.fd = -1;
+       imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
        imap->in_progress_append = &imap->in_progress;
 
        /* open connection to IMAP server */
 
        if (srvc->tunnel) {
-               imap_info("Starting tunnel '%s'... ", srvc->tunnel);
+               const char *argv[4];
+               struct child_process tunnel = {0};
 
-               if (socketpair(PF_UNIX, SOCK_STREAM, 0, a)) {
-                       perror("socketpair");
-                       exit(1);
-               }
+               imap_info("Starting tunnel '%s'... ", srvc->tunnel);
 
-               pid = fork();
-               if (pid < 0)
-                       _exit(127);
-               if (!pid) {
-                       if (dup2(a[0], 0) == -1 || dup2(a[0], 1) == -1)
-                               _exit(127);
-                       close(a[0]);
-                       close(a[1]);
-                       execl("/bin/sh", "sh", "-c", srvc->tunnel, NULL);
-                       _exit(127);
-               }
+               argv[0] = "sh";
+               argv[1] = "-c";
+               argv[2] = srvc->tunnel;
+               argv[3] = NULL;
 
-               close(a[0]);
+               tunnel.argv = argv;
+               tunnel.in = -1;
+               tunnel.out = -1;
+               if (start_command(&tunnel))
+                       die("cannot start proxy %s", argv[0]);
 
-               imap->buf.sock.fd = a[1];
+               imap->buf.sock.fd[0] = tunnel.out;
+               imap->buf.sock.fd[1] = tunnel.in;
 
                imap_info("ok\n");
        } else {
+#ifndef NO_IPV6
+               struct addrinfo hints, *ai0, *ai;
+               int gai;
+               char portstr[6];
+
+               snprintf(portstr, sizeof(portstr), "%hu", srvc->port);
+
+               memset(&hints, 0, sizeof(hints));
+               hints.ai_socktype = SOCK_STREAM;
+               hints.ai_protocol = IPPROTO_TCP;
+
+               imap_info("Resolving %s... ", srvc->host);
+               gai = getaddrinfo(srvc->host, portstr, &hints, &ai);
+               if (gai) {
+                       fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
+                       goto bail;
+               }
+               imap_info("ok\n");
+
+               for (ai0 = ai; ai; ai = ai->ai_next) {
+                       char addr[NI_MAXHOST];
+
+                       s = socket(ai->ai_family, ai->ai_socktype,
+                                  ai->ai_protocol);
+                       if (s < 0)
+                               continue;
+
+                       getnameinfo(ai->ai_addr, ai->ai_addrlen, addr,
+                                   sizeof(addr), NULL, 0, NI_NUMERICHOST);
+                       imap_info("Connecting to [%s]:%s... ", addr, portstr);
+
+                       if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
+                               close(s);
+                               s = -1;
+                               perror("connect");
+                               continue;
+                       }
+
+                       break;
+               }
+               freeaddrinfo(ai0);
+#else /* NO_IPV6 */
+               struct hostent *he;
+               struct sockaddr_in addr;
+
                memset(&addr, 0, sizeof(addr));
                addr.sin_port = htons(srvc->port);
                addr.sin_family = AF_INET;
@@ -1041,11 +1050,17 @@ static store_t *imap_open_store(imap_server_conf_t *srvc)
                imap_info("Connecting to %s:%hu... ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
                if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
                        close(s);
+                       s = -1;
                        perror("connect");
+               }
+#endif
+               if (s < 0) {
+                       fputs("Error: unable to connect to server.\n", stderr);
                        goto bail;
                }
 
-               imap->buf.sock.fd = s;
+               imap->buf.sock.fd[0] = s;
+               imap->buf.sock.fd[1] = dup(s);
 
                if (srvc->use_ssl &&
                    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
@@ -1117,8 +1132,8 @@ static store_t *imap_open_store(imap_server_conf_t *srvc)
                        goto bail;
                }
                if (!imap->buf.sock.ssl)
-                       imap_warn( "*** IMAP Warning *** Password is being "
-                                  "sent in the clear\n" );
+                       imap_warn("*** IMAP Warning *** Password is being "
+                                 "sent in the clear\n");
                if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
                        fprintf(stderr, "IMAP error: LOGIN failed\n");
                        goto bail;
@@ -1127,9 +1142,9 @@ static store_t *imap_open_store(imap_server_conf_t *srvc)
 
        ctx->prefix = "";
        ctx->trashnc = 1;
-       return (store_t *)ctx;
+       return (struct store *)ctx;
 
-  bail:
+bail:
        imap_close_store(&ctx->gen);
        return NULL;
 }
@@ -1151,88 +1166,20 @@ static int imap_make_flags(int flags, char *buf)
        return d;
 }
 
-#define TUIDL 8
-
-static int imap_store_msg(store_t *gctx, msg_data_t *data, int *uid)
+static int imap_store_msg(struct store *gctx, struct msg_data *data)
 {
-       imap_store_t *ctx = (imap_store_t *)gctx;
-       imap_t *imap = ctx->imap;
+       struct imap_store *ctx = (struct imap_store *)gctx;
+       struct imap *imap = ctx->imap;
        struct imap_cmd_cb cb;
-       char *fmap, *buf;
        const char *prefix, *box;
-       int ret, i, j, d, len, extra, nocr;
-       int start, sbreak = 0, ebreak = 0;
-       char flagstr[128], tuid[TUIDL * 2 + 1];
+       int ret, d;
+       char flagstr[128];
 
        memset(&cb, 0, sizeof(cb));
 
-       fmap = data->data;
-       len = data->len;
-       nocr = !data->crlf;
-       extra = 0, i = 0;
-       if (!CAP(UIDPLUS) && uid) {
-         nloop:
-               start = i;
-               while (i < len)
-                       if (fmap[i++] == '\n') {
-                               extra += nocr;
-                               if (i - 2 + nocr == start) {
-                                       sbreak = ebreak = i - 2 + nocr;
-                                       goto mktid;
-                               }
-                               if (!memcmp(fmap + start, "X-TUID: ", 8)) {
-                                       extra -= (ebreak = i) - (sbreak = start) + nocr;
-                                       goto mktid;
-                               }
-                               goto nloop;
-                       }
-               /* invalid message */
-               free(fmap);
-               return DRV_MSG_BAD;
-        mktid:
-               for (j = 0; j < TUIDL; j++)
-                       sprintf(tuid + j * 2, "%02x", arc4_getbyte());
-               extra += 8 + TUIDL * 2 + 2;
-       }
-       if (nocr)
-               for (; i < len; i++)
-                       if (fmap[i] == '\n')
-                               extra++;
-
-       cb.dlen = len + extra;
-       buf = cb.data = xmalloc(cb.dlen);
-       i = 0;
-       if (!CAP(UIDPLUS) && uid) {
-               if (nocr) {
-                       for (; i < sbreak; i++)
-                               if (fmap[i] == '\n') {
-                                       *buf++ = '\r';
-                                       *buf++ = '\n';
-                               } else
-                                       *buf++ = fmap[i];
-               } else {
-                       memcpy(buf, fmap, sbreak);
-                       buf += sbreak;
-               }
-               memcpy(buf, "X-TUID: ", 8);
-               buf += 8;
-               memcpy(buf, tuid, TUIDL * 2);
-               buf += TUIDL * 2;
-               *buf++ = '\r';
-               *buf++ = '\n';
-               i = ebreak;
-       }
-       if (nocr) {
-               for (; i < len; i++)
-                       if (fmap[i] == '\n') {
-                               *buf++ = '\r';
-                               *buf++ = '\n';
-                       } else
-                               *buf++ = fmap[i];
-       } else
-               memcpy(buf, fmap + i, len - i);
-
-       free(fmap);
+       cb.dlen = data->len;
+       cb.data = xmalloc(cb.dlen);
+       memcpy(cb.data, data->data, data->len);
 
        d = 0;
        if (data->flags) {
@@ -1241,38 +1188,72 @@ static int imap_store_msg(store_t *gctx, msg_data_t *data, int *uid)
        }
        flagstr[d] = 0;
 
-       if (!uid) {
-               box = gctx->conf->trash;
-               prefix = ctx->prefix;
-               cb.create = 1;
-               if (ctx->trashnc)
-                       imap->caps = imap->rcaps & ~(1 << LITERALPLUS);
-       } else {
-               box = gctx->name;
-               prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
-               cb.create = 0;
-       }
-       cb.ctx = uid;
+       box = gctx->name;
+       prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
+       cb.create = 0;
        ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr);
        imap->caps = imap->rcaps;
        if (ret != DRV_OK)
                return ret;
-       if (!uid)
-               ctx->trashnc = 0;
-       else
-               gctx->count++;
+       gctx->count++;
 
        return DRV_OK;
 }
 
+static void encode_html_chars(struct strbuf *p)
+{
+       int i;
+       for (i = 0; i < p->len; i++) {
+               if (p->buf[i] == '&')
+                       strbuf_splice(p, i, 1, "&amp;", 5);
+               if (p->buf[i] == '<')
+                       strbuf_splice(p, i, 1, "&lt;", 4);
+               if (p->buf[i] == '>')
+                       strbuf_splice(p, i, 1, "&gt;", 4);
+               if (p->buf[i] == '"')
+                       strbuf_splice(p, i, 1, "&quot;", 6);
+       }
+}
+static void wrap_in_html(struct msg_data *msg)
+{
+       struct strbuf buf = STRBUF_INIT;
+       struct strbuf **lines;
+       struct strbuf **p;
+       static char *content_type = "Content-Type: text/html;\n";
+       static char *pre_open = "<pre>\n";
+       static char *pre_close = "</pre>\n";
+       int added_header = 0;
+
+       strbuf_attach(&buf, msg->data, msg->len, msg->len);
+       lines = strbuf_split(&buf, '\n');
+       strbuf_release(&buf);
+       for (p = lines; *p; p++) {
+               if (! added_header) {
+                       if ((*p)->len == 1 && *((*p)->buf) == '\n') {
+                               strbuf_addstr(&buf, content_type);
+                               strbuf_addbuf(&buf, *p);
+                               strbuf_addstr(&buf, pre_open);
+                               added_header = 1;
+                               continue;
+                       }
+               }
+               else
+                       encode_html_chars(*p);
+               strbuf_addbuf(&buf, *p);
+       }
+       strbuf_addstr(&buf, pre_close);
+       strbuf_list_free(lines);
+       msg->len  = buf.len;
+       msg->data = strbuf_detach(&buf, NULL);
+}
+
 #define CHUNKSIZE 0x1000
 
-static int read_message(FILE *f, msg_data_t *msg)
+static int read_message(FILE *f, struct msg_data *msg)
 {
-       struct strbuf buf;
+       struct strbuf buf = STRBUF_INIT;
 
        memset(msg, 0, sizeof(*msg));
-       strbuf_init(&buf, 0);
 
        do {
                if (strbuf_fread(&buf, CHUNKSIZE, f) <= 0)
@@ -1284,7 +1265,7 @@ static int read_message(FILE *f, msg_data_t *msg)
        return msg->len;
 }
 
-static int count_messages(msg_data_t *msg)
+static int count_messages(struct msg_data *msg)
 {
        int count = 0;
        char *p = msg->data;
@@ -1302,7 +1283,7 @@ static int count_messages(msg_data_t *msg)
        return count;
 }
 
-static int split_msg(msg_data_t *all_msgs, msg_data_t *msg, int *ofs)
+static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
 {
        char *p, *data;
 
@@ -1333,7 +1314,7 @@ static int split_msg(msg_data_t *all_msgs, msg_data_t *msg, int *ofs)
        return 1;
 }
 
-static imap_server_conf_t server = {
+static struct imap_server_conf server = {
        NULL,   /* name */
        NULL,   /* tunnel */
        NULL,   /* host */
@@ -1342,6 +1323,7 @@ static imap_server_conf_t server = {
        NULL,   /* pass */
        0,      /* use_ssl */
        1,      /* ssl_verify */
+       0,      /* use_html */
 };
 
 static char *imap_folder;
@@ -1370,8 +1352,7 @@ static int git_imap_config(const char *key, const char *val, void *cb)
                if (!prefixcmp(val, "//"))
                        val += 2;
                server.host = xstrdup(val);
-       }
-       else if (!strcmp("user", key))
+       } else if (!strcmp("user", key))
                server.user = xstrdup(val);
        else if (!strcmp("pass", key))
                server.pass = xstrdup(val);
@@ -1381,21 +1362,24 @@ static int git_imap_config(const char *key, const char *val, void *cb)
                server.tunnel = xstrdup(val);
        else if (!strcmp("sslverify", key))
                server.ssl_verify = git_config_bool(key, val);
+       else if (!strcmp("preformattedHTML", key))
+               server.use_html = git_config_bool(key, val);
        return 0;
 }
 
 int main(int argc, char **argv)
 {
-       msg_data_t all_msgs, msg;
-       store_t *ctx = NULL;
-       int uid = 0;
+       struct msg_data all_msgs, msg;
+       struct store *ctx = NULL;
        int ofs = 0;
        int r;
        int total, n = 0;
        int nongit_ok;
 
-       /* init the random number generator */
-       arc4_init();
+       git_extract_argv0_path(argv[0]);
+
+       if (argc != 1)
+               usage(imap_send_usage);
 
        setup_git_directory_gently(&nongit_ok);
        git_config(git_imap_config, NULL);
@@ -1417,32 +1401,35 @@ int main(int argc, char **argv)
 
        /* read the messages */
        if (!read_message(stdin, &all_msgs)) {
-               fprintf(stderr,"nothing to send\n");
+               fprintf(stderr, "nothing to send\n");
                return 1;
        }
 
        total = count_messages(&all_msgs);
        if (!total) {
-               fprintf(stderr,"no messages to send\n");
+               fprintf(stderr, "no messages to send\n");
                return 1;
        }
 
        /* write it to the imap server */
        ctx = imap_open_store(&server);
        if (!ctx) {
-               fprintf(stderr,"failed to open store\n");
+               fprintf(stderr, "failed to open store\n");
                return 1;
        }
 
-       fprintf(stderr, "sending %d message%s\n", total, (total!=1)?"s":"");
+       fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
        ctx->name = imap_folder;
        while (1) {
                unsigned percent = n * 100 / total;
                fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
                if (!split_msg(&all_msgs, &msg, &ofs))
                        break;
-               r = imap_store_msg(ctx, &msg, &uid);
-               if (r != DRV_OK) break;
+               if (server.use_html)
+                       wrap_in_html(&msg);
+               r = imap_store_msg(ctx, &msg);
+               if (r != DRV_OK)
+                       break;
                n++;
        }
        fprintf(stderr, "\n");