Merge branch 'maint'
[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)
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                         free( cmdp->cb.data );
862                         free( cmdp->cmd );
863                         free( cmdp );
864                         if (!tcmd || tcmd == cmdp)
865                                 return resp;
866                 }
867         }
868         /* not reached */
869 }
870
871 static void
872 imap_close_server( imap_store_t *ictx )
873 {
874         imap_t *imap = ictx->imap;
875
876         if (imap->buf.sock.fd != -1) {
877                 imap_exec( ictx, NULL, "LOGOUT" );
878                 close( imap->buf.sock.fd );
879         }
880         free_list( imap->ns_personal );
881         free_list( imap->ns_other );
882         free_list( imap->ns_shared );
883         free( imap );
884 }
885
886 static void
887 imap_close_store( store_t *ctx )
888 {
889         imap_close_server( (imap_store_t *)ctx );
890         free_generic_messages( ctx->msgs );
891         free( ctx );
892 }
893
894 static store_t *
895 imap_open_store( imap_server_conf_t *srvc )
896 {
897         imap_store_t *ctx;
898         imap_t *imap;
899         char *arg, *rsp;
900         struct hostent *he;
901         struct sockaddr_in addr;
902         int s, a[2], preauth;
903         pid_t pid;
904
905         ctx = xcalloc( sizeof(*ctx), 1 );
906
907         ctx->imap = imap = xcalloc( sizeof(*imap), 1 );
908         imap->buf.sock.fd = -1;
909         imap->in_progress_append = &imap->in_progress;
910
911         /* open connection to IMAP server */
912
913         if (srvc->tunnel) {
914                 imap_info( "Starting tunnel '%s'... ", srvc->tunnel );
915
916                 if (socketpair( PF_UNIX, SOCK_STREAM, 0, a )) {
917                         perror( "socketpair" );
918                         exit( 1 );
919                 }
920
921                 pid = fork();
922                 if (pid < 0)
923                         _exit( 127 );
924                 if (!pid) {
925                         if (dup2( a[0], 0 ) == -1 || dup2( a[0], 1 ) == -1)
926                                 _exit( 127 );
927                         close( a[0] );
928                         close( a[1] );
929                         execl( "/bin/sh", "sh", "-c", srvc->tunnel, NULL );
930                         _exit( 127 );
931                 }
932
933                 close (a[0]);
934
935                 imap->buf.sock.fd = a[1];
936
937                 imap_info( "ok\n" );
938         } else {
939                 memset( &addr, 0, sizeof(addr) );
940                 addr.sin_port = htons( srvc->port );
941                 addr.sin_family = AF_INET;
942
943                 imap_info( "Resolving %s... ", srvc->host );
944                 he = gethostbyname( srvc->host );
945                 if (!he) {
946                         perror( "gethostbyname" );
947                         goto bail;
948                 }
949                 imap_info( "ok\n" );
950
951                 addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
952
953                 s = socket( PF_INET, SOCK_STREAM, 0 );
954
955                 imap_info( "Connecting to %s:%hu... ", inet_ntoa( addr.sin_addr ), ntohs( addr.sin_port ) );
956                 if (connect( s, (struct sockaddr *)&addr, sizeof(addr) )) {
957                         close( s );
958                         perror( "connect" );
959                         goto bail;
960                 }
961                 imap_info( "ok\n" );
962
963                 imap->buf.sock.fd = s;
964
965         }
966
967         /* read the greeting string */
968         if (buffer_gets( &imap->buf, &rsp )) {
969                 fprintf( stderr, "IMAP error: no greeting response\n" );
970                 goto bail;
971         }
972         arg = next_arg( &rsp );
973         if (!arg || *arg != '*' || (arg = next_arg( &rsp )) == NULL) {
974                 fprintf( stderr, "IMAP error: invalid greeting response\n" );
975                 goto bail;
976         }
977         preauth = 0;
978         if (!strcmp( "PREAUTH", arg ))
979                 preauth = 1;
980         else if (strcmp( "OK", arg ) != 0) {
981                 fprintf( stderr, "IMAP error: unknown greeting response\n" );
982                 goto bail;
983         }
984         parse_response_code( ctx, NULL, rsp );
985         if (!imap->caps && imap_exec( ctx, NULL, "CAPABILITY" ) != RESP_OK)
986                 goto bail;
987
988         if (!preauth) {
989
990                 imap_info ("Logging in...\n");
991                 if (!srvc->user) {
992                         fprintf( stderr, "Skipping server %s, no user\n", srvc->host );
993                         goto bail;
994                 }
995                 if (!srvc->pass) {
996                         char prompt[80];
997                         sprintf( prompt, "Password (%s@%s): ", srvc->user, srvc->host );
998                         arg = getpass( prompt );
999                         if (!arg) {
1000                                 perror( "getpass" );
1001                                 exit( 1 );
1002                         }
1003                         if (!*arg) {
1004                                 fprintf( stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host );
1005                                 goto bail;
1006                         }
1007                         /*
1008                          * getpass() returns a pointer to a static buffer.  make a copy
1009                          * for long term storage.
1010                          */
1011                         srvc->pass = xstrdup( arg );
1012                 }
1013                 if (CAP(NOLOGIN)) {
1014                         fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host );
1015                         goto bail;
1016                 }
1017                 imap_warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
1018                 if (imap_exec( ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) {
1019                         fprintf( stderr, "IMAP error: LOGIN failed\n" );
1020                         goto bail;
1021                 }
1022         } /* !preauth */
1023
1024         ctx->prefix = "";
1025         ctx->trashnc = 1;
1026         return (store_t *)ctx;
1027
1028   bail:
1029         imap_close_store( &ctx->gen );
1030         return NULL;
1031 }
1032
1033 static int
1034 imap_make_flags( int flags, char *buf )
1035 {
1036         const char *s;
1037         unsigned i, d;
1038
1039         for (i = d = 0; i < ARRAY_SIZE(Flags); i++)
1040                 if (flags & (1 << i)) {
1041                         buf[d++] = ' ';
1042                         buf[d++] = '\\';
1043                         for (s = Flags[i]; *s; s++)
1044                                 buf[d++] = *s;
1045                 }
1046         buf[0] = '(';
1047         buf[d++] = ')';
1048         return d;
1049 }
1050
1051 #define TUIDL 8
1052
1053 static int
1054 imap_store_msg( store_t *gctx, msg_data_t *data, int *uid )
1055 {
1056         imap_store_t *ctx = (imap_store_t *)gctx;
1057         imap_t *imap = ctx->imap;
1058         struct imap_cmd_cb cb;
1059         char *fmap, *buf;
1060         const char *prefix, *box;
1061         int ret, i, j, d, len, extra, nocr;
1062         int start, sbreak = 0, ebreak = 0;
1063         char flagstr[128], tuid[TUIDL * 2 + 1];
1064
1065         memset( &cb, 0, sizeof(cb) );
1066
1067         fmap = data->data;
1068         len = data->len;
1069         nocr = !data->crlf;
1070         extra = 0, i = 0;
1071         if (!CAP(UIDPLUS) && uid) {
1072           nloop:
1073                 start = i;
1074                 while (i < len)
1075                         if (fmap[i++] == '\n') {
1076                                 extra += nocr;
1077                                 if (i - 2 + nocr == start) {
1078                                         sbreak = ebreak = i - 2 + nocr;
1079                                         goto mktid;
1080                                 }
1081                                 if (!memcmp( fmap + start, "X-TUID: ", 8 )) {
1082                                         extra -= (ebreak = i) - (sbreak = start) + nocr;
1083                                         goto mktid;
1084                                 }
1085                                 goto nloop;
1086                         }
1087                 /* invalid message */
1088                 free( fmap );
1089                 return DRV_MSG_BAD;
1090          mktid:
1091                 for (j = 0; j < TUIDL; j++)
1092                         sprintf( tuid + j * 2, "%02x", arc4_getbyte() );
1093                 extra += 8 + TUIDL * 2 + 2;
1094         }
1095         if (nocr)
1096                 for (; i < len; i++)
1097                         if (fmap[i] == '\n')
1098                                 extra++;
1099
1100         cb.dlen = len + extra;
1101         buf = cb.data = xmalloc( cb.dlen );
1102         i = 0;
1103         if (!CAP(UIDPLUS) && uid) {
1104                 if (nocr) {
1105                         for (; i < sbreak; i++)
1106                                 if (fmap[i] == '\n') {
1107                                         *buf++ = '\r';
1108                                         *buf++ = '\n';
1109                                 } else
1110                                         *buf++ = fmap[i];
1111                 } else {
1112                         memcpy( buf, fmap, sbreak );
1113                         buf += sbreak;
1114                 }
1115                 memcpy( buf, "X-TUID: ", 8 );
1116                 buf += 8;
1117                 memcpy( buf, tuid, TUIDL * 2 );
1118                 buf += TUIDL * 2;
1119                 *buf++ = '\r';
1120                 *buf++ = '\n';
1121                 i = ebreak;
1122         }
1123         if (nocr) {
1124                 for (; i < len; i++)
1125                         if (fmap[i] == '\n') {
1126                                 *buf++ = '\r';
1127                                 *buf++ = '\n';
1128                         } else
1129                                 *buf++ = fmap[i];
1130         } else
1131                 memcpy( buf, fmap + i, len - i );
1132
1133         free( fmap );
1134
1135         d = 0;
1136         if (data->flags) {
1137                 d = imap_make_flags( data->flags, flagstr );
1138                 flagstr[d++] = ' ';
1139         }
1140         flagstr[d] = 0;
1141
1142         if (!uid) {
1143                 box = gctx->conf->trash;
1144                 prefix = ctx->prefix;
1145                 cb.create = 1;
1146                 if (ctx->trashnc)
1147                         imap->caps = imap->rcaps & ~(1 << LITERALPLUS);
1148         } else {
1149                 box = gctx->name;
1150                 prefix = !strcmp( box, "INBOX" ) ? "" : ctx->prefix;
1151                 cb.create = 0;
1152         }
1153         cb.ctx = uid;
1154         ret = imap_exec_m( ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr );
1155         imap->caps = imap->rcaps;
1156         if (ret != DRV_OK)
1157                 return ret;
1158         if (!uid)
1159                 ctx->trashnc = 0;
1160         else
1161                 gctx->count++;
1162
1163         return DRV_OK;
1164 }
1165
1166 #define CHUNKSIZE 0x1000
1167
1168 static int
1169 read_message( FILE *f, msg_data_t *msg )
1170 {
1171         struct strbuf buf;
1172
1173         memset(msg, 0, sizeof(*msg));
1174         strbuf_init(&buf, 0);
1175
1176         do {
1177                 if (strbuf_fread(&buf, CHUNKSIZE, f) <= 0)
1178                         break;
1179         } while (!feof(f));
1180
1181         msg->len  = buf.len;
1182         msg->data = strbuf_detach(&buf, NULL);
1183         return msg->len;
1184 }
1185
1186 static int
1187 count_messages( msg_data_t *msg )
1188 {
1189         int count = 0;
1190         char *p = msg->data;
1191
1192         while (1) {
1193                 if (!prefixcmp(p, "From ")) {
1194                         count++;
1195                         p += 5;
1196                 }
1197                 p = strstr( p+5, "\nFrom ");
1198                 if (!p)
1199                         break;
1200                 p++;
1201         }
1202         return count;
1203 }
1204
1205 static int
1206 split_msg( msg_data_t *all_msgs, msg_data_t *msg, int *ofs )
1207 {
1208         char *p, *data;
1209
1210         memset( msg, 0, sizeof *msg );
1211         if (*ofs >= all_msgs->len)
1212                 return 0;
1213
1214         data = &all_msgs->data[ *ofs ];
1215         msg->len = all_msgs->len - *ofs;
1216
1217         if (msg->len < 5 || prefixcmp(data, "From "))
1218                 return 0;
1219
1220         p = strchr( data, '\n' );
1221         if (p) {
1222                 p = &p[1];
1223                 msg->len -= p-data;
1224                 *ofs += p-data;
1225                 data = p;
1226         }
1227
1228         p = strstr( data, "\nFrom " );
1229         if (p)
1230                 msg->len = &p[1] - data;
1231
1232         msg->data = xmemdupz(data, msg->len);
1233         *ofs += msg->len;
1234         return 1;
1235 }
1236
1237 static imap_server_conf_t server =
1238 {
1239         NULL,   /* name */
1240         NULL,   /* tunnel */
1241         NULL,   /* host */
1242         0,      /* port */
1243         NULL,   /* user */
1244         NULL,   /* pass */
1245 };
1246
1247 static char *imap_folder;
1248
1249 static int
1250 git_imap_config(const char *key, const char *val, void *cb)
1251 {
1252         char imap_key[] = "imap.";
1253
1254         if (strncmp( key, imap_key, sizeof imap_key - 1 ))
1255                 return 0;
1256
1257         if (!val)
1258                 return config_error_nonbool(key);
1259
1260         key += sizeof imap_key - 1;
1261
1262         if (!strcmp( "folder", key )) {
1263                 imap_folder = xstrdup( val );
1264         } else if (!strcmp( "host", key )) {
1265                 {
1266                         if (!prefixcmp(val, "imap:"))
1267                                 val += 5;
1268                         if (!server.port)
1269                                 server.port = 143;
1270                 }
1271                 if (!prefixcmp(val, "//"))
1272                         val += 2;
1273                 server.host = xstrdup( val );
1274         }
1275         else if (!strcmp( "user", key ))
1276                 server.user = xstrdup( val );
1277         else if (!strcmp( "pass", key ))
1278                 server.pass = xstrdup( val );
1279         else if (!strcmp( "port", key ))
1280                 server.port = git_config_int( key, val );
1281         else if (!strcmp( "tunnel", key ))
1282                 server.tunnel = xstrdup( val );
1283         return 0;
1284 }
1285
1286 int
1287 main(int argc, char **argv)
1288 {
1289         msg_data_t all_msgs, msg;
1290         store_t *ctx = NULL;
1291         int uid = 0;
1292         int ofs = 0;
1293         int r;
1294         int total, n = 0;
1295
1296         /* init the random number generator */
1297         arc4_init();
1298
1299         git_config(git_imap_config, NULL);
1300
1301         if (!imap_folder) {
1302                 fprintf( stderr, "no imap store specified\n" );
1303                 return 1;
1304         }
1305         if (!server.host) {
1306                 if (!server.tunnel) {
1307                         fprintf( stderr, "no imap host specified\n" );
1308                         return 1;
1309                 }
1310                 server.host = "tunnel";
1311         }
1312
1313         /* read the messages */
1314         if (!read_message( stdin, &all_msgs )) {
1315                 fprintf(stderr,"nothing to send\n");
1316                 return 1;
1317         }
1318
1319         total = count_messages( &all_msgs );
1320         if (!total) {
1321                 fprintf(stderr,"no messages to send\n");
1322                 return 1;
1323         }
1324
1325         /* write it to the imap server */
1326         ctx = imap_open_store( &server );
1327         if (!ctx) {
1328                 fprintf( stderr,"failed to open store\n");
1329                 return 1;
1330         }
1331
1332         fprintf( stderr, "sending %d message%s\n", total, (total!=1)?"s":"" );
1333         ctx->name = imap_folder;
1334         while (1) {
1335                 unsigned percent = n * 100 / total;
1336                 fprintf( stderr, "%4u%% (%d/%d) done\r", percent, n, total );
1337                 if (!split_msg( &all_msgs, &msg, &ofs ))
1338                         break;
1339                 r = imap_store_msg( ctx, &msg, &uid );
1340                 if (r != DRV_OK) break;
1341                 n++;
1342         }
1343         fprintf( stderr,"\n" );
1344
1345         imap_close_store( ctx );
1346
1347         return 0;
1348 }