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