Merge branch 'sv/submitting-final-patch'
[git] / imap-send.c
1 /*
2  * git-imap-send - drops patches into an imap Drafts folder
3  *                 derived from isync/mbsync - mailbox synchronizer
4  *
5  * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
6  * Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
7  * Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
8  * Copyright (C) 2006 Mike McCormack
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
23  */
24
25 #include "cache.h"
26 #include "credential.h"
27 #include "exec_cmd.h"
28 #include "run-command.h"
29 #ifdef NO_OPENSSL
30 typedef void *SSL;
31 #endif
32
33 static const char imap_send_usage[] = "git imap-send < <mbox>";
34
35 #undef DRV_OK
36 #define DRV_OK          0
37 #define DRV_MSG_BAD     -1
38 #define DRV_BOX_BAD     -2
39 #define DRV_STORE_BAD   -3
40
41 static int Verbose, Quiet;
42
43 __attribute__((format (printf, 1, 2)))
44 static void imap_info(const char *, ...);
45 __attribute__((format (printf, 1, 2)))
46 static void imap_warn(const char *, ...);
47
48 static char *next_arg(char **);
49
50 __attribute__((format (printf, 3, 4)))
51 static int nfsnprintf(char *buf, int blen, const char *fmt, ...);
52
53 static int nfvasprintf(char **strp, const char *fmt, va_list ap)
54 {
55         int len;
56         char tmp[8192];
57
58         len = vsnprintf(tmp, sizeof(tmp), fmt, ap);
59         if (len < 0)
60                 die("Fatal: Out of memory");
61         if (len >= sizeof(tmp))
62                 die("imap command overflow!");
63         *strp = xmemdupz(tmp, len);
64         return len;
65 }
66
67 struct imap_server_conf {
68         char *name;
69         char *tunnel;
70         char *host;
71         int port;
72         char *folder;
73         char *user;
74         char *pass;
75         int use_ssl;
76         int ssl_verify;
77         int use_html;
78         char *auth_method;
79 };
80
81 static struct imap_server_conf server = {
82         NULL,   /* name */
83         NULL,   /* tunnel */
84         NULL,   /* host */
85         0,      /* port */
86         NULL,   /* folder */
87         NULL,   /* user */
88         NULL,   /* pass */
89         0,      /* use_ssl */
90         1,      /* ssl_verify */
91         0,      /* use_html */
92         NULL,   /* auth_method */
93 };
94
95 struct imap_socket {
96         int fd[2];
97         SSL *ssl;
98 };
99
100 struct imap_buffer {
101         struct imap_socket sock;
102         int bytes;
103         int offset;
104         char buf[1024];
105 };
106
107 struct imap_cmd;
108
109 struct imap {
110         int uidnext; /* from SELECT responses */
111         unsigned caps, rcaps; /* CAPABILITY results */
112         /* command queue */
113         int nexttag, num_in_progress, literal_pending;
114         struct imap_cmd *in_progress, **in_progress_append;
115         struct imap_buffer buf; /* this is BIG, so put it last */
116 };
117
118 struct imap_store {
119         /* currently open mailbox */
120         const char *name; /* foreign! maybe preset? */
121         int uidvalidity;
122         struct imap *imap;
123         const char *prefix;
124 };
125
126 struct imap_cmd_cb {
127         int (*cont)(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt);
128         void (*done)(struct imap_store *ctx, struct imap_cmd *cmd, int response);
129         void *ctx;
130         char *data;
131         int dlen;
132         int uid;
133 };
134
135 struct imap_cmd {
136         struct imap_cmd *next;
137         struct imap_cmd_cb cb;
138         char *cmd;
139         int tag;
140 };
141
142 #define CAP(cap) (imap->caps & (1 << (cap)))
143
144 enum CAPABILITY {
145         NOLOGIN = 0,
146         UIDPLUS,
147         LITERALPLUS,
148         NAMESPACE,
149         STARTTLS,
150         AUTH_CRAM_MD5
151 };
152
153 static const char *cap_list[] = {
154         "LOGINDISABLED",
155         "UIDPLUS",
156         "LITERAL+",
157         "NAMESPACE",
158         "STARTTLS",
159         "AUTH=CRAM-MD5",
160 };
161
162 #define RESP_OK    0
163 #define RESP_NO    1
164 #define RESP_BAD   2
165
166 static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd);
167
168
169 #ifndef NO_OPENSSL
170 static void ssl_socket_perror(const char *func)
171 {
172         fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), NULL));
173 }
174 #endif
175
176 static void socket_perror(const char *func, struct imap_socket *sock, int ret)
177 {
178 #ifndef NO_OPENSSL
179         if (sock->ssl) {
180                 int sslerr = SSL_get_error(sock->ssl, ret);
181                 switch (sslerr) {
182                 case SSL_ERROR_NONE:
183                         break;
184                 case SSL_ERROR_SYSCALL:
185                         perror("SSL_connect");
186                         break;
187                 default:
188                         ssl_socket_perror("SSL_connect");
189                         break;
190                 }
191         } else
192 #endif
193         {
194                 if (ret < 0)
195                         perror(func);
196                 else
197                         fprintf(stderr, "%s: unexpected EOF\n", func);
198         }
199 }
200
201 #ifdef NO_OPENSSL
202 static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
203 {
204         fprintf(stderr, "SSL requested but SSL support not compiled in\n");
205         return -1;
206 }
207
208 #else
209
210 static int host_matches(const char *host, const char *pattern)
211 {
212         if (pattern[0] == '*' && pattern[1] == '.') {
213                 pattern += 2;
214                 if (!(host = strchr(host, '.')))
215                         return 0;
216                 host++;
217         }
218
219         return *host && *pattern && !strcasecmp(host, pattern);
220 }
221
222 static int verify_hostname(X509 *cert, const char *hostname)
223 {
224         int len;
225         X509_NAME *subj;
226         char cname[1000];
227         int i, found;
228         STACK_OF(GENERAL_NAME) *subj_alt_names;
229
230         /* try the DNS subjectAltNames */
231         found = 0;
232         if ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) {
233                 int num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names);
234                 for (i = 0; !found && i < num_subj_alt_names; i++) {
235                         GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
236                         if (subj_alt_name->type == GEN_DNS &&
237                             strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length &&
238                             host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data)))
239                                 found = 1;
240                 }
241                 sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);
242         }
243         if (found)
244                 return 0;
245
246         /* try the common name */
247         if (!(subj = X509_get_subject_name(cert)))
248                 return error("cannot get certificate subject");
249         if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
250                 return error("cannot get certificate common name");
251         if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
252                 return 0;
253         return error("certificate owner '%s' does not match hostname '%s'",
254                      cname, hostname);
255 }
256
257 static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
258 {
259 #if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
260         const SSL_METHOD *meth;
261 #else
262         SSL_METHOD *meth;
263 #endif
264         SSL_CTX *ctx;
265         int ret;
266         X509 *cert;
267
268         SSL_library_init();
269         SSL_load_error_strings();
270
271         if (use_tls_only)
272                 meth = TLSv1_method();
273         else
274                 meth = SSLv23_method();
275
276         if (!meth) {
277                 ssl_socket_perror("SSLv23_method");
278                 return -1;
279         }
280
281         ctx = SSL_CTX_new(meth);
282
283         if (verify)
284                 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
285
286         if (!SSL_CTX_set_default_verify_paths(ctx)) {
287                 ssl_socket_perror("SSL_CTX_set_default_verify_paths");
288                 return -1;
289         }
290         sock->ssl = SSL_new(ctx);
291         if (!sock->ssl) {
292                 ssl_socket_perror("SSL_new");
293                 return -1;
294         }
295         if (!SSL_set_rfd(sock->ssl, sock->fd[0])) {
296                 ssl_socket_perror("SSL_set_rfd");
297                 return -1;
298         }
299         if (!SSL_set_wfd(sock->ssl, sock->fd[1])) {
300                 ssl_socket_perror("SSL_set_wfd");
301                 return -1;
302         }
303
304 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
305         /*
306          * SNI (RFC4366)
307          * OpenSSL does not document this function, but the implementation
308          * returns 1 on success, 0 on failure after calling SSLerr().
309          */
310         ret = SSL_set_tlsext_host_name(sock->ssl, server.host);
311         if (ret != 1)
312                 warning("SSL_set_tlsext_host_name(%s) failed.", server.host);
313 #endif
314
315         ret = SSL_connect(sock->ssl);
316         if (ret <= 0) {
317                 socket_perror("SSL_connect", sock, ret);
318                 return -1;
319         }
320
321         if (verify) {
322                 /* make sure the hostname matches that of the certificate */
323                 cert = SSL_get_peer_certificate(sock->ssl);
324                 if (!cert)
325                         return error("unable to get peer certificate.");
326                 if (verify_hostname(cert, server.host) < 0)
327                         return -1;
328         }
329
330         return 0;
331 }
332 #endif
333
334 static int socket_read(struct imap_socket *sock, char *buf, int len)
335 {
336         ssize_t n;
337 #ifndef NO_OPENSSL
338         if (sock->ssl)
339                 n = SSL_read(sock->ssl, buf, len);
340         else
341 #endif
342                 n = xread(sock->fd[0], buf, len);
343         if (n <= 0) {
344                 socket_perror("read", sock, n);
345                 close(sock->fd[0]);
346                 close(sock->fd[1]);
347                 sock->fd[0] = sock->fd[1] = -1;
348         }
349         return n;
350 }
351
352 static int socket_write(struct imap_socket *sock, const char *buf, int len)
353 {
354         int n;
355 #ifndef NO_OPENSSL
356         if (sock->ssl)
357                 n = SSL_write(sock->ssl, buf, len);
358         else
359 #endif
360                 n = write_in_full(sock->fd[1], buf, len);
361         if (n != len) {
362                 socket_perror("write", sock, n);
363                 close(sock->fd[0]);
364                 close(sock->fd[1]);
365                 sock->fd[0] = sock->fd[1] = -1;
366         }
367         return n;
368 }
369
370 static void socket_shutdown(struct imap_socket *sock)
371 {
372 #ifndef NO_OPENSSL
373         if (sock->ssl) {
374                 SSL_shutdown(sock->ssl);
375                 SSL_free(sock->ssl);
376         }
377 #endif
378         close(sock->fd[0]);
379         close(sock->fd[1]);
380 }
381
382 /* simple line buffering */
383 static int buffer_gets(struct imap_buffer *b, char **s)
384 {
385         int n;
386         int start = b->offset;
387
388         *s = b->buf + start;
389
390         for (;;) {
391                 /* make sure we have enough data to read the \r\n sequence */
392                 if (b->offset + 1 >= b->bytes) {
393                         if (start) {
394                                 /* shift down used bytes */
395                                 *s = b->buf;
396
397                                 assert(start <= b->bytes);
398                                 n = b->bytes - start;
399
400                                 if (n)
401                                         memmove(b->buf, b->buf + start, n);
402                                 b->offset -= start;
403                                 b->bytes = n;
404                                 start = 0;
405                         }
406
407                         n = socket_read(&b->sock, b->buf + b->bytes,
408                                          sizeof(b->buf) - b->bytes);
409
410                         if (n <= 0)
411                                 return -1;
412
413                         b->bytes += n;
414                 }
415
416                 if (b->buf[b->offset] == '\r') {
417                         assert(b->offset + 1 < b->bytes);
418                         if (b->buf[b->offset + 1] == '\n') {
419                                 b->buf[b->offset] = 0;  /* terminate the string */
420                                 b->offset += 2; /* next line */
421                                 if (Verbose)
422                                         puts(*s);
423                                 return 0;
424                         }
425                 }
426
427                 b->offset++;
428         }
429         /* not reached */
430 }
431
432 static void imap_info(const char *msg, ...)
433 {
434         va_list va;
435
436         if (!Quiet) {
437                 va_start(va, msg);
438                 vprintf(msg, va);
439                 va_end(va);
440                 fflush(stdout);
441         }
442 }
443
444 static void imap_warn(const char *msg, ...)
445 {
446         va_list va;
447
448         if (Quiet < 2) {
449                 va_start(va, msg);
450                 vfprintf(stderr, msg, va);
451                 va_end(va);
452         }
453 }
454
455 static char *next_arg(char **s)
456 {
457         char *ret;
458
459         if (!s || !*s)
460                 return NULL;
461         while (isspace((unsigned char) **s))
462                 (*s)++;
463         if (!**s) {
464                 *s = NULL;
465                 return NULL;
466         }
467         if (**s == '"') {
468                 ++*s;
469                 ret = *s;
470                 *s = strchr(*s, '"');
471         } else {
472                 ret = *s;
473                 while (**s && !isspace((unsigned char) **s))
474                         (*s)++;
475         }
476         if (*s) {
477                 if (**s)
478                         *(*s)++ = 0;
479                 if (!**s)
480                         *s = NULL;
481         }
482         return ret;
483 }
484
485 static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
486 {
487         int ret;
488         va_list va;
489
490         va_start(va, fmt);
491         if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
492                 die("Fatal: buffer too small. Please report a bug.");
493         va_end(va);
494         return ret;
495 }
496
497 static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
498                                        struct imap_cmd_cb *cb,
499                                        const char *fmt, va_list ap)
500 {
501         struct imap *imap = ctx->imap;
502         struct imap_cmd *cmd;
503         int n, bufl;
504         char buf[1024];
505
506         cmd = xmalloc(sizeof(struct imap_cmd));
507         nfvasprintf(&cmd->cmd, fmt, ap);
508         cmd->tag = ++imap->nexttag;
509
510         if (cb)
511                 cmd->cb = *cb;
512         else
513                 memset(&cmd->cb, 0, sizeof(cmd->cb));
514
515         while (imap->literal_pending)
516                 get_cmd_result(ctx, NULL);
517
518         if (!cmd->cb.data)
519                 bufl = nfsnprintf(buf, sizeof(buf), "%d %s\r\n", cmd->tag, cmd->cmd);
520         else
521                 bufl = nfsnprintf(buf, sizeof(buf), "%d %s{%d%s}\r\n",
522                                   cmd->tag, cmd->cmd, cmd->cb.dlen,
523                                   CAP(LITERALPLUS) ? "+" : "");
524
525         if (Verbose) {
526                 if (imap->num_in_progress)
527                         printf("(%d in progress) ", imap->num_in_progress);
528                 if (!starts_with(cmd->cmd, "LOGIN"))
529                         printf(">>> %s", buf);
530                 else
531                         printf(">>> %d LOGIN <user> <pass>\n", cmd->tag);
532         }
533         if (socket_write(&imap->buf.sock, buf, bufl) != bufl) {
534                 free(cmd->cmd);
535                 free(cmd);
536                 if (cb)
537                         free(cb->data);
538                 return NULL;
539         }
540         if (cmd->cb.data) {
541                 if (CAP(LITERALPLUS)) {
542                         n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen);
543                         free(cmd->cb.data);
544                         if (n != cmd->cb.dlen ||
545                             socket_write(&imap->buf.sock, "\r\n", 2) != 2) {
546                                 free(cmd->cmd);
547                                 free(cmd);
548                                 return NULL;
549                         }
550                         cmd->cb.data = NULL;
551                 } else
552                         imap->literal_pending = 1;
553         } else if (cmd->cb.cont)
554                 imap->literal_pending = 1;
555         cmd->next = NULL;
556         *imap->in_progress_append = cmd;
557         imap->in_progress_append = &cmd->next;
558         imap->num_in_progress++;
559         return cmd;
560 }
561
562 __attribute__((format (printf, 3, 4)))
563 static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,
564                      const char *fmt, ...)
565 {
566         va_list ap;
567         struct imap_cmd *cmdp;
568
569         va_start(ap, fmt);
570         cmdp = issue_imap_cmd(ctx, cb, fmt, ap);
571         va_end(ap);
572         if (!cmdp)
573                 return RESP_BAD;
574
575         return get_cmd_result(ctx, cmdp);
576 }
577
578 __attribute__((format (printf, 3, 4)))
579 static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb,
580                        const char *fmt, ...)
581 {
582         va_list ap;
583         struct imap_cmd *cmdp;
584
585         va_start(ap, fmt);
586         cmdp = issue_imap_cmd(ctx, cb, fmt, ap);
587         va_end(ap);
588         if (!cmdp)
589                 return DRV_STORE_BAD;
590
591         switch (get_cmd_result(ctx, cmdp)) {
592         case RESP_BAD: return DRV_STORE_BAD;
593         case RESP_NO: return DRV_MSG_BAD;
594         default: return DRV_OK;
595         }
596 }
597
598 static int skip_imap_list_l(char **sp, int level)
599 {
600         char *s = *sp;
601
602         for (;;) {
603                 while (isspace((unsigned char)*s))
604                         s++;
605                 if (level && *s == ')') {
606                         s++;
607                         break;
608                 }
609                 if (*s == '(') {
610                         /* sublist */
611                         s++;
612                         if (skip_imap_list_l(&s, level + 1))
613                                 goto bail;
614                 } else if (*s == '"') {
615                         /* quoted string */
616                         s++;
617                         for (; *s != '"'; s++)
618                                 if (!*s)
619                                         goto bail;
620                         s++;
621                 } else {
622                         /* atom */
623                         for (; *s && !isspace((unsigned char)*s); s++)
624                                 if (level && *s == ')')
625                                         break;
626                 }
627
628                 if (!level)
629                         break;
630                 if (!*s)
631                         goto bail;
632         }
633         *sp = s;
634         return 0;
635
636 bail:
637         return -1;
638 }
639
640 static void skip_list(char **sp)
641 {
642         skip_imap_list_l(sp, 0);
643 }
644
645 static void parse_capability(struct imap *imap, char *cmd)
646 {
647         char *arg;
648         unsigned i;
649
650         imap->caps = 0x80000000;
651         while ((arg = next_arg(&cmd)))
652                 for (i = 0; i < ARRAY_SIZE(cap_list); i++)
653                         if (!strcmp(cap_list[i], arg))
654                                 imap->caps |= 1 << i;
655         imap->rcaps = imap->caps;
656 }
657
658 static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb,
659                                char *s)
660 {
661         struct imap *imap = ctx->imap;
662         char *arg, *p;
663
664         if (*s != '[')
665                 return RESP_OK;         /* no response code */
666         s++;
667         if (!(p = strchr(s, ']'))) {
668                 fprintf(stderr, "IMAP error: malformed response code\n");
669                 return RESP_BAD;
670         }
671         *p++ = 0;
672         arg = next_arg(&s);
673         if (!strcmp("UIDVALIDITY", arg)) {
674                 if (!(arg = next_arg(&s)) || !(ctx->uidvalidity = atoi(arg))) {
675                         fprintf(stderr, "IMAP error: malformed UIDVALIDITY status\n");
676                         return RESP_BAD;
677                 }
678         } else if (!strcmp("UIDNEXT", arg)) {
679                 if (!(arg = next_arg(&s)) || !(imap->uidnext = atoi(arg))) {
680                         fprintf(stderr, "IMAP error: malformed NEXTUID status\n");
681                         return RESP_BAD;
682                 }
683         } else if (!strcmp("CAPABILITY", arg)) {
684                 parse_capability(imap, s);
685         } else if (!strcmp("ALERT", arg)) {
686                 /* RFC2060 says that these messages MUST be displayed
687                  * to the user
688                  */
689                 for (; isspace((unsigned char)*p); p++);
690                 fprintf(stderr, "*** IMAP ALERT *** %s\n", p);
691         } else if (cb && cb->ctx && !strcmp("APPENDUID", arg)) {
692                 if (!(arg = next_arg(&s)) || !(ctx->uidvalidity = atoi(arg)) ||
693                     !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg))) {
694                         fprintf(stderr, "IMAP error: malformed APPENDUID status\n");
695                         return RESP_BAD;
696                 }
697         }
698         return RESP_OK;
699 }
700
701 static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
702 {
703         struct imap *imap = ctx->imap;
704         struct imap_cmd *cmdp, **pcmdp;
705         char *cmd, *arg, *arg1;
706         int n, resp, resp2, tag;
707
708         for (;;) {
709                 if (buffer_gets(&imap->buf, &cmd))
710                         return RESP_BAD;
711
712                 arg = next_arg(&cmd);
713                 if (*arg == '*') {
714                         arg = next_arg(&cmd);
715                         if (!arg) {
716                                 fprintf(stderr, "IMAP error: unable to parse untagged response\n");
717                                 return RESP_BAD;
718                         }
719
720                         if (!strcmp("NAMESPACE", arg)) {
721                                 /* rfc2342 NAMESPACE response. */
722                                 skip_list(&cmd); /* Personal mailboxes */
723                                 skip_list(&cmd); /* Others' mailboxes */
724                                 skip_list(&cmd); /* Shared mailboxes */
725                         } else if (!strcmp("OK", arg) || !strcmp("BAD", arg) ||
726                                    !strcmp("NO", arg) || !strcmp("BYE", arg)) {
727                                 if ((resp = parse_response_code(ctx, NULL, cmd)) != RESP_OK)
728                                         return resp;
729                         } else if (!strcmp("CAPABILITY", arg)) {
730                                 parse_capability(imap, cmd);
731                         } else if ((arg1 = next_arg(&cmd))) {
732                                 ; /*
733                                    * Unhandled response-data with at least two words.
734                                    * Ignore it.
735                                    *
736                                    * NEEDSWORK: Previously this case handled '<num> EXISTS'
737                                    * and '<num> RECENT' but as a probably-unintended side
738                                    * effect it ignores other unrecognized two-word
739                                    * responses.  imap-send doesn't ever try to read
740                                    * messages or mailboxes these days, so consider
741                                    * eliminating this case.
742                                    */
743                         } else {
744                                 fprintf(stderr, "IMAP error: unable to parse untagged response\n");
745                                 return RESP_BAD;
746                         }
747                 } else if (!imap->in_progress) {
748                         fprintf(stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "");
749                         return RESP_BAD;
750                 } else if (*arg == '+') {
751                         /* This can happen only with the last command underway, as
752                            it enforces a round-trip. */
753                         cmdp = (struct imap_cmd *)((char *)imap->in_progress_append -
754                                offsetof(struct imap_cmd, next));
755                         if (cmdp->cb.data) {
756                                 n = socket_write(&imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen);
757                                 free(cmdp->cb.data);
758                                 cmdp->cb.data = NULL;
759                                 if (n != (int)cmdp->cb.dlen)
760                                         return RESP_BAD;
761                         } else if (cmdp->cb.cont) {
762                                 if (cmdp->cb.cont(ctx, cmdp, cmd))
763                                         return RESP_BAD;
764                         } else {
765                                 fprintf(stderr, "IMAP error: unexpected command continuation request\n");
766                                 return RESP_BAD;
767                         }
768                         if (socket_write(&imap->buf.sock, "\r\n", 2) != 2)
769                                 return RESP_BAD;
770                         if (!cmdp->cb.cont)
771                                 imap->literal_pending = 0;
772                         if (!tcmd)
773                                 return DRV_OK;
774                 } else {
775                         tag = atoi(arg);
776                         for (pcmdp = &imap->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
777                                 if (cmdp->tag == tag)
778                                         goto gottag;
779                         fprintf(stderr, "IMAP error: unexpected tag %s\n", arg);
780                         return RESP_BAD;
781                 gottag:
782                         if (!(*pcmdp = cmdp->next))
783                                 imap->in_progress_append = pcmdp;
784                         imap->num_in_progress--;
785                         if (cmdp->cb.cont || cmdp->cb.data)
786                                 imap->literal_pending = 0;
787                         arg = next_arg(&cmd);
788                         if (!strcmp("OK", arg))
789                                 resp = DRV_OK;
790                         else {
791                                 if (!strcmp("NO", arg))
792                                         resp = RESP_NO;
793                                 else /*if (!strcmp("BAD", arg))*/
794                                         resp = RESP_BAD;
795                                 fprintf(stderr, "IMAP command '%s' returned response (%s) - %s\n",
796                                         !starts_with(cmdp->cmd, "LOGIN") ?
797                                                         cmdp->cmd : "LOGIN <user> <pass>",
798                                                         arg, cmd ? cmd : "");
799                         }
800                         if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp)
801                                 resp = resp2;
802                         if (cmdp->cb.done)
803                                 cmdp->cb.done(ctx, cmdp, resp);
804                         free(cmdp->cb.data);
805                         free(cmdp->cmd);
806                         free(cmdp);
807                         if (!tcmd || tcmd == cmdp)
808                                 return resp;
809                 }
810         }
811         /* not reached */
812 }
813
814 static void imap_close_server(struct imap_store *ictx)
815 {
816         struct imap *imap = ictx->imap;
817
818         if (imap->buf.sock.fd[0] != -1) {
819                 imap_exec(ictx, NULL, "LOGOUT");
820                 socket_shutdown(&imap->buf.sock);
821         }
822         free(imap);
823 }
824
825 static void imap_close_store(struct imap_store *ctx)
826 {
827         imap_close_server(ctx);
828         free(ctx);
829 }
830
831 #ifndef NO_OPENSSL
832
833 /*
834  * hexchar() and cram() functions are based on the code from the isync
835  * project (http://isync.sf.net/).
836  */
837 static char hexchar(unsigned int b)
838 {
839         return b < 10 ? '0' + b : 'a' + (b - 10);
840 }
841
842 #define ENCODED_SIZE(n) (4*((n+2)/3))
843 static char *cram(const char *challenge_64, const char *user, const char *pass)
844 {
845         int i, resp_len, encoded_len, decoded_len;
846         HMAC_CTX hmac;
847         unsigned char hash[16];
848         char hex[33];
849         char *response, *response_64, *challenge;
850
851         /*
852          * length of challenge_64 (i.e. base-64 encoded string) is a good
853          * enough upper bound for challenge (decoded result).
854          */
855         encoded_len = strlen(challenge_64);
856         challenge = xmalloc(encoded_len);
857         decoded_len = EVP_DecodeBlock((unsigned char *)challenge,
858                                       (unsigned char *)challenge_64, encoded_len);
859         if (decoded_len < 0)
860                 die("invalid challenge %s", challenge_64);
861         HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
862         HMAC_Update(&hmac, (unsigned char *)challenge, decoded_len);
863         HMAC_Final(&hmac, hash, NULL);
864         HMAC_CTX_cleanup(&hmac);
865
866         hex[32] = 0;
867         for (i = 0; i < 16; i++) {
868                 hex[2 * i] = hexchar((hash[i] >> 4) & 0xf);
869                 hex[2 * i + 1] = hexchar(hash[i] & 0xf);
870         }
871
872         /* response: "<user> <digest in hex>" */
873         resp_len = strlen(user) + 1 + strlen(hex) + 1;
874         response = xmalloc(resp_len);
875         sprintf(response, "%s %s", user, hex);
876
877         response_64 = xmalloc(ENCODED_SIZE(resp_len) + 1);
878         encoded_len = EVP_EncodeBlock((unsigned char *)response_64,
879                                       (unsigned char *)response, resp_len);
880         if (encoded_len < 0)
881                 die("EVP_EncodeBlock error");
882         response_64[encoded_len] = '\0';
883         return (char *)response_64;
884 }
885
886 #else
887
888 static char *cram(const char *challenge_64, const char *user, const char *pass)
889 {
890         die("If you want to use CRAM-MD5 authenticate method, "
891             "you have to build git-imap-send with OpenSSL library.");
892 }
893
894 #endif
895
896 static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt)
897 {
898         int ret;
899         char *response;
900
901         response = cram(prompt, server.user, server.pass);
902
903         ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
904         if (ret != strlen(response))
905                 return error("IMAP error: sending response failed");
906
907         free(response);
908
909         return 0;
910 }
911
912 static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *folder)
913 {
914         struct credential cred = CREDENTIAL_INIT;
915         struct imap_store *ctx;
916         struct imap *imap;
917         char *arg, *rsp;
918         int s = -1, preauth;
919
920         ctx = xcalloc(1, sizeof(*ctx));
921
922         ctx->imap = imap = xcalloc(1, sizeof(*imap));
923         imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
924         imap->in_progress_append = &imap->in_progress;
925
926         /* open connection to IMAP server */
927
928         if (srvc->tunnel) {
929                 struct child_process tunnel = CHILD_PROCESS_INIT;
930
931                 imap_info("Starting tunnel '%s'... ", srvc->tunnel);
932
933                 argv_array_push(&tunnel.args, srvc->tunnel);
934                 tunnel.use_shell = 1;
935                 tunnel.in = -1;
936                 tunnel.out = -1;
937                 if (start_command(&tunnel))
938                         die("cannot start proxy %s", srvc->tunnel);
939
940                 imap->buf.sock.fd[0] = tunnel.out;
941                 imap->buf.sock.fd[1] = tunnel.in;
942
943                 imap_info("ok\n");
944         } else {
945 #ifndef NO_IPV6
946                 struct addrinfo hints, *ai0, *ai;
947                 int gai;
948                 char portstr[6];
949
950                 snprintf(portstr, sizeof(portstr), "%d", srvc->port);
951
952                 memset(&hints, 0, sizeof(hints));
953                 hints.ai_socktype = SOCK_STREAM;
954                 hints.ai_protocol = IPPROTO_TCP;
955
956                 imap_info("Resolving %s... ", srvc->host);
957                 gai = getaddrinfo(srvc->host, portstr, &hints, &ai);
958                 if (gai) {
959                         fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
960                         goto bail;
961                 }
962                 imap_info("ok\n");
963
964                 for (ai0 = ai; ai; ai = ai->ai_next) {
965                         char addr[NI_MAXHOST];
966
967                         s = socket(ai->ai_family, ai->ai_socktype,
968                                    ai->ai_protocol);
969                         if (s < 0)
970                                 continue;
971
972                         getnameinfo(ai->ai_addr, ai->ai_addrlen, addr,
973                                     sizeof(addr), NULL, 0, NI_NUMERICHOST);
974                         imap_info("Connecting to [%s]:%s... ", addr, portstr);
975
976                         if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
977                                 close(s);
978                                 s = -1;
979                                 perror("connect");
980                                 continue;
981                         }
982
983                         break;
984                 }
985                 freeaddrinfo(ai0);
986 #else /* NO_IPV6 */
987                 struct hostent *he;
988                 struct sockaddr_in addr;
989
990                 memset(&addr, 0, sizeof(addr));
991                 addr.sin_port = htons(srvc->port);
992                 addr.sin_family = AF_INET;
993
994                 imap_info("Resolving %s... ", srvc->host);
995                 he = gethostbyname(srvc->host);
996                 if (!he) {
997                         perror("gethostbyname");
998                         goto bail;
999                 }
1000                 imap_info("ok\n");
1001
1002                 addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
1003
1004                 s = socket(PF_INET, SOCK_STREAM, 0);
1005
1006                 imap_info("Connecting to %s:%hu... ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
1007                 if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
1008                         close(s);
1009                         s = -1;
1010                         perror("connect");
1011                 }
1012 #endif
1013                 if (s < 0) {
1014                         fputs("Error: unable to connect to server.\n", stderr);
1015                         goto bail;
1016                 }
1017
1018                 imap->buf.sock.fd[0] = s;
1019                 imap->buf.sock.fd[1] = dup(s);
1020
1021                 if (srvc->use_ssl &&
1022                     ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
1023                         close(s);
1024                         goto bail;
1025                 }
1026                 imap_info("ok\n");
1027         }
1028
1029         /* read the greeting string */
1030         if (buffer_gets(&imap->buf, &rsp)) {
1031                 fprintf(stderr, "IMAP error: no greeting response\n");
1032                 goto bail;
1033         }
1034         arg = next_arg(&rsp);
1035         if (!arg || *arg != '*' || (arg = next_arg(&rsp)) == NULL) {
1036                 fprintf(stderr, "IMAP error: invalid greeting response\n");
1037                 goto bail;
1038         }
1039         preauth = 0;
1040         if (!strcmp("PREAUTH", arg))
1041                 preauth = 1;
1042         else if (strcmp("OK", arg) != 0) {
1043                 fprintf(stderr, "IMAP error: unknown greeting response\n");
1044                 goto bail;
1045         }
1046         parse_response_code(ctx, NULL, rsp);
1047         if (!imap->caps && imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
1048                 goto bail;
1049
1050         if (!preauth) {
1051 #ifndef NO_OPENSSL
1052                 if (!srvc->use_ssl && CAP(STARTTLS)) {
1053                         if (imap_exec(ctx, NULL, "STARTTLS") != RESP_OK)
1054                                 goto bail;
1055                         if (ssl_socket_connect(&imap->buf.sock, 1,
1056                                                srvc->ssl_verify))
1057                                 goto bail;
1058                         /* capabilities may have changed, so get the new capabilities */
1059                         if (imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
1060                                 goto bail;
1061                 }
1062 #endif
1063                 imap_info("Logging in...\n");
1064                 if (!srvc->user || !srvc->pass) {
1065                         cred.protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
1066                         cred.host = xstrdup(srvc->host);
1067
1068                         if (srvc->user)
1069                                 cred.username = xstrdup(srvc->user);
1070                         if (srvc->pass)
1071                                 cred.password = xstrdup(srvc->pass);
1072
1073                         credential_fill(&cred);
1074
1075                         if (!srvc->user)
1076                                 srvc->user = xstrdup(cred.username);
1077                         if (!srvc->pass)
1078                                 srvc->pass = xstrdup(cred.password);
1079                 }
1080
1081                 if (CAP(NOLOGIN)) {
1082                         fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host);
1083                         goto bail;
1084                 }
1085
1086                 if (srvc->auth_method) {
1087                         struct imap_cmd_cb cb;
1088
1089                         if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
1090                                 if (!CAP(AUTH_CRAM_MD5)) {
1091                                         fprintf(stderr, "You specified"
1092                                                 "CRAM-MD5 as authentication method, "
1093                                                 "but %s doesn't support it.\n", srvc->host);
1094                                         goto bail;
1095                                 }
1096                                 /* CRAM-MD5 */
1097
1098                                 memset(&cb, 0, sizeof(cb));
1099                                 cb.cont = auth_cram_md5;
1100                                 if (imap_exec(ctx, &cb, "AUTHENTICATE CRAM-MD5") != RESP_OK) {
1101                                         fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
1102                                         goto bail;
1103                                 }
1104                         } else {
1105                                 fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
1106                                 goto bail;
1107                         }
1108                 } else {
1109                         if (!imap->buf.sock.ssl)
1110                                 imap_warn("*** IMAP Warning *** Password is being "
1111                                           "sent in the clear\n");
1112                         if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
1113                                 fprintf(stderr, "IMAP error: LOGIN failed\n");
1114                                 goto bail;
1115                         }
1116                 }
1117         } /* !preauth */
1118
1119         if (cred.username)
1120                 credential_approve(&cred);
1121         credential_clear(&cred);
1122
1123         /* check the target mailbox exists */
1124         ctx->name = folder;
1125         switch (imap_exec(ctx, NULL, "EXAMINE \"%s\"", ctx->name)) {
1126         case RESP_OK:
1127                 /* ok */
1128                 break;
1129         case RESP_BAD:
1130                 fprintf(stderr, "IMAP error: could not check mailbox\n");
1131                 goto out;
1132         case RESP_NO:
1133                 if (imap_exec(ctx, NULL, "CREATE \"%s\"", ctx->name) == RESP_OK) {
1134                         imap_info("Created missing mailbox\n");
1135                 } else {
1136                         fprintf(stderr, "IMAP error: could not create missing mailbox\n");
1137                         goto out;
1138                 }
1139                 break;
1140         }
1141
1142         ctx->prefix = "";
1143         return ctx;
1144
1145 bail:
1146         if (cred.username)
1147                 credential_reject(&cred);
1148         credential_clear(&cred);
1149
1150  out:
1151         imap_close_store(ctx);
1152         return NULL;
1153 }
1154
1155 /*
1156  * Insert CR characters as necessary in *msg to ensure that every LF
1157  * character in *msg is preceded by a CR.
1158  */
1159 static void lf_to_crlf(struct strbuf *msg)
1160 {
1161         char *new;
1162         size_t i, j;
1163         char lastc;
1164
1165         /* First pass: tally, in j, the size of the new string: */
1166         for (i = j = 0, lastc = '\0'; i < msg->len; i++) {
1167                 if (msg->buf[i] == '\n' && lastc != '\r')
1168                         j++; /* a CR will need to be added here */
1169                 lastc = msg->buf[i];
1170                 j++;
1171         }
1172
1173         new = xmalloc(j + 1);
1174
1175         /*
1176          * Second pass: write the new string.  Note that this loop is
1177          * otherwise identical to the first pass.
1178          */
1179         for (i = j = 0, lastc = '\0'; i < msg->len; i++) {
1180                 if (msg->buf[i] == '\n' && lastc != '\r')
1181                         new[j++] = '\r';
1182                 lastc = new[j++] = msg->buf[i];
1183         }
1184         strbuf_attach(msg, new, j, j + 1);
1185 }
1186
1187 /*
1188  * Store msg to IMAP.  Also detach and free the data from msg->data,
1189  * leaving msg->data empty.
1190  */
1191 static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)
1192 {
1193         struct imap *imap = ctx->imap;
1194         struct imap_cmd_cb cb;
1195         const char *prefix, *box;
1196         int ret;
1197
1198         lf_to_crlf(msg);
1199         memset(&cb, 0, sizeof(cb));
1200
1201         cb.dlen = msg->len;
1202         cb.data = strbuf_detach(msg, NULL);
1203
1204         box = ctx->name;
1205         prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
1206         ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" ", prefix, box);
1207         imap->caps = imap->rcaps;
1208         if (ret != DRV_OK)
1209                 return ret;
1210
1211         return DRV_OK;
1212 }
1213
1214 static void wrap_in_html(struct strbuf *msg)
1215 {
1216         struct strbuf buf = STRBUF_INIT;
1217         static char *content_type = "Content-Type: text/html;\n";
1218         static char *pre_open = "<pre>\n";
1219         static char *pre_close = "</pre>\n";
1220         const char *body = strstr(msg->buf, "\n\n");
1221
1222         if (!body)
1223                 return; /* Headers but no body; no wrapping needed */
1224
1225         body += 2;
1226
1227         strbuf_add(&buf, msg->buf, body - msg->buf - 1);
1228         strbuf_addstr(&buf, content_type);
1229         strbuf_addch(&buf, '\n');
1230         strbuf_addstr(&buf, pre_open);
1231         strbuf_addstr_xml_quoted(&buf, body);
1232         strbuf_addstr(&buf, pre_close);
1233
1234         strbuf_release(msg);
1235         *msg = buf;
1236 }
1237
1238 #define CHUNKSIZE 0x1000
1239
1240 static int read_message(FILE *f, struct strbuf *all_msgs)
1241 {
1242         do {
1243                 if (strbuf_fread(all_msgs, CHUNKSIZE, f) <= 0)
1244                         break;
1245         } while (!feof(f));
1246
1247         return ferror(f) ? -1 : 0;
1248 }
1249
1250 static int count_messages(struct strbuf *all_msgs)
1251 {
1252         int count = 0;
1253         char *p = all_msgs->buf;
1254
1255         while (1) {
1256                 if (starts_with(p, "From ")) {
1257                         p = strstr(p+5, "\nFrom: ");
1258                         if (!p) break;
1259                         p = strstr(p+7, "\nDate: ");
1260                         if (!p) break;
1261                         p = strstr(p+7, "\nSubject: ");
1262                         if (!p) break;
1263                         p += 10;
1264                         count++;
1265                 }
1266                 p = strstr(p+5, "\nFrom ");
1267                 if (!p)
1268                         break;
1269                 p++;
1270         }
1271         return count;
1272 }
1273
1274 /*
1275  * Copy the next message from all_msgs, starting at offset *ofs, to
1276  * msg.  Update *ofs to the start of the following message.  Return
1277  * true iff a message was successfully copied.
1278  */
1279 static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
1280 {
1281         char *p, *data;
1282         size_t len;
1283
1284         if (*ofs >= all_msgs->len)
1285                 return 0;
1286
1287         data = &all_msgs->buf[*ofs];
1288         len = all_msgs->len - *ofs;
1289
1290         if (len < 5 || !starts_with(data, "From "))
1291                 return 0;
1292
1293         p = strchr(data, '\n');
1294         if (p) {
1295                 p++;
1296                 len -= p - data;
1297                 *ofs += p - data;
1298                 data = p;
1299         }
1300
1301         p = strstr(data, "\nFrom ");
1302         if (p)
1303                 len = &p[1] - data;
1304
1305         strbuf_add(msg, data, len);
1306         *ofs += len;
1307         return 1;
1308 }
1309
1310 static void git_imap_config(void)
1311 {
1312         const char *val = NULL;
1313
1314         git_config_get_bool("imap.sslverify", &server.ssl_verify);
1315         git_config_get_bool("imap.preformattedhtml", &server.use_html);
1316         git_config_get_string("imap.folder", &server.folder);
1317
1318         if (!git_config_get_value("imap.host", &val)) {
1319                 if (!val) {
1320                         git_die_config("imap.host", "Missing value for 'imap.host'");
1321                 } else {
1322                         if (starts_with(val, "imap:"))
1323                                 val += 5;
1324                         else if (starts_with(val, "imaps:")) {
1325                                 val += 6;
1326                                 server.use_ssl = 1;
1327                         }
1328                         if (starts_with(val, "//"))
1329                                 val += 2;
1330                         server.host = xstrdup(val);
1331                 }
1332         }
1333
1334         git_config_get_string("imap.user", &server.user);
1335         git_config_get_string("imap.pass", &server.pass);
1336         git_config_get_int("imap.port", &server.port);
1337         git_config_get_string("imap.tunnel", &server.tunnel);
1338         git_config_get_string("imap.authmethod", &server.auth_method);
1339 }
1340
1341 int main(int argc, char **argv)
1342 {
1343         struct strbuf all_msgs = STRBUF_INIT;
1344         struct strbuf msg = STRBUF_INIT;
1345         struct imap_store *ctx = NULL;
1346         int ofs = 0;
1347         int r;
1348         int total, n = 0;
1349         int nongit_ok;
1350
1351         git_extract_argv0_path(argv[0]);
1352
1353         git_setup_gettext();
1354
1355         if (argc != 1)
1356                 usage(imap_send_usage);
1357
1358         setup_git_directory_gently(&nongit_ok);
1359         git_imap_config();
1360
1361         if (!server.port)
1362                 server.port = server.use_ssl ? 993 : 143;
1363
1364         if (!server.folder) {
1365                 fprintf(stderr, "no imap store specified\n");
1366                 return 1;
1367         }
1368         if (!server.host) {
1369                 if (!server.tunnel) {
1370                         fprintf(stderr, "no imap host specified\n");
1371                         return 1;
1372                 }
1373                 server.host = "tunnel";
1374         }
1375
1376         /* read the messages */
1377         if (read_message(stdin, &all_msgs)) {
1378                 fprintf(stderr, "error reading input\n");
1379                 return 1;
1380         }
1381
1382         if (all_msgs.len == 0) {
1383                 fprintf(stderr, "nothing to send\n");
1384                 return 1;
1385         }
1386
1387         total = count_messages(&all_msgs);
1388         if (!total) {
1389                 fprintf(stderr, "no messages to send\n");
1390                 return 1;
1391         }
1392
1393         /* write it to the imap server */
1394         ctx = imap_open_store(&server, server.folder);
1395         if (!ctx) {
1396                 fprintf(stderr, "failed to open store\n");
1397                 return 1;
1398         }
1399
1400         fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
1401         while (1) {
1402                 unsigned percent = n * 100 / total;
1403
1404                 fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
1405                 if (!split_msg(&all_msgs, &msg, &ofs))
1406                         break;
1407                 if (server.use_html)
1408                         wrap_in_html(&msg);
1409                 r = imap_store_msg(ctx, &msg);
1410                 if (r != DRV_OK)
1411                         break;
1412                 n++;
1413         }
1414         fprintf(stderr, "\n");
1415
1416         imap_close_store(ctx);
1417
1418         return 0;
1419 }