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