Merge branch 'mw/send-email'
[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
27 typedef struct store_conf {
28         char *name;
29         const char *path; /* should this be here? its interpretation is driver-specific */
30         char *map_inbox;
31         char *trash;
32         unsigned max_size; /* off_t is overkill */
33         unsigned trash_remote_new:1, trash_only_new:1;
34 } store_conf_t;
35
36 typedef struct string_list {
37         struct string_list *next;
38         char string[1];
39 } string_list_t;
40
41 typedef struct channel_conf {
42         struct channel_conf *next;
43         char *name;
44         store_conf_t *master, *slave;
45         char *master_name, *slave_name;
46         char *sync_state;
47         string_list_t *patterns;
48         int mops, sops;
49         unsigned max_messages; /* for slave only */
50 } channel_conf_t;
51
52 typedef struct group_conf {
53         struct group_conf *next;
54         char *name;
55         string_list_t *channels;
56 } group_conf_t;
57
58 /* For message->status */
59 #define M_RECENT       (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
60 #define M_DEAD         (1<<1) /* expunged */
61 #define M_FLAGS        (1<<2) /* flags fetched */
62
63 typedef struct message {
64         struct message *next;
65         /* string_list_t *keywords; */
66         size_t size; /* zero implies "not fetched" */
67         int uid;
68         unsigned char flags, status;
69 } message_t;
70
71 typedef struct store {
72         store_conf_t *conf; /* foreign */
73
74         /* currently open mailbox */
75         const char *name; /* foreign! maybe preset? */
76         char *path; /* own */
77         message_t *msgs; /* own */
78         int uidvalidity;
79         unsigned char opts; /* maybe preset? */
80         /* note that the following do _not_ reflect stats from msgs, but mailbox totals */
81         int count; /* # of messages */
82         int recent; /* # of recent messages - don't trust this beyond the initial read */
83 } store_t;
84
85 typedef struct {
86         char *data;
87         int len;
88         unsigned char flags;
89         unsigned int crlf:1;
90 } msg_data_t;
91
92 #define DRV_OK          0
93 #define DRV_MSG_BAD     -1
94 #define DRV_BOX_BAD     -2
95 #define DRV_STORE_BAD   -3
96
97 static int Verbose, Quiet;
98
99 static void imap_info( const char *, ... );
100 static void imap_warn( const char *, ... );
101
102 static char *next_arg( char ** );
103
104 static void free_generic_messages( message_t * );
105
106 static int nfsnprintf( char *buf, int blen, const char *fmt, ... );
107
108 static int nfvasprintf(char **strp, const char *fmt, va_list ap)
109 {
110         int len;
111         char tmp[8192];
112
113         len = vsnprintf(tmp, sizeof(tmp), fmt, ap);
114         if (len < 0)
115                 die("Fatal: Out of memory\n");
116         if (len >= sizeof(tmp))
117                 die("imap command overflow !\n");
118         *strp = xmemdupz(tmp, len);
119         return len;
120 }
121
122 static void arc4_init( void );
123 static unsigned char arc4_getbyte( void );
124
125 typedef struct imap_server_conf {
126         char *name;
127         char *tunnel;
128         char *host;
129         int port;
130         char *user;
131         char *pass;
132 } imap_server_conf_t;
133
134 typedef struct imap_store_conf {
135         store_conf_t gen;
136         imap_server_conf_t *server;
137         unsigned use_namespace:1;
138 } imap_store_conf_t;
139
140 #define NIL     (void*)0x1
141 #define LIST    (void*)0x2
142
143 typedef struct _list {
144         struct _list *next, *child;
145         char *val;
146         int len;
147 } list_t;
148
149 typedef struct {
150         int fd;
151 } Socket_t;
152
153 typedef struct {
154         Socket_t sock;
155         int bytes;
156         int offset;
157         char buf[1024];
158 } buffer_t;
159
160 struct imap_cmd;
161
162 typedef struct imap {
163         int uidnext; /* from SELECT responses */
164         list_t *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
165         unsigned caps, rcaps; /* CAPABILITY results */
166         /* command queue */
167         int nexttag, num_in_progress, literal_pending;
168         struct imap_cmd *in_progress, **in_progress_append;
169         buffer_t buf; /* this is BIG, so put it last */
170 } imap_t;
171
172 typedef struct imap_store {
173         store_t gen;
174         int uidvalidity;
175         imap_t *imap;
176         const char *prefix;
177         unsigned /*currentnc:1,*/ trashnc:1;
178 } imap_store_t;
179
180 struct imap_cmd_cb {
181         int (*cont)( imap_store_t *ctx, struct imap_cmd *cmd, const char *prompt );
182         void (*done)( imap_store_t *ctx, struct imap_cmd *cmd, int response);
183         void *ctx;
184         char *data;
185         int dlen;
186         int uid;
187         unsigned create:1, trycreate:1;
188 };
189
190 struct imap_cmd {
191         struct imap_cmd *next;
192         struct imap_cmd_cb cb;
193         char *cmd;
194         int tag;
195 };
196
197 #define CAP(cap) (imap->caps & (1 << (cap)))
198
199 enum CAPABILITY {
200         NOLOGIN = 0,
201         UIDPLUS,
202         LITERALPLUS,
203         NAMESPACE,
204 };
205
206 static const char *cap_list[] = {
207         "LOGINDISABLED",
208         "UIDPLUS",
209         "LITERAL+",
210         "NAMESPACE",
211 };
212
213 #define RESP_OK    0
214 #define RESP_NO    1
215 #define RESP_BAD   2
216
217 static int get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd );
218
219
220 static const char *Flags[] = {
221         "Draft",
222         "Flagged",
223         "Answered",
224         "Seen",
225         "Deleted",
226 };
227
228 static void
229 socket_perror( const char *func, Socket_t *sock, int ret )
230 {
231         if (ret < 0)
232                 perror( func );
233         else
234                 fprintf( stderr, "%s: unexpected EOF\n", func );
235 }
236
237 static int
238 socket_read( Socket_t *sock, char *buf, int len )
239 {
240         ssize_t n = xread( sock->fd, buf, len );
241         if (n <= 0) {
242                 socket_perror( "read", sock, n );
243                 close( sock->fd );
244                 sock->fd = -1;
245         }
246         return n;
247 }
248
249 static int
250 socket_write( Socket_t *sock, const char *buf, int len )
251 {
252         int n = write_in_full( sock->fd, buf, len );
253         if (n != len) {
254                 socket_perror( "write", sock, n );
255                 close( sock->fd );
256                 sock->fd = -1;
257         }
258         return n;
259 }
260
261 /* simple line buffering */
262 static int
263 buffer_gets( buffer_t * b, char **s )
264 {
265         int n;
266         int start = b->offset;
267
268         *s = b->buf + start;
269
270         for (;;) {
271                 /* make sure we have enough data to read the \r\n sequence */
272                 if (b->offset + 1 >= b->bytes) {
273                         if (start) {
274                                 /* shift down used bytes */
275                                 *s = b->buf;
276
277                                 assert( start <= b->bytes );
278                                 n = b->bytes - start;
279
280                                 if (n)
281                                         memmove(b->buf, b->buf + start, n);
282                                 b->offset -= start;
283                                 b->bytes = n;
284                                 start = 0;
285                         }
286
287                         n = socket_read( &b->sock, b->buf + b->bytes,
288                                          sizeof(b->buf) - b->bytes );
289
290                         if (n <= 0)
291                                 return -1;
292
293                         b->bytes += n;
294                 }
295
296                 if (b->buf[b->offset] == '\r') {
297                         assert( b->offset + 1 < b->bytes );
298                         if (b->buf[b->offset + 1] == '\n') {
299                                 b->buf[b->offset] = 0;  /* terminate the string */
300                                 b->offset += 2; /* next line */
301                                 if (Verbose)
302                                         puts( *s );
303                                 return 0;
304                         }
305                 }
306
307                 b->offset++;
308         }
309         /* not reached */
310 }
311
312 static void
313 imap_info( const char *msg, ... )
314 {
315         va_list va;
316
317         if (!Quiet) {
318                 va_start( va, msg );
319                 vprintf( msg, va );
320                 va_end( va );
321                 fflush( stdout );
322         }
323 }
324
325 static void
326 imap_warn( const char *msg, ... )
327 {
328         va_list va;
329
330         if (Quiet < 2) {
331                 va_start( va, msg );
332                 vfprintf( stderr, msg, va );
333                 va_end( va );
334         }
335 }
336
337 static char *
338 next_arg( char **s )
339 {
340         char *ret;
341
342         if (!s || !*s)
343                 return NULL;
344         while (isspace( (unsigned char) **s ))
345                 (*s)++;
346         if (!**s) {
347                 *s = NULL;
348                 return NULL;
349         }
350         if (**s == '"') {
351                 ++*s;
352                 ret = *s;
353                 *s = strchr( *s, '"' );
354         } else {
355                 ret = *s;
356                 while (**s && !isspace( (unsigned char) **s ))
357                         (*s)++;
358         }
359         if (*s) {
360                 if (**s)
361                         *(*s)++ = 0;
362                 if (!**s)
363                         *s = NULL;
364         }
365         return ret;
366 }
367
368 static void
369 free_generic_messages( message_t *msgs )
370 {
371         message_t *tmsg;
372
373         for (; msgs; msgs = tmsg) {
374                 tmsg = msgs->next;
375                 free( msgs );
376         }
377 }
378
379 static int
380 nfsnprintf( char *buf, int blen, const char *fmt, ... )
381 {
382         int ret;
383         va_list va;
384
385         va_start( va, fmt );
386         if (blen <= 0 || (unsigned)(ret = vsnprintf( buf, blen, fmt, va )) >= (unsigned)blen)
387                 die( "Fatal: buffer too small. Please report a bug.\n");
388         va_end( va );
389         return ret;
390 }
391
392 static struct {
393         unsigned char i, j, s[256];
394 } rs;
395
396 static void
397 arc4_init( void )
398 {
399         int i, fd;
400         unsigned char j, si, dat[128];
401
402         if ((fd = open( "/dev/urandom", O_RDONLY )) < 0 && (fd = open( "/dev/random", O_RDONLY )) < 0) {
403                 fprintf( stderr, "Fatal: no random number source available.\n" );
404                 exit( 3 );
405         }
406         if (read_in_full( fd, dat, 128 ) != 128) {
407                 fprintf( stderr, "Fatal: cannot read random number source.\n" );
408                 exit( 3 );
409         }
410         close( fd );
411
412         for (i = 0; i < 256; i++)
413                 rs.s[i] = i;
414         for (i = j = 0; i < 256; i++) {
415                 si = rs.s[i];
416                 j += si + dat[i & 127];
417                 rs.s[i] = rs.s[j];
418                 rs.s[j] = si;
419         }
420         rs.i = rs.j = 0;
421
422         for (i = 0; i < 256; i++)
423                 arc4_getbyte();
424 }
425
426 static unsigned char
427 arc4_getbyte( void )
428 {
429         unsigned char si, sj;
430
431         rs.i++;
432         si = rs.s[rs.i];
433         rs.j += si;
434         sj = rs.s[rs.j];
435         rs.s[rs.i] = sj;
436         rs.s[rs.j] = si;
437         return rs.s[(si + sj) & 0xff];
438 }
439
440 static struct imap_cmd *
441 v_issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb,
442                   const char *fmt, va_list ap )
443 {
444         imap_t *imap = ctx->imap;
445         struct imap_cmd *cmd;
446         int n, bufl;
447         char buf[1024];
448
449         cmd = xmalloc( sizeof(struct imap_cmd) );
450         nfvasprintf( &cmd->cmd, fmt, ap );
451         cmd->tag = ++imap->nexttag;
452
453         if (cb)
454                 cmd->cb = *cb;
455         else
456                 memset( &cmd->cb, 0, sizeof(cmd->cb) );
457
458         while (imap->literal_pending)
459                 get_cmd_result( ctx, NULL );
460
461         bufl = nfsnprintf( buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
462                            "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
463                            cmd->tag, cmd->cmd, cmd->cb.dlen );
464         if (Verbose) {
465                 if (imap->num_in_progress)
466                         printf( "(%d in progress) ", imap->num_in_progress );
467                 if (memcmp( cmd->cmd, "LOGIN", 5 ))
468                         printf( ">>> %s", buf );
469                 else
470                         printf( ">>> %d LOGIN <user> <pass>\n", cmd->tag );
471         }
472         if (socket_write( &imap->buf.sock, buf, bufl ) != bufl) {
473                 free( cmd->cmd );
474                 free( cmd );
475                 if (cb && cb->data)
476                         free( cb->data );
477                 return NULL;
478         }
479         if (cmd->cb.data) {
480                 if (CAP(LITERALPLUS)) {
481                         n = socket_write( &imap->buf.sock, cmd->cb.data, cmd->cb.dlen );
482                         free( cmd->cb.data );
483                         if (n != cmd->cb.dlen ||
484                             (n = socket_write( &imap->buf.sock, "\r\n", 2 )) != 2)
485                         {
486                                 free( cmd->cmd );
487                                 free( cmd );
488                                 return NULL;
489                         }
490                         cmd->cb.data = NULL;
491                 } else
492                         imap->literal_pending = 1;
493         } else if (cmd->cb.cont)
494                 imap->literal_pending = 1;
495         cmd->next = NULL;
496         *imap->in_progress_append = cmd;
497         imap->in_progress_append = &cmd->next;
498         imap->num_in_progress++;
499         return cmd;
500 }
501
502 static struct imap_cmd *
503 issue_imap_cmd( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
504 {
505         struct imap_cmd *ret;
506         va_list ap;
507
508         va_start( ap, fmt );
509         ret = v_issue_imap_cmd( ctx, cb, fmt, ap );
510         va_end( ap );
511         return ret;
512 }
513
514 static int
515 imap_exec( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
516 {
517         va_list ap;
518         struct imap_cmd *cmdp;
519
520         va_start( ap, fmt );
521         cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );
522         va_end( ap );
523         if (!cmdp)
524                 return RESP_BAD;
525
526         return get_cmd_result( ctx, cmdp );
527 }
528
529 static int
530 imap_exec_m( imap_store_t *ctx, struct imap_cmd_cb *cb, const char *fmt, ... )
531 {
532         va_list ap;
533         struct imap_cmd *cmdp;
534
535         va_start( ap, fmt );
536         cmdp = v_issue_imap_cmd( ctx, cb, fmt, ap );
537         va_end( ap );
538         if (!cmdp)
539                 return DRV_STORE_BAD;
540
541         switch (get_cmd_result( ctx, cmdp )) {
542         case RESP_BAD: return DRV_STORE_BAD;
543         case RESP_NO: return DRV_MSG_BAD;
544         default: return DRV_OK;
545         }
546 }
547
548 static int
549 is_atom( list_t *list )
550 {
551         return list && list->val && list->val != NIL && list->val != LIST;
552 }
553
554 static int
555 is_list( list_t *list )
556 {
557         return list && list->val == LIST;
558 }
559
560 static void
561 free_list( list_t *list )
562 {
563         list_t *tmp;
564
565         for (; list; list = tmp) {
566                 tmp = list->next;
567                 if (is_list( list ))
568                         free_list( list->child );
569                 else if (is_atom( list ))
570                         free( list->val );
571                 free( list );
572         }
573 }
574
575 static int
576 parse_imap_list_l( imap_t *imap, char **sp, list_t **curp, int level )
577 {
578         list_t *cur;
579         char *s = *sp, *p;
580         int n, bytes;
581
582         for (;;) {
583                 while (isspace( (unsigned char)*s ))
584                         s++;
585                 if (level && *s == ')') {
586                         s++;
587                         break;
588                 }
589                 *curp = cur = xmalloc( sizeof(*cur) );
590                 curp = &cur->next;
591                 cur->val = NULL; /* for clean bail */
592                 if (*s == '(') {
593                         /* sublist */
594                         s++;
595                         cur->val = LIST;
596                         if (parse_imap_list_l( imap, &s, &cur->child, level + 1 ))
597                                 goto bail;
598                 } else if (imap && *s == '{') {
599                         /* literal */
600                         bytes = cur->len = strtol( s + 1, &s, 10 );
601                         if (*s != '}')
602                                 goto bail;
603
604                         s = cur->val = xmalloc( cur->len );
605
606                         /* dump whats left over in the input buffer */
607                         n = imap->buf.bytes - imap->buf.offset;
608
609                         if (n > bytes)
610                                 /* the entire message fit in the buffer */
611                                 n = bytes;
612
613                         memcpy( s, imap->buf.buf + imap->buf.offset, n );
614                         s += n;
615                         bytes -= n;
616
617                         /* mark that we used part of the buffer */
618                         imap->buf.offset += n;
619
620                         /* now read the rest of the message */
621                         while (bytes > 0) {
622                                 if ((n = socket_read (&imap->buf.sock, s, bytes)) <= 0)
623                                         goto bail;
624                                 s += n;
625                                 bytes -= n;
626                         }
627
628                         if (buffer_gets( &imap->buf, &s ))
629                                 goto bail;
630                 } else if (*s == '"') {
631                         /* quoted string */
632                         s++;
633                         p = s;
634                         for (; *s != '"'; s++)
635                                 if (!*s)
636                                         goto bail;
637                         cur->len = s - p;
638                         s++;
639                         cur->val = xmemdupz(p, cur->len);
640                 } else {
641                         /* atom */
642                         p = s;
643                         for (; *s && !isspace( (unsigned char)*s ); s++)
644                                 if (level && *s == ')')
645                                         break;
646                         cur->len = s - p;
647                         if (cur->len == 3 && !memcmp ("NIL", p, 3)) {
648                                 cur->val = NIL;
649                         } else {
650                                 cur->val = xmemdupz(p, cur->len);
651                         }
652                 }
653
654                 if (!level)
655                         break;
656                 if (!*s)
657                         goto bail;
658         }
659         *sp = s;
660         *curp = NULL;
661         return 0;
662
663   bail:
664         *curp = NULL;
665         return -1;
666 }
667
668 static list_t *
669 parse_imap_list( imap_t *imap, char **sp )
670 {
671         list_t *head;
672
673         if (!parse_imap_list_l( imap, sp, &head, 0 ))
674                 return head;
675         free_list( head );
676         return NULL;
677 }
678
679 static list_t *
680 parse_list( char **sp )
681 {
682         return parse_imap_list( NULL, sp );
683 }
684
685 static void
686 parse_capability( imap_t *imap, char *cmd )
687 {
688         char *arg;
689         unsigned i;
690
691         imap->caps = 0x80000000;
692         while ((arg = next_arg( &cmd )))
693                 for (i = 0; i < ARRAY_SIZE(cap_list); i++)
694                         if (!strcmp( cap_list[i], arg ))
695                                 imap->caps |= 1 << i;
696         imap->rcaps = imap->caps;
697 }
698
699 static int
700 parse_response_code( imap_store_t *ctx, struct imap_cmd_cb *cb, char *s )
701 {
702         imap_t *imap = ctx->imap;
703         char *arg, *p;
704
705         if (*s != '[')
706                 return RESP_OK;         /* no response code */
707         s++;
708         if (!(p = strchr( s, ']' ))) {
709                 fprintf( stderr, "IMAP error: malformed response code\n" );
710                 return RESP_BAD;
711         }
712         *p++ = 0;
713         arg = next_arg( &s );
714         if (!strcmp( "UIDVALIDITY", arg )) {
715                 if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg ))) {
716                         fprintf( stderr, "IMAP error: malformed UIDVALIDITY status\n" );
717                         return RESP_BAD;
718                 }
719         } else if (!strcmp( "UIDNEXT", arg )) {
720                 if (!(arg = next_arg( &s )) || !(imap->uidnext = atoi( arg ))) {
721                         fprintf( stderr, "IMAP error: malformed NEXTUID status\n" );
722                         return RESP_BAD;
723                 }
724         } else if (!strcmp( "CAPABILITY", arg )) {
725                 parse_capability( imap, s );
726         } else if (!strcmp( "ALERT", arg )) {
727                 /* RFC2060 says that these messages MUST be displayed
728                  * to the user
729                  */
730                 for (; isspace( (unsigned char)*p ); p++);
731                 fprintf( stderr, "*** IMAP ALERT *** %s\n", p );
732         } else if (cb && cb->ctx && !strcmp( "APPENDUID", arg )) {
733                 if (!(arg = next_arg( &s )) || !(ctx->gen.uidvalidity = atoi( arg )) ||
734                     !(arg = next_arg( &s )) || !(*(int *)cb->ctx = atoi( arg )))
735                 {
736                         fprintf( stderr, "IMAP error: malformed APPENDUID status\n" );
737                         return RESP_BAD;
738                 }
739         }
740         return RESP_OK;
741 }
742
743 static int
744 get_cmd_result( imap_store_t *ctx, struct imap_cmd *tcmd )
745 {
746         imap_t *imap = ctx->imap;
747         struct imap_cmd *cmdp, **pcmdp, *ncmdp;
748         char *cmd, *arg, *arg1, *p;
749         int n, resp, resp2, tag;
750
751         for (;;) {
752                 if (buffer_gets( &imap->buf, &cmd ))
753                         return RESP_BAD;
754
755                 arg = next_arg( &cmd );
756                 if (*arg == '*') {
757                         arg = next_arg( &cmd );
758                         if (!arg) {
759                                 fprintf( stderr, "IMAP error: unable to parse untagged response\n" );
760                                 return RESP_BAD;
761                         }
762
763                         if (!strcmp( "NAMESPACE", arg )) {
764                                 imap->ns_personal = parse_list( &cmd );
765                                 imap->ns_other = parse_list( &cmd );
766                                 imap->ns_shared = parse_list( &cmd );
767                         } else if (!strcmp( "OK", arg ) || !strcmp( "BAD", arg ) ||
768                                    !strcmp( "NO", arg ) || !strcmp( "BYE", arg )) {
769                                 if ((resp = parse_response_code( ctx, NULL, cmd )) != RESP_OK)
770                                         return resp;
771                         } else if (!strcmp( "CAPABILITY", arg ))
772                                 parse_capability( imap, cmd );
773                         else if ((arg1 = next_arg( &cmd ))) {
774                                 if (!strcmp( "EXISTS", arg1 ))
775                                         ctx->gen.count = atoi( arg );
776                                 else if (!strcmp( "RECENT", arg1 ))
777                                         ctx->gen.recent = atoi( arg );
778                         } else {
779                                 fprintf( stderr, "IMAP error: unable to parse untagged response\n" );
780                                 return RESP_BAD;
781                         }
782                 } else if (!imap->in_progress) {
783                         fprintf( stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "" );
784                         return RESP_BAD;
785                 } else if (*arg == '+') {
786                         /* This can happen only with the last command underway, as
787                            it enforces a round-trip. */
788                         cmdp = (struct imap_cmd *)((char *)imap->in_progress_append -
789                                offsetof(struct imap_cmd, next));
790                         if (cmdp->cb.data) {
791                                 n = socket_write( &imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen );
792                                 free( cmdp->cb.data );
793                                 cmdp->cb.data = NULL;
794                                 if (n != (int)cmdp->cb.dlen)
795                                         return RESP_BAD;
796                         } else if (cmdp->cb.cont) {
797                                 if (cmdp->cb.cont( ctx, cmdp, cmd ))
798                                         return RESP_BAD;
799                         } else {
800                                 fprintf( stderr, "IMAP error: unexpected command continuation request\n" );
801                                 return RESP_BAD;
802                         }
803                         if (socket_write( &imap->buf.sock, "\r\n", 2 ) != 2)
804                                 return RESP_BAD;
805                         if (!cmdp->cb.cont)
806                                 imap->literal_pending = 0;
807                         if (!tcmd)
808                                 return DRV_OK;
809                 } else {
810                         tag = atoi( arg );
811                         for (pcmdp = &imap->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
812                                 if (cmdp->tag == tag)
813                                         goto gottag;
814                         fprintf( stderr, "IMAP error: unexpected tag %s\n", arg );
815                         return RESP_BAD;
816                   gottag:
817                         if (!(*pcmdp = cmdp->next))
818                                 imap->in_progress_append = pcmdp;
819                         imap->num_in_progress--;
820                         if (cmdp->cb.cont || cmdp->cb.data)
821                                 imap->literal_pending = 0;
822                         arg = next_arg( &cmd );
823                         if (!strcmp( "OK", arg ))
824                                 resp = DRV_OK;
825                         else {
826                                 if (!strcmp( "NO", arg )) {
827                                         if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp( cmd, "[TRYCREATE]", 11 ))) { /* SELECT, APPEND or UID COPY */
828                                                 p = strchr( cmdp->cmd, '"' );
829                                                 if (!issue_imap_cmd( ctx, NULL, "CREATE \"%.*s\"", strchr( p + 1, '"' ) - p + 1, p )) {
830                                                         resp = RESP_BAD;
831                                                         goto normal;
832                                                 }
833                                                 /* not waiting here violates the spec, but a server that does not
834                                                    grok this nonetheless violates it too. */
835                                                 cmdp->cb.create = 0;
836                                                 if (!(ncmdp = issue_imap_cmd( ctx, &cmdp->cb, "%s", cmdp->cmd ))) {
837                                                         resp = RESP_BAD;
838                                                         goto normal;
839                                                 }
840                                                 free( cmdp->cmd );
841                                                 free( cmdp );
842                                                 if (!tcmd)
843                                                         return 0;       /* ignored */
844                                                 if (cmdp == tcmd)
845                                                         tcmd = ncmdp;
846                                                 continue;
847                                         }
848                                         resp = RESP_NO;
849                                 } else /*if (!strcmp( "BAD", arg ))*/
850                                         resp = RESP_BAD;
851                                 fprintf( stderr, "IMAP command '%s' returned response (%s) - %s\n",
852                                          memcmp (cmdp->cmd, "LOGIN", 5) ?
853                                                         cmdp->cmd : "LOGIN <user> <pass>",
854                                                         arg, cmd ? cmd : "");
855                         }
856                         if ((resp2 = parse_response_code( ctx, &cmdp->cb, cmd )) > resp)
857                                 resp = resp2;
858                   normal:
859                         if (cmdp->cb.done)
860                                 cmdp->cb.done( ctx, cmdp, resp );
861                         if (cmdp->cb.data)
862                                 free( cmdp->cb.data );
863                         free( cmdp->cmd );
864                         free( cmdp );
865                         if (!tcmd || tcmd == cmdp)
866                                 return resp;
867                 }
868         }
869         /* not reached */
870 }
871
872 static void
873 imap_close_server( imap_store_t *ictx )
874 {
875         imap_t *imap = ictx->imap;
876
877         if (imap->buf.sock.fd != -1) {
878                 imap_exec( ictx, NULL, "LOGOUT" );
879                 close( imap->buf.sock.fd );
880         }
881         free_list( imap->ns_personal );
882         free_list( imap->ns_other );
883         free_list( imap->ns_shared );
884         free( imap );
885 }
886
887 static void
888 imap_close_store( store_t *ctx )
889 {
890         imap_close_server( (imap_store_t *)ctx );
891         free_generic_messages( ctx->msgs );
892         free( ctx );
893 }
894
895 static store_t *
896 imap_open_store( imap_server_conf_t *srvc )
897 {
898         imap_store_t *ctx;
899         imap_t *imap;
900         char *arg, *rsp;
901         struct hostent *he;
902         struct sockaddr_in addr;
903         int s, a[2], preauth;
904         pid_t pid;
905
906         ctx = xcalloc( sizeof(*ctx), 1 );
907
908         ctx->imap = imap = xcalloc( sizeof(*imap), 1 );
909         imap->buf.sock.fd = -1;
910         imap->in_progress_append = &imap->in_progress;
911
912         /* open connection to IMAP server */
913
914         if (srvc->tunnel) {
915                 imap_info( "Starting tunnel '%s'... ", srvc->tunnel );
916
917                 if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) {
918                         perror( "socketpair" );
919                         exit( 1 );
920                 }
921
922                 pid = fork();
923                 if (pid < 0)
924                         _exit( 127 );
925                 if (!pid) {
926                         if (dup2( a[0], 0 ) == -1 || dup2( a[0], 1 ) == -1)
927                                 _exit( 127 );
928                         close( a[0] );
929                         close( a[1] );
930                         execl( "/bin/sh", "sh", "-c", srvc->tunnel, NULL );
931                         _exit( 127 );
932                 }
933
934                 close (a[0]);
935
936                 imap->buf.sock.fd = a[1];
937
938                 imap_info( "ok\n" );
939         } else {
940                 memset( &addr, 0, sizeof(addr) );
941                 addr.sin_port = htons( srvc->port );
942                 addr.sin_family = AF_INET;
943
944                 imap_info( "Resolving %s... ", srvc->host );
945                 he = gethostbyname( srvc->host );
946                 if (!he) {
947                         perror( "gethostbyname" );
948                         goto bail;
949                 }
950                 imap_info( "ok\n" );
951
952                 addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
953
954                 s = socket( PF_INET, SOCK_STREAM, 0 );
955
956                 imap_info( "Connecting to %s:%hu... ", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) );
957                 if (connect( s, (struct sockaddr *)&addr, sizeof(addr) )) {
958                         close( s );
959                         perror( "connect" );
960                         goto bail;
961                 }
962                 imap_info( "ok\n" );
963
964                 imap->buf.sock.fd = s;
965
966         }
967
968         /* read the greeting string */
969         if (buffer_gets( &imap->buf, &rsp )) {
970                 fprintf( stderr, "IMAP error: no greeting response\n" );
971                 goto bail;
972         }
973         arg = next_arg( &rsp );
974         if (!arg || *arg != '*' || (arg = next_arg( &rsp )) == NULL) {
975                 fprintf( stderr, "IMAP error: invalid greeting response\n" );
976                 goto bail;
977         }
978         preauth = 0;
979         if (!strcmp( "PREAUTH", arg ))
980                 preauth = 1;
981         else if (strcmp( "OK", arg ) != 0) {
982                 fprintf( stderr, "IMAP error: unknown greeting response\n" );
983                 goto bail;
984         }
985         parse_response_code( ctx, NULL, rsp );
986         if (!imap->caps && imap_exec( ctx, NULL, "CAPABILITY" ) != RESP_OK)
987                 goto bail;
988
989         if (!preauth) {
990
991                 imap_info ("Logging in...\n");
992                 if (!srvc->user) {
993                         fprintf( stderr, "Skipping server %s, no user\n", srvc->host );
994                         goto bail;
995                 }
996                 if (!srvc->pass) {
997                         char prompt[80];
998                         sprintf( prompt, "Password (%s@%s): ", srvc->user, srvc->host );
999                         arg = getpass( prompt );
1000                         if (!arg) {
1001                                 perror( "getpass" );
1002                                 exit( 1 );
1003                         }
1004                         if (!*arg) {
1005                                 fprintf( stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host );
1006                                 goto bail;
1007                         }
1008                         /*
1009                          * getpass() returns a pointer to a static buffer.  make a copy
1010                          * for long term storage.
1011                          */
1012                         srvc->pass = xstrdup( arg );
1013                 }
1014                 if (CAP(NOLOGIN)) {
1015                         fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host );
1016                         goto bail;
1017                 }
1018                 imap_warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
1019                 if (imap_exec( ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) {
1020                         fprintf( stderr, "IMAP error: LOGIN failed\n" );
1021                         goto bail;
1022                 }
1023         } /* !preauth */
1024
1025         ctx->prefix = "";
1026         ctx->trashnc = 1;
1027         return (store_t *)ctx;
1028
1029   bail:
1030         imap_close_store( &ctx->gen );
1031         return NULL;
1032 }
1033
1034 static int
1035 imap_make_flags( int flags, char *buf )
1036 {
1037         const char *s;
1038         unsigned i, d;
1039
1040         for (i = d = 0; i < ARRAY_SIZE(Flags); i++)
1041                 if (flags & (1 << i)) {
1042                         buf[d++] = ' ';
1043                         buf[d++] = '\\';
1044                         for (s = Flags[i]; *s; s++)
1045                                 buf[d++] = *s;
1046                 }
1047         buf[0] = '(';
1048         buf[d++] = ')';
1049         return d;
1050 }
1051
1052 #define TUIDL 8
1053
1054 static int
1055 imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
1056 {
1057         imap_store_t *ctx = (imap_store_t *)gctx;
1058         imap_t *imap = ctx->imap;
1059         struct imap_cmd_cb cb;
1060         char *fmap, *buf;
1061         const char *prefix, *box;
1062         int ret, i, j, d, len, extra, nocr;
1063         int start, sbreak = 0, ebreak = 0;
1064         char flagstr[128], tuid[TUIDL * 2 + 1];
1065
1066         memset( &cb, 0, sizeof(cb) );
1067
1068         fmap = data->data;
1069         len = data->len;
1070         nocr = !data->crlf;
1071         extra = 0, i = 0;
1072         if (!CAP(UIDPLUS) && uid) {
1073           nloop:
1074                 start = i;
1075                 while (i < len)
1076                         if (fmap[i++] == '\n') {
1077                                 extra += nocr;
1078                                 if (i - 2 + nocr == start) {
1079                                         sbreak = ebreak = i - 2 + nocr;
1080                                         goto mktid;
1081                                 }
1082                                 if (!memcmp( fmap + start, "X-TUID: ", 8 )) {
1083                                         extra -= (ebreak = i) - (sbreak = start) + nocr;
1084                                         goto mktid;
1085                                 }
1086                                 goto nloop;
1087                         }
1088                 /* invalid message */
1089                 free( fmap );
1090                 return DRV_MSG_BAD;
1091          mktid:
1092                 for (j = 0; j < TUIDL; j++)
1093                         sprintf( tuid + j * 2, "%02x", arc4_getbyte() );
1094                 extra += 8 + TUIDL * 2 + 2;
1095         }
1096         if (nocr)
1097                 for (; i < len; i++)
1098                         if (fmap[i] == '\n')
1099                                 extra++;
1100
1101         cb.dlen = len + extra;
1102         buf = cb.data = xmalloc( cb.dlen );
1103         i = 0;
1104         if (!CAP(UIDPLUS) && uid) {
1105                 if (nocr) {
1106                         for (; i < sbreak; i++)
1107                                 if (fmap[i] == '\n') {
1108                                         *buf++ = '\r';
1109                                         *buf++ = '\n';
1110                                 } else
1111                                         *buf++ = fmap[i];
1112                 } else {
1113                         memcpy( buf, fmap, sbreak );
1114                         buf += sbreak;
1115                 }
1116                 memcpy( buf, "X-TUID: ", 8 );
1117                 buf += 8;
1118                 memcpy( buf, tuid, TUIDL * 2 );
1119                 buf += TUIDL * 2;
1120                 *buf++ = '\r';
1121                 *buf++ = '\n';
1122                 i = ebreak;
1123         }
1124         if (nocr) {
1125                 for (; i < len; i++)
1126                         if (fmap[i] == '\n') {
1127                                 *buf++ = '\r';
1128                                 *buf++ = '\n';
1129                         } else
1130                                 *buf++ = fmap[i];
1131         } else
1132                 memcpy( buf, fmap + i, len - i );
1133
1134         free( fmap );
1135
1136         d = 0;
1137         if (data->flags) {
1138                 d = imap_make_flags( data->flags, flagstr );
1139                 flagstr[d++] = ' ';
1140         }
1141         flagstr[d] = 0;
1142
1143         if (!uid) {
1144                 box = gctx->conf->trash;
1145                 prefix = ctx->prefix;
1146                 cb.create = 1;
1147                 if (ctx->trashnc)
1148                         imap->caps = imap->rcaps & ~(1 << LITERALPLUS);
1149         } else {
1150                 box = gctx->name;
1151                 prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix;
1152                 cb.create = 0;
1153         }
1154         cb.ctx = uid;
1155         ret = imap_exec_m( ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr );
1156         imap->caps = imap->rcaps;
1157         if (ret != DRV_OK)
1158                 return ret;
1159         if (!uid)
1160                 ctx->trashnc = 0;
1161         else
1162                 gctx->count++;
1163
1164         return DRV_OK;
1165 }
1166
1167 #define CHUNKSIZE 0x1000
1168
1169 static int
1170 read_message( FILE *f, msg_data_t *msg )
1171 {
1172         struct strbuf buf;
1173
1174         memset(msg, 0, sizeof(*msg));
1175         strbuf_init(&buf, 0);
1176
1177         do {
1178                 if (strbuf_fread(&buf, CHUNKSIZE, f) <= 0)
1179                         break;
1180         } while (!feof(f));
1181
1182         msg->len  = buf.len;
1183         msg->data = strbuf_detach(&buf, NULL);
1184         return msg->len;
1185 }
1186
1187 static int
1188 count_messages( msg_data_t *msg )
1189 {
1190         int count = 0;
1191         char *p = msg->data;
1192
1193         while (1) {
1194                 if (!prefixcmp(p, "From ")) {
1195                         count++;
1196                         p += 5;
1197                 }
1198                 p = strstr( p+5, "\nFrom ");
1199                 if (!p)
1200                         break;
1201                 p++;
1202         }
1203         return count;
1204 }
1205
1206 static int
1207 split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )
1208 {
1209         char *p, *data;
1210
1211         memset( msg, 0, sizeof *msg );
1212         if (*ofs >= all_msgs->len)
1213                 return 0;
1214
1215         data = &all_msgs->data[ *ofs ];
1216         msg->len = all_msgs->len - *ofs;
1217
1218         if (msg->len < 5 || prefixcmp(data, "From "))
1219                 return 0;
1220
1221         p = strchr( data, '\n' );
1222         if (p) {
1223                 p = &p[1];
1224                 msg->len -= p-data;
1225                 *ofs += p-data;
1226                 data = p;
1227         }
1228
1229         p = strstr( data, "\nFrom " );
1230         if (p)
1231                 msg->len = &p[1] - data;
1232
1233         msg->data = xmemdupz(data, msg->len);
1234         *ofs += msg->len;
1235         return 1;
1236 }
1237
1238 static imap_server_conf_t server =
1239 {
1240         NULL,   /* name */
1241         NULL,   /* tunnel */
1242         NULL,   /* host */
1243         0,      /* port */
1244         NULL,   /* user */
1245         NULL,   /* pass */
1246 };
1247
1248 static char *imap_folder;
1249
1250 static int
1251 git_imap_config(const char *key, const char *val)
1252 {
1253         char imap_key[] = "imap.";
1254
1255         if (strncmp( key, imap_key, sizeof imap_key - 1 ))
1256                 return 0;
1257
1258         if (!val)
1259                 return config_error_nonbool(key);
1260
1261         key += sizeof imap_key - 1;
1262
1263         if (!strcmp( "folder", key )) {
1264                 imap_folder = xstrdup( val );
1265         } else if (!strcmp( "host", key )) {
1266                 {
1267                         if (!prefixcmp(val, "imap:"))
1268                                 val += 5;
1269                         if (!server.port)
1270                                 server.port = 143;
1271                 }
1272                 if (!prefixcmp(val, "//"))
1273                         val += 2;
1274                 server.host = xstrdup( val );
1275         }
1276         else if (!strcmp( "user", key ))
1277                 server.user = xstrdup( val );
1278         else if (!strcmp( "pass", key ))
1279                 server.pass = xstrdup( val );
1280         else if (!strcmp( "port", key ))
1281                 server.port = git_config_int( key, val );
1282         else if (!strcmp( "tunnel", key ))
1283                 server.tunnel = xstrdup( val );
1284         return 0;
1285 }
1286
1287 int
1288 main(int argc, char **argv)
1289 {
1290         msg_data_t all_msgs, msg;
1291         store_t *ctx = NULL;
1292         int uid = 0;
1293         int ofs = 0;
1294         int r;
1295         int total, n = 0;
1296
1297         /* init the random number generator */
1298         arc4_init();
1299
1300         git_config( git_imap_config );
1301
1302         if (!imap_folder) {
1303                 fprintf( stderr, "no imap store specified\n" );
1304                 return 1;
1305         }
1306
1307         /* read the messages */
1308         if (!read_message( stdin, &all_msgs )) {
1309                 fprintf(stderr,"nothing to send\n");
1310                 return 1;
1311         }
1312
1313         total = count_messages( &all_msgs );
1314         if (!total) {
1315                 fprintf(stderr,"no messages to send\n");
1316                 return 1;
1317         }
1318
1319         /* write it to the imap server */
1320         ctx = imap_open_store( &server );
1321         if (!ctx) {
1322                 fprintf( stderr,"failed to open store\n");
1323                 return 1;
1324         }
1325
1326         fprintf( stderr, "sending %d message%s\n", total, (total!=1)?"s":"" );
1327         ctx->name = imap_folder;
1328         while (1) {
1329                 unsigned percent = n * 100 / total;
1330                 fprintf( stderr, "%4u%% (%d/%d) done\r", percent, n, total );
1331                 if (!split_msg( &all_msgs, &msg, &ofs ))
1332                         break;
1333                 r = imap_store_msg( ctx, &msg, &uid );
1334                 if (r != DRV_OK) break;
1335                 n++;
1336         }
1337         fprintf( stderr,"\n" );
1338
1339         imap_close_store( ctx );
1340
1341         return 0;
1342 }