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