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