imap-send.c 32.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
 * git-imap-send - drops patches into an imap Drafts folder
 *                 derived from isync/mbsync - mailbox synchronizer
 *
 * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
 * Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
 * Copyright (C) 2004 Theodore Y. Ts'o <tytso@mit.edu>
 * Copyright (C) 2006 Mike McCormack
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "cache.h"
26
#include "exec_cmd.h"
R
Robert Shearman 已提交
27 28 29
#ifdef NO_OPENSSL
typedef void *SSL;
#endif
30

J
Junio C Hamano 已提交
31
struct store_conf {
32 33 34 35 36 37
	char *name;
	const char *path; /* should this be here? its interpretation is driver-specific */
	char *map_inbox;
	char *trash;
	unsigned max_size; /* off_t is overkill */
	unsigned trash_remote_new:1, trash_only_new:1;
J
Junio C Hamano 已提交
38
};
39

J
Junio C Hamano 已提交
40
struct string_list {
41 42
	struct string_list *next;
	char string[1];
J
Junio C Hamano 已提交
43
};
44

J
Junio C Hamano 已提交
45
struct channel_conf {
46 47
	struct channel_conf *next;
	char *name;
J
Junio C Hamano 已提交
48
	struct store_conf *master, *slave;
49 50
	char *master_name, *slave_name;
	char *sync_state;
J
Junio C Hamano 已提交
51
	struct string_list *patterns;
52 53
	int mops, sops;
	unsigned max_messages; /* for slave only */
J
Junio C Hamano 已提交
54
};
55

J
Junio C Hamano 已提交
56
struct group_conf {
57 58
	struct group_conf *next;
	char *name;
J
Junio C Hamano 已提交
59 60
	struct string_list *channels;
};
61 62 63 64 65 66

/* For message->status */
#define M_RECENT       (1<<0) /* unsyncable flag; maildir_* depend on this being 1<<0 */
#define M_DEAD         (1<<1) /* expunged */
#define M_FLAGS        (1<<2) /* flags fetched */

J
Junio C Hamano 已提交
67
struct message {
68
	struct message *next;
J
Junio C Hamano 已提交
69
	/* struct string_list *keywords; */
70 71 72
	size_t size; /* zero implies "not fetched" */
	int uid;
	unsigned char flags, status;
J
Junio C Hamano 已提交
73
};
74

J
Junio C Hamano 已提交
75 76
struct store {
	struct store_conf *conf; /* foreign */
77 78 79 80

	/* currently open mailbox */
	const char *name; /* foreign! maybe preset? */
	char *path; /* own */
J
Junio C Hamano 已提交
81
	struct message *msgs; /* own */
82 83 84 85 86
	int uidvalidity;
	unsigned char opts; /* maybe preset? */
	/* note that the following do _not_ reflect stats from msgs, but mailbox totals */
	int count; /* # of messages */
	int recent; /* # of recent messages - don't trust this beyond the initial read */
J
Junio C Hamano 已提交
87
};
88

J
Junio C Hamano 已提交
89
struct msg_data {
90 91 92
	char *data;
	int len;
	unsigned char flags;
93
	unsigned int crlf:1;
J
Junio C Hamano 已提交
94
};
95 96 97 98 99 100 101 102

#define DRV_OK          0
#define DRV_MSG_BAD     -1
#define DRV_BOX_BAD     -2
#define DRV_STORE_BAD   -3

static int Verbose, Quiet;

R
Robert Shearman 已提交
103 104
static void imap_info(const char *, ...);
static void imap_warn(const char *, ...);
105

R
Robert Shearman 已提交
106
static char *next_arg(char **);
107

J
Junio C Hamano 已提交
108
static void free_generic_messages(struct message *);
109

R
Robert Shearman 已提交
110
static int nfsnprintf(char *buf, int blen, const char *fmt, ...);
111

112 113 114 115 116 117 118
static int nfvasprintf(char **strp, const char *fmt, va_list ap)
{
	int len;
	char tmp[8192];

	len = vsnprintf(tmp, sizeof(tmp), fmt, ap);
	if (len < 0)
119
		die("Fatal: Out of memory");
120
	if (len >= sizeof(tmp))
121
		die("imap command overflow!");
122 123 124
	*strp = xmemdupz(tmp, len);
	return len;
}
125

R
Robert Shearman 已提交
126 127
static void arc4_init(void);
static unsigned char arc4_getbyte(void);
128

J
Junio C Hamano 已提交
129
struct imap_server_conf {
130 131 132 133 134 135
	char *name;
	char *tunnel;
	char *host;
	int port;
	char *user;
	char *pass;
R
Robert Shearman 已提交
136 137
	int use_ssl;
	int ssl_verify;
138
	int use_html;
J
Junio C Hamano 已提交
139
};
140

J
Junio C Hamano 已提交
141 142 143
struct imap_store_conf {
	struct store_conf gen;
	struct imap_server_conf *server;
144
	unsigned use_namespace:1;
J
Junio C Hamano 已提交
145
};
146

J
Junio C Hamano 已提交
147 148
#define NIL	(void *)0x1
#define LIST	(void *)0x2
149

J
Junio C Hamano 已提交
150 151
struct imap_list {
	struct imap_list *next, *child;
152 153
	char *val;
	int len;
J
Junio C Hamano 已提交
154
};
155

J
Junio C Hamano 已提交
156
struct imap_socket {
157
	int fd;
R
Robert Shearman 已提交
158
	SSL *ssl;
J
Junio C Hamano 已提交
159
};
160

J
Junio C Hamano 已提交
161 162
struct imap_buffer {
	struct imap_socket sock;
163 164 165
	int bytes;
	int offset;
	char buf[1024];
J
Junio C Hamano 已提交
166
};
167 168 169

struct imap_cmd;

J
Junio C Hamano 已提交
170
struct imap {
171
	int uidnext; /* from SELECT responses */
J
Junio C Hamano 已提交
172
	struct imap_list *ns_personal, *ns_other, *ns_shared; /* NAMESPACE info */
173 174 175 176
	unsigned caps, rcaps; /* CAPABILITY results */
	/* command queue */
	int nexttag, num_in_progress, literal_pending;
	struct imap_cmd *in_progress, **in_progress_append;
J
Junio C Hamano 已提交
177 178
	struct imap_buffer buf; /* this is BIG, so put it last */
};
179

J
Junio C Hamano 已提交
180 181
struct imap_store {
	struct store gen;
182
	int uidvalidity;
J
Junio C Hamano 已提交
183
	struct imap *imap;
184 185
	const char *prefix;
	unsigned /*currentnc:1,*/ trashnc:1;
J
Junio C Hamano 已提交
186
};
187 188

struct imap_cmd_cb {
J
Junio C Hamano 已提交
189 190
	int (*cont)(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt);
	void (*done)(struct imap_store *ctx, struct imap_cmd *cmd, int response);
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
	void *ctx;
	char *data;
	int dlen;
	int uid;
	unsigned create:1, trycreate:1;
};

struct imap_cmd {
	struct imap_cmd *next;
	struct imap_cmd_cb cb;
	char *cmd;
	int tag;
};

#define CAP(cap) (imap->caps & (1 << (cap)))

enum CAPABILITY {
	NOLOGIN = 0,
	UIDPLUS,
	LITERALPLUS,
	NAMESPACE,
R
Robert Shearman 已提交
212
	STARTTLS,
213 214 215 216 217 218 219
};

static const char *cap_list[] = {
	"LOGINDISABLED",
	"UIDPLUS",
	"LITERAL+",
	"NAMESPACE",
R
Robert Shearman 已提交
220
	"STARTTLS",
221 222 223 224 225 226
};

#define RESP_OK    0
#define RESP_NO    1
#define RESP_BAD   2

J
Junio C Hamano 已提交
227
static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd);
228 229 230 231 232 233 234 235 236 237


static const char *Flags[] = {
	"Draft",
	"Flagged",
	"Answered",
	"Seen",
	"Deleted",
};

R
Robert Shearman 已提交
238 239 240 241 242 243 244
#ifndef NO_OPENSSL
static void ssl_socket_perror(const char *func)
{
	fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), 0));
}
#endif

J
Junio C Hamano 已提交
245
static void socket_perror(const char *func, struct imap_socket *sock, int ret)
246
{
R
Robert Shearman 已提交
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
#ifndef NO_OPENSSL
	if (sock->ssl) {
		int sslerr = SSL_get_error(sock->ssl, ret);
		switch (sslerr) {
		case SSL_ERROR_NONE:
			break;
		case SSL_ERROR_SYSCALL:
			perror("SSL_connect");
			break;
		default:
			ssl_socket_perror("SSL_connect");
			break;
		}
	} else
#endif
	{
		if (ret < 0)
			perror(func);
		else
			fprintf(stderr, "%s: unexpected EOF\n", func);
	}
}

J
Junio C Hamano 已提交
270
static int ssl_socket_connect(struct imap_socket *sock, int use_tls_only, int verify)
R
Robert Shearman 已提交
271 272 273 274 275 276 277 278 279 280 281 282 283 284
{
#ifdef NO_OPENSSL
	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
	return -1;
#else
	SSL_METHOD *meth;
	SSL_CTX *ctx;
	int ret;

	SSL_library_init();
	SSL_load_error_strings();

	if (use_tls_only)
		meth = TLSv1_method();
285
	else
R
Robert Shearman 已提交
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
		meth = SSLv23_method();

	if (!meth) {
		ssl_socket_perror("SSLv23_method");
		return -1;
	}

	ctx = SSL_CTX_new(meth);

	if (verify)
		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

	if (!SSL_CTX_set_default_verify_paths(ctx)) {
		ssl_socket_perror("SSL_CTX_set_default_verify_paths");
		return -1;
	}
	sock->ssl = SSL_new(ctx);
	if (!sock->ssl) {
		ssl_socket_perror("SSL_new");
		return -1;
	}
	if (!SSL_set_fd(sock->ssl, sock->fd)) {
		ssl_socket_perror("SSL_set_fd");
		return -1;
	}

	ret = SSL_connect(sock->ssl);
	if (ret <= 0) {
		socket_perror("SSL_connect", sock, ret);
		return -1;
	}

	return 0;
#endif
320 321
}

J
Junio C Hamano 已提交
322
static int socket_read(struct imap_socket *sock, char *buf, int len)
323
{
R
Robert Shearman 已提交
324 325 326 327 328 329
	ssize_t n;
#ifndef NO_OPENSSL
	if (sock->ssl)
		n = SSL_read(sock->ssl, buf, len);
	else
#endif
R
Robert Shearman 已提交
330
		n = xread(sock->fd, buf, len);
331
	if (n <= 0) {
R
Robert Shearman 已提交
332 333
		socket_perror("read", sock, n);
		close(sock->fd);
334 335 336 337 338
		sock->fd = -1;
	}
	return n;
}

J
Junio C Hamano 已提交
339
static int socket_write(struct imap_socket *sock, const char *buf, int len)
340
{
R
Robert Shearman 已提交
341 342 343 344 345 346
	int n;
#ifndef NO_OPENSSL
	if (sock->ssl)
		n = SSL_write(sock->ssl, buf, len);
	else
#endif
R
Robert Shearman 已提交
347
		n = write_in_full(sock->fd, buf, len);
348
	if (n != len) {
R
Robert Shearman 已提交
349 350
		socket_perror("write", sock, n);
		close(sock->fd);
351 352 353 354 355
		sock->fd = -1;
	}
	return n;
}

J
Junio C Hamano 已提交
356
static void socket_shutdown(struct imap_socket *sock)
R
Robert Shearman 已提交
357 358 359 360 361 362 363 364 365 366
{
#ifndef NO_OPENSSL
	if (sock->ssl) {
		SSL_shutdown(sock->ssl);
		SSL_free(sock->ssl);
	}
#endif
	close(sock->fd);
}

367
/* simple line buffering */
J
Junio C Hamano 已提交
368
static int buffer_gets(struct imap_buffer *b, char **s)
369 370 371 372 373 374 375 376 377 378 379 380 381
{
	int n;
	int start = b->offset;

	*s = b->buf + start;

	for (;;) {
		/* make sure we have enough data to read the \r\n sequence */
		if (b->offset + 1 >= b->bytes) {
			if (start) {
				/* shift down used bytes */
				*s = b->buf;

R
Robert Shearman 已提交
382
				assert(start <= b->bytes);
383 384 385
				n = b->bytes - start;

				if (n)
386
					memmove(b->buf, b->buf + start, n);
387 388 389 390 391
				b->offset -= start;
				b->bytes = n;
				start = 0;
			}

R
Robert Shearman 已提交
392 393
			n = socket_read(&b->sock, b->buf + b->bytes,
					 sizeof(b->buf) - b->bytes);
394 395 396 397 398 399 400 401

			if (n <= 0)
				return -1;

			b->bytes += n;
		}

		if (b->buf[b->offset] == '\r') {
R
Robert Shearman 已提交
402
			assert(b->offset + 1 < b->bytes);
403 404 405 406
			if (b->buf[b->offset + 1] == '\n') {
				b->buf[b->offset] = 0;  /* terminate the string */
				b->offset += 2; /* next line */
				if (Verbose)
R
Robert Shearman 已提交
407
					puts(*s);
408 409 410 411 412 413 414 415 416
				return 0;
			}
		}

		b->offset++;
	}
	/* not reached */
}

R
Robert Shearman 已提交
417
static void imap_info(const char *msg, ...)
418 419 420 421
{
	va_list va;

	if (!Quiet) {
R
Robert Shearman 已提交
422 423 424 425
		va_start(va, msg);
		vprintf(msg, va);
		va_end(va);
		fflush(stdout);
426 427 428
	}
}

R
Robert Shearman 已提交
429
static void imap_warn(const char *msg, ...)
430 431 432 433
{
	va_list va;

	if (Quiet < 2) {
R
Robert Shearman 已提交
434 435 436
		va_start(va, msg);
		vfprintf(stderr, msg, va);
		va_end(va);
437 438 439
	}
}

R
Robert Shearman 已提交
440
static char *next_arg(char **s)
441 442 443 444
{
	char *ret;

	if (!s || !*s)
445
		return NULL;
R
Robert Shearman 已提交
446
	while (isspace((unsigned char) **s))
447 448
		(*s)++;
	if (!**s) {
449 450
		*s = NULL;
		return NULL;
451 452 453 454
	}
	if (**s == '"') {
		++*s;
		ret = *s;
R
Robert Shearman 已提交
455
		*s = strchr(*s, '"');
456 457
	} else {
		ret = *s;
R
Robert Shearman 已提交
458
		while (**s && !isspace((unsigned char) **s))
459 460 461 462 463 464
			(*s)++;
	}
	if (*s) {
		if (**s)
			*(*s)++ = 0;
		if (!**s)
465
			*s = NULL;
466 467 468 469
	}
	return ret;
}

J
Junio C Hamano 已提交
470
static void free_generic_messages(struct message *msgs)
471
{
J
Junio C Hamano 已提交
472
	struct message *tmsg;
473 474 475

	for (; msgs; msgs = tmsg) {
		tmsg = msgs->next;
R
Robert Shearman 已提交
476
		free(msgs);
477 478 479
	}
}

R
Robert Shearman 已提交
480
static int nfsnprintf(char *buf, int blen, const char *fmt, ...)
481 482 483 484
{
	int ret;
	va_list va;

R
Robert Shearman 已提交
485 486
	va_start(va, fmt);
	if (blen <= 0 || (unsigned)(ret = vsnprintf(buf, blen, fmt, va)) >= (unsigned)blen)
487
		die("Fatal: buffer too small. Please report a bug.");
R
Robert Shearman 已提交
488
	va_end(va);
489 490 491 492 493 494 495
	return ret;
}

static struct {
	unsigned char i, j, s[256];
} rs;

R
Robert Shearman 已提交
496
static void arc4_init(void)
497 498 499 500
{
	int i, fd;
	unsigned char j, si, dat[128];

R
Robert Shearman 已提交
501 502 503
	if ((fd = open("/dev/urandom", O_RDONLY)) < 0 && (fd = open("/dev/random", O_RDONLY)) < 0) {
		fprintf(stderr, "Fatal: no random number source available.\n");
		exit(3);
504
	}
R
Robert Shearman 已提交
505 506 507
	if (read_in_full(fd, dat, 128) != 128) {
		fprintf(stderr, "Fatal: cannot read random number source.\n");
		exit(3);
508
	}
R
Robert Shearman 已提交
509
	close(fd);
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524

	for (i = 0; i < 256; i++)
		rs.s[i] = i;
	for (i = j = 0; i < 256; i++) {
		si = rs.s[i];
		j += si + dat[i & 127];
		rs.s[i] = rs.s[j];
		rs.s[j] = si;
	}
	rs.i = rs.j = 0;

	for (i = 0; i < 256; i++)
		arc4_getbyte();
}

R
Robert Shearman 已提交
525
static unsigned char arc4_getbyte(void)
526 527 528 529 530 531 532 533 534 535 536 537
{
	unsigned char si, sj;

	rs.i++;
	si = rs.s[rs.i];
	rs.j += si;
	sj = rs.s[rs.j];
	rs.s[rs.i] = sj;
	rs.s[rs.j] = si;
	return rs.s[(si + sj) & 0xff];
}

J
Junio C Hamano 已提交
538
static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
R
Robert Shearman 已提交
539 540
					 struct imap_cmd_cb *cb,
					 const char *fmt, va_list ap)
541
{
J
Junio C Hamano 已提交
542
	struct imap *imap = ctx->imap;
543 544 545 546
	struct imap_cmd *cmd;
	int n, bufl;
	char buf[1024];

R
Robert Shearman 已提交
547 548
	cmd = xmalloc(sizeof(struct imap_cmd));
	nfvasprintf(&cmd->cmd, fmt, ap);
549 550 551 552 553
	cmd->tag = ++imap->nexttag;

	if (cb)
		cmd->cb = *cb;
	else
R
Robert Shearman 已提交
554
		memset(&cmd->cb, 0, sizeof(cmd->cb));
555 556

	while (imap->literal_pending)
R
Robert Shearman 已提交
557
		get_cmd_result(ctx, NULL);
558

R
Robert Shearman 已提交
559
	bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
560
			   "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
R
Robert Shearman 已提交
561
			   cmd->tag, cmd->cmd, cmd->cb.dlen);
562 563
	if (Verbose) {
		if (imap->num_in_progress)
R
Robert Shearman 已提交
564 565 566
			printf("(%d in progress) ", imap->num_in_progress);
		if (memcmp(cmd->cmd, "LOGIN", 5))
			printf(">>> %s", buf);
567
		else
R
Robert Shearman 已提交
568
			printf(">>> %d LOGIN <user> <pass>\n", cmd->tag);
569
	}
R
Robert Shearman 已提交
570 571 572
	if (socket_write(&imap->buf.sock, buf, bufl) != bufl) {
		free(cmd->cmd);
		free(cmd);
573
		if (cb)
R
Robert Shearman 已提交
574
			free(cb->data);
575 576 577 578
		return NULL;
	}
	if (cmd->cb.data) {
		if (CAP(LITERALPLUS)) {
R
Robert Shearman 已提交
579 580
			n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen);
			free(cmd->cb.data);
581
			if (n != cmd->cb.dlen ||
J
Junio C Hamano 已提交
582
			    (n = socket_write(&imap->buf.sock, "\r\n", 2)) != 2) {
R
Robert Shearman 已提交
583 584
				free(cmd->cmd);
				free(cmd);
585 586
				return NULL;
			}
587
			cmd->cb.data = NULL;
588 589 590 591
		} else
			imap->literal_pending = 1;
	} else if (cmd->cb.cont)
		imap->literal_pending = 1;
592
	cmd->next = NULL;
593 594 595 596 597 598
	*imap->in_progress_append = cmd;
	imap->in_progress_append = &cmd->next;
	imap->num_in_progress++;
	return cmd;
}

J
Junio C Hamano 已提交
599
static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
R
Robert Shearman 已提交
600 601
				       struct imap_cmd_cb *cb,
				       const char *fmt, ...)
602 603 604 605
{
	struct imap_cmd *ret;
	va_list ap;

R
Robert Shearman 已提交
606 607 608
	va_start(ap, fmt);
	ret = v_issue_imap_cmd(ctx, cb, fmt, ap);
	va_end(ap);
609 610 611
	return ret;
}

J
Junio C Hamano 已提交
612
static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,
R
Robert Shearman 已提交
613
		     const char *fmt, ...)
614 615 616 617
{
	va_list ap;
	struct imap_cmd *cmdp;

R
Robert Shearman 已提交
618 619 620
	va_start(ap, fmt);
	cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap);
	va_end(ap);
621 622 623
	if (!cmdp)
		return RESP_BAD;

R
Robert Shearman 已提交
624
	return get_cmd_result(ctx, cmdp);
625 626
}

J
Junio C Hamano 已提交
627
static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb,
R
Robert Shearman 已提交
628
		       const char *fmt, ...)
629 630 631 632
{
	va_list ap;
	struct imap_cmd *cmdp;

R
Robert Shearman 已提交
633 634 635
	va_start(ap, fmt);
	cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap);
	va_end(ap);
636 637 638
	if (!cmdp)
		return DRV_STORE_BAD;

R
Robert Shearman 已提交
639
	switch (get_cmd_result(ctx, cmdp)) {
640 641 642 643 644 645
	case RESP_BAD: return DRV_STORE_BAD;
	case RESP_NO: return DRV_MSG_BAD;
	default: return DRV_OK;
	}
}

J
Junio C Hamano 已提交
646
static int is_atom(struct imap_list *list)
647 648 649 650
{
	return list && list->val && list->val != NIL && list->val != LIST;
}

J
Junio C Hamano 已提交
651
static int is_list(struct imap_list *list)
652 653 654 655
{
	return list && list->val == LIST;
}

J
Junio C Hamano 已提交
656
static void free_list(struct imap_list *list)
657
{
J
Junio C Hamano 已提交
658
	struct imap_list *tmp;
659 660 661

	for (; list; list = tmp) {
		tmp = list->next;
R
Robert Shearman 已提交
662 663 664 665 666
		if (is_list(list))
			free_list(list->child);
		else if (is_atom(list))
			free(list->val);
		free(list);
667 668 669
	}
}

J
Junio C Hamano 已提交
670
static int parse_imap_list_l(struct imap *imap, char **sp, struct imap_list **curp, int level)
671
{
J
Junio C Hamano 已提交
672
	struct imap_list *cur;
673 674 675 676
	char *s = *sp, *p;
	int n, bytes;

	for (;;) {
R
Robert Shearman 已提交
677
		while (isspace((unsigned char)*s))
678 679 680 681 682
			s++;
		if (level && *s == ')') {
			s++;
			break;
		}
R
Robert Shearman 已提交
683
		*curp = cur = xmalloc(sizeof(*cur));
684
		curp = &cur->next;
685
		cur->val = NULL; /* for clean bail */
686 687 688 689
		if (*s == '(') {
			/* sublist */
			s++;
			cur->val = LIST;
R
Robert Shearman 已提交
690
			if (parse_imap_list_l(imap, &s, &cur->child, level + 1))
691 692 693
				goto bail;
		} else if (imap && *s == '{') {
			/* literal */
R
Robert Shearman 已提交
694
			bytes = cur->len = strtol(s + 1, &s, 10);
695 696 697
			if (*s != '}')
				goto bail;

R
Robert Shearman 已提交
698
			s = cur->val = xmalloc(cur->len);
699 700 701 702 703 704 705 706

			/* dump whats left over in the input buffer */
			n = imap->buf.bytes - imap->buf.offset;

			if (n > bytes)
				/* the entire message fit in the buffer */
				n = bytes;

R
Robert Shearman 已提交
707
			memcpy(s, imap->buf.buf + imap->buf.offset, n);
708 709 710 711 712 713 714 715
			s += n;
			bytes -= n;

			/* mark that we used part of the buffer */
			imap->buf.offset += n;

			/* now read the rest of the message */
			while (bytes > 0) {
R
Robert Shearman 已提交
716
				if ((n = socket_read(&imap->buf.sock, s, bytes)) <= 0)
717 718 719 720 721
					goto bail;
				s += n;
				bytes -= n;
			}

R
Robert Shearman 已提交
722
			if (buffer_gets(&imap->buf, &s))
723 724 725 726 727 728 729 730 731 732
				goto bail;
		} else if (*s == '"') {
			/* quoted string */
			s++;
			p = s;
			for (; *s != '"'; s++)
				if (!*s)
					goto bail;
			cur->len = s - p;
			s++;
P
Pierre Habouzit 已提交
733
			cur->val = xmemdupz(p, cur->len);
734 735 736
		} else {
			/* atom */
			p = s;
R
Robert Shearman 已提交
737
			for (; *s && !isspace((unsigned char)*s); s++)
738 739 740
				if (level && *s == ')')
					break;
			cur->len = s - p;
J
Junio C Hamano 已提交
741
			if (cur->len == 3 && !memcmp("NIL", p, 3))
742
				cur->val = NIL;
J
Junio C Hamano 已提交
743
			else
P
Pierre Habouzit 已提交
744
				cur->val = xmemdupz(p, cur->len);
745 746 747 748 749 750 751 752
		}

		if (!level)
			break;
		if (!*s)
			goto bail;
	}
	*sp = s;
753
	*curp = NULL;
754 755
	return 0;

J
Junio C Hamano 已提交
756
bail:
757
	*curp = NULL;
758 759 760
	return -1;
}

J
Junio C Hamano 已提交
761
static struct imap_list *parse_imap_list(struct imap *imap, char **sp)
762
{
J
Junio C Hamano 已提交
763
	struct imap_list *head;
764

R
Robert Shearman 已提交
765
	if (!parse_imap_list_l(imap, sp, &head, 0))
766
		return head;
R
Robert Shearman 已提交
767
	free_list(head);
768 769 770
	return NULL;
}

J
Junio C Hamano 已提交
771
static struct imap_list *parse_list(char **sp)
772
{
R
Robert Shearman 已提交
773
	return parse_imap_list(NULL, sp);
774 775
}

J
Junio C Hamano 已提交
776
static void parse_capability(struct imap *imap, char *cmd)
777 778 779 780 781
{
	char *arg;
	unsigned i;

	imap->caps = 0x80000000;
R
Robert Shearman 已提交
782
	while ((arg = next_arg(&cmd)))
783
		for (i = 0; i < ARRAY_SIZE(cap_list); i++)
R
Robert Shearman 已提交
784
			if (!strcmp(cap_list[i], arg))
785 786 787 788
				imap->caps |= 1 << i;
	imap->rcaps = imap->caps;
}

J
Junio C Hamano 已提交
789
static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb,
R
Robert Shearman 已提交
790
			       char *s)
791
{
J
Junio C Hamano 已提交
792
	struct imap *imap = ctx->imap;
793 794 795 796 797
	char *arg, *p;

	if (*s != '[')
		return RESP_OK;		/* no response code */
	s++;
R
Robert Shearman 已提交
798 799
	if (!(p = strchr(s, ']'))) {
		fprintf(stderr, "IMAP error: malformed response code\n");
800 801 802
		return RESP_BAD;
	}
	*p++ = 0;
R
Robert Shearman 已提交
803 804 805 806
	arg = next_arg(&s);
	if (!strcmp("UIDVALIDITY", arg)) {
		if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg))) {
			fprintf(stderr, "IMAP error: malformed UIDVALIDITY status\n");
807 808
			return RESP_BAD;
		}
R
Robert Shearman 已提交
809 810 811
	} else if (!strcmp("UIDNEXT", arg)) {
		if (!(arg = next_arg(&s)) || !(imap->uidnext = atoi(arg))) {
			fprintf(stderr, "IMAP error: malformed NEXTUID status\n");
812 813
			return RESP_BAD;
		}
R
Robert Shearman 已提交
814 815 816
	} else if (!strcmp("CAPABILITY", arg)) {
		parse_capability(imap, s);
	} else if (!strcmp("ALERT", arg)) {
817 818 819
		/* RFC2060 says that these messages MUST be displayed
		 * to the user
		 */
R
Robert Shearman 已提交
820 821 822 823
		for (; isspace((unsigned char)*p); p++);
		fprintf(stderr, "*** IMAP ALERT *** %s\n", p);
	} else if (cb && cb->ctx && !strcmp("APPENDUID", arg)) {
		if (!(arg = next_arg(&s)) || !(ctx->gen.uidvalidity = atoi(arg)) ||
J
Junio C Hamano 已提交
824
		    !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg))) {
R
Robert Shearman 已提交
825
			fprintf(stderr, "IMAP error: malformed APPENDUID status\n");
826 827 828 829 830 831
			return RESP_BAD;
		}
	}
	return RESP_OK;
}

J
Junio C Hamano 已提交
832
static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
833
{
J
Junio C Hamano 已提交
834
	struct imap *imap = ctx->imap;
835 836 837 838 839
	struct imap_cmd *cmdp, **pcmdp, *ncmdp;
	char *cmd, *arg, *arg1, *p;
	int n, resp, resp2, tag;

	for (;;) {
R
Robert Shearman 已提交
840
		if (buffer_gets(&imap->buf, &cmd))
841 842
			return RESP_BAD;

R
Robert Shearman 已提交
843
		arg = next_arg(&cmd);
844
		if (*arg == '*') {
R
Robert Shearman 已提交
845
			arg = next_arg(&cmd);
846
			if (!arg) {
R
Robert Shearman 已提交
847
				fprintf(stderr, "IMAP error: unable to parse untagged response\n");
848 849 850
				return RESP_BAD;
			}

R
Robert Shearman 已提交
851 852 853 854 855 856 857
			if (!strcmp("NAMESPACE", arg)) {
				imap->ns_personal = parse_list(&cmd);
				imap->ns_other = parse_list(&cmd);
				imap->ns_shared = parse_list(&cmd);
			} else if (!strcmp("OK", arg) || !strcmp("BAD", arg) ||
				   !strcmp("NO", arg) || !strcmp("BYE", arg)) {
				if ((resp = parse_response_code(ctx, NULL, cmd)) != RESP_OK)
858
					return resp;
R
Robert Shearman 已提交
859 860 861 862 863 864 865
			} else if (!strcmp("CAPABILITY", arg))
				parse_capability(imap, cmd);
			else if ((arg1 = next_arg(&cmd))) {
				if (!strcmp("EXISTS", arg1))
					ctx->gen.count = atoi(arg);
				else if (!strcmp("RECENT", arg1))
					ctx->gen.recent = atoi(arg);
866
			} else {
R
Robert Shearman 已提交
867
				fprintf(stderr, "IMAP error: unable to parse untagged response\n");
868 869 870
				return RESP_BAD;
			}
		} else if (!imap->in_progress) {
R
Robert Shearman 已提交
871
			fprintf(stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "");
872 873 874 875 876 877 878
			return RESP_BAD;
		} else if (*arg == '+') {
			/* This can happen only with the last command underway, as
			   it enforces a round-trip. */
			cmdp = (struct imap_cmd *)((char *)imap->in_progress_append -
			       offsetof(struct imap_cmd, next));
			if (cmdp->cb.data) {
R
Robert Shearman 已提交
879 880
				n = socket_write(&imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen);
				free(cmdp->cb.data);
881
				cmdp->cb.data = NULL;
882 883 884
				if (n != (int)cmdp->cb.dlen)
					return RESP_BAD;
			} else if (cmdp->cb.cont) {
R
Robert Shearman 已提交
885
				if (cmdp->cb.cont(ctx, cmdp, cmd))
886 887
					return RESP_BAD;
			} else {
R
Robert Shearman 已提交
888
				fprintf(stderr, "IMAP error: unexpected command continuation request\n");
889 890
				return RESP_BAD;
			}
R
Robert Shearman 已提交
891
			if (socket_write(&imap->buf.sock, "\r\n", 2) != 2)
892 893 894 895 896 897
				return RESP_BAD;
			if (!cmdp->cb.cont)
				imap->literal_pending = 0;
			if (!tcmd)
				return DRV_OK;
		} else {
R
Robert Shearman 已提交
898
			tag = atoi(arg);
899 900 901
			for (pcmdp = &imap->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
				if (cmdp->tag == tag)
					goto gottag;
R
Robert Shearman 已提交
902
			fprintf(stderr, "IMAP error: unexpected tag %s\n", arg);
903
			return RESP_BAD;
J
Junio C Hamano 已提交
904
		gottag:
905 906 907 908 909
			if (!(*pcmdp = cmdp->next))
				imap->in_progress_append = pcmdp;
			imap->num_in_progress--;
			if (cmdp->cb.cont || cmdp->cb.data)
				imap->literal_pending = 0;
R
Robert Shearman 已提交
910 911
			arg = next_arg(&cmd);
			if (!strcmp("OK", arg))
912 913
				resp = DRV_OK;
			else {
R
Robert Shearman 已提交
914 915 916 917
				if (!strcmp("NO", arg)) {
					if (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp(cmd, "[TRYCREATE]", 11))) { /* SELECT, APPEND or UID COPY */
						p = strchr(cmdp->cmd, '"');
						if (!issue_imap_cmd(ctx, NULL, "CREATE \"%.*s\"", strchr(p + 1, '"') - p + 1, p)) {
918 919 920 921 922 923
							resp = RESP_BAD;
							goto normal;
						}
						/* not waiting here violates the spec, but a server that does not
						   grok this nonetheless violates it too. */
						cmdp->cb.create = 0;
R
Robert Shearman 已提交
924
						if (!(ncmdp = issue_imap_cmd(ctx, &cmdp->cb, "%s", cmdp->cmd))) {
925 926 927
							resp = RESP_BAD;
							goto normal;
						}
R
Robert Shearman 已提交
928 929
						free(cmdp->cmd);
						free(cmdp);
930 931 932 933 934 935 936
						if (!tcmd)
							return 0;	/* ignored */
						if (cmdp == tcmd)
							tcmd = ncmdp;
						continue;
					}
					resp = RESP_NO;
R
Robert Shearman 已提交
937
				} else /*if (!strcmp("BAD", arg))*/
938
					resp = RESP_BAD;
R
Robert Shearman 已提交
939 940
				fprintf(stderr, "IMAP command '%s' returned response (%s) - %s\n",
					 memcmp(cmdp->cmd, "LOGIN", 5) ?
941 942 943
							cmdp->cmd : "LOGIN <user> <pass>",
							arg, cmd ? cmd : "");
			}
R
Robert Shearman 已提交
944
			if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp)
945
				resp = resp2;
J
Junio C Hamano 已提交
946
		normal:
947
			if (cmdp->cb.done)
R
Robert Shearman 已提交
948 949 950 951
				cmdp->cb.done(ctx, cmdp, resp);
			free(cmdp->cb.data);
			free(cmdp->cmd);
			free(cmdp);
952 953 954 955 956 957 958
			if (!tcmd || tcmd == cmdp)
				return resp;
		}
	}
	/* not reached */
}

J
Junio C Hamano 已提交
959
static void imap_close_server(struct imap_store *ictx)
960
{
J
Junio C Hamano 已提交
961
	struct imap *imap = ictx->imap;
962 963

	if (imap->buf.sock.fd != -1) {
R
Robert Shearman 已提交
964 965
		imap_exec(ictx, NULL, "LOGOUT");
		socket_shutdown(&imap->buf.sock);
966
	}
R
Robert Shearman 已提交
967 968 969 970
	free_list(imap->ns_personal);
	free_list(imap->ns_other);
	free_list(imap->ns_shared);
	free(imap);
971 972
}

J
Junio C Hamano 已提交
973
static void imap_close_store(struct store *ctx)
974
{
J
Junio C Hamano 已提交
975
	imap_close_server((struct imap_store *)ctx);
R
Robert Shearman 已提交
976 977
	free_generic_messages(ctx->msgs);
	free(ctx);
978 979
}

J
Junio C Hamano 已提交
980
static struct store *imap_open_store(struct imap_server_conf *srvc)
981
{
J
Junio C Hamano 已提交
982 983
	struct imap_store *ctx;
	struct imap *imap;
984 985 986 987
	char *arg, *rsp;
	struct hostent *he;
	struct sockaddr_in addr;
	int s, a[2], preauth;
988
	pid_t pid;
989

R
Robert Shearman 已提交
990
	ctx = xcalloc(sizeof(*ctx), 1);
991

R
Robert Shearman 已提交
992
	ctx->imap = imap = xcalloc(sizeof(*imap), 1);
993 994 995 996 997 998
	imap->buf.sock.fd = -1;
	imap->in_progress_append = &imap->in_progress;

	/* open connection to IMAP server */

	if (srvc->tunnel) {
R
Robert Shearman 已提交
999
		imap_info("Starting tunnel '%s'... ", srvc->tunnel);
1000

R
Robert Shearman 已提交
1001 1002 1003
		if (socketpair(PF_UNIX, SOCK_STREAM, 0, a)) {
			perror("socketpair");
			exit(1);
1004 1005
		}

1006 1007
		pid = fork();
		if (pid < 0)
R
Robert Shearman 已提交
1008
			_exit(127);
1009
		if (!pid) {
R
Robert Shearman 已提交
1010 1011 1012 1013 1014 1015
			if (dup2(a[0], 0) == -1 || dup2(a[0], 1) == -1)
				_exit(127);
			close(a[0]);
			close(a[1]);
			execl("/bin/sh", "sh", "-c", srvc->tunnel, NULL);
			_exit(127);
1016 1017
		}

R
Robert Shearman 已提交
1018
		close(a[0]);
1019 1020 1021

		imap->buf.sock.fd = a[1];

R
Robert Shearman 已提交
1022
		imap_info("ok\n");
1023
	} else {
R
Robert Shearman 已提交
1024 1025
		memset(&addr, 0, sizeof(addr));
		addr.sin_port = htons(srvc->port);
1026 1027
		addr.sin_family = AF_INET;

R
Robert Shearman 已提交
1028 1029
		imap_info("Resolving %s... ", srvc->host);
		he = gethostbyname(srvc->host);
1030
		if (!he) {
R
Robert Shearman 已提交
1031
			perror("gethostbyname");
1032 1033
			goto bail;
		}
R
Robert Shearman 已提交
1034
		imap_info("ok\n");
1035 1036 1037

		addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);

R
Robert Shearman 已提交
1038
		s = socket(PF_INET, SOCK_STREAM, 0);
1039

R
Robert Shearman 已提交
1040 1041 1042 1043
		imap_info("Connecting to %s:%hu... ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
		if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
			close(s);
			perror("connect");
1044 1045 1046 1047 1048
			goto bail;
		}

		imap->buf.sock.fd = s;

R
Robert Shearman 已提交
1049 1050 1051 1052 1053
		if (srvc->use_ssl &&
		    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
			close(s);
			goto bail;
		}
R
Robert Shearman 已提交
1054
		imap_info("ok\n");
1055 1056 1057
	}

	/* read the greeting string */
R
Robert Shearman 已提交
1058 1059
	if (buffer_gets(&imap->buf, &rsp)) {
		fprintf(stderr, "IMAP error: no greeting response\n");
1060 1061
		goto bail;
	}
R
Robert Shearman 已提交
1062 1063 1064
	arg = next_arg(&rsp);
	if (!arg || *arg != '*' || (arg = next_arg(&rsp)) == NULL) {
		fprintf(stderr, "IMAP error: invalid greeting response\n");
1065 1066 1067
		goto bail;
	}
	preauth = 0;
R
Robert Shearman 已提交
1068
	if (!strcmp("PREAUTH", arg))
1069
		preauth = 1;
R
Robert Shearman 已提交
1070 1071
	else if (strcmp("OK", arg) != 0) {
		fprintf(stderr, "IMAP error: unknown greeting response\n");
1072 1073
		goto bail;
	}
R
Robert Shearman 已提交
1074 1075
	parse_response_code(ctx, NULL, rsp);
	if (!imap->caps && imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
1076 1077 1078
		goto bail;

	if (!preauth) {
R
Robert Shearman 已提交
1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090
#ifndef NO_OPENSSL
		if (!srvc->use_ssl && CAP(STARTTLS)) {
			if (imap_exec(ctx, 0, "STARTTLS") != RESP_OK)
				goto bail;
			if (ssl_socket_connect(&imap->buf.sock, 1,
					       srvc->ssl_verify))
				goto bail;
			/* capabilities may have changed, so get the new capabilities */
			if (imap_exec(ctx, 0, "CAPABILITY") != RESP_OK)
				goto bail;
		}
#endif
R
Robert Shearman 已提交
1091
		imap_info("Logging in...\n");
1092
		if (!srvc->user) {
R
Robert Shearman 已提交
1093
			fprintf(stderr, "Skipping server %s, no user\n", srvc->host);
1094 1095 1096 1097
			goto bail;
		}
		if (!srvc->pass) {
			char prompt[80];
R
Robert Shearman 已提交
1098 1099
			sprintf(prompt, "Password (%s@%s): ", srvc->user, srvc->host);
			arg = getpass(prompt);
1100
			if (!arg) {
R
Robert Shearman 已提交
1101 1102
				perror("getpass");
				exit(1);
1103 1104
			}
			if (!*arg) {
R
Robert Shearman 已提交
1105
				fprintf(stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host);
1106 1107 1108 1109 1110 1111
				goto bail;
			}
			/*
			 * getpass() returns a pointer to a static buffer.  make a copy
			 * for long term storage.
			 */
R
Robert Shearman 已提交
1112
			srvc->pass = xstrdup(arg);
1113 1114
		}
		if (CAP(NOLOGIN)) {
R
Robert Shearman 已提交
1115
			fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host);
1116 1117
			goto bail;
		}
R
Robert Shearman 已提交
1118
		if (!imap->buf.sock.ssl)
J
Junio C Hamano 已提交
1119 1120
			imap_warn("*** IMAP Warning *** Password is being "
				  "sent in the clear\n");
R
Robert Shearman 已提交
1121 1122
		if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
			fprintf(stderr, "IMAP error: LOGIN failed\n");
1123 1124 1125 1126 1127 1128
			goto bail;
		}
	} /* !preauth */

	ctx->prefix = "";
	ctx->trashnc = 1;
J
Junio C Hamano 已提交
1129
	return (struct store *)ctx;
1130

J
Junio C Hamano 已提交
1131
bail:
R
Robert Shearman 已提交
1132
	imap_close_store(&ctx->gen);
1133
	return NULL;
1134 1135
}

R
Robert Shearman 已提交
1136
static int imap_make_flags(int flags, char *buf)
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
{
	const char *s;
	unsigned i, d;

	for (i = d = 0; i < ARRAY_SIZE(Flags); i++)
		if (flags & (1 << i)) {
			buf[d++] = ' ';
			buf[d++] = '\\';
			for (s = Flags[i]; *s; s++)
				buf[d++] = *s;
		}
	buf[0] = '(';
	buf[d++] = ')';
	return d;
}

#define TUIDL 8

J
Junio C Hamano 已提交
1155
static int imap_store_msg(struct store *gctx, struct msg_data *data, int *uid)
1156
{
J
Junio C Hamano 已提交
1157 1158
	struct imap_store *ctx = (struct imap_store *)gctx;
	struct imap *imap = ctx->imap;
1159 1160 1161 1162 1163 1164 1165
	struct imap_cmd_cb cb;
	char *fmap, *buf;
	const char *prefix, *box;
	int ret, i, j, d, len, extra, nocr;
	int start, sbreak = 0, ebreak = 0;
	char flagstr[128], tuid[TUIDL * 2 + 1];

R
Robert Shearman 已提交
1166
	memset(&cb, 0, sizeof(cb));
1167 1168 1169 1170 1171 1172

	fmap = data->data;
	len = data->len;
	nocr = !data->crlf;
	extra = 0, i = 0;
	if (!CAP(UIDPLUS) && uid) {
J
Junio C Hamano 已提交
1173
	nloop:
1174 1175 1176 1177 1178 1179 1180 1181
		start = i;
		while (i < len)
			if (fmap[i++] == '\n') {
				extra += nocr;
				if (i - 2 + nocr == start) {
					sbreak = ebreak = i - 2 + nocr;
					goto mktid;
				}
R
Robert Shearman 已提交
1182
				if (!memcmp(fmap + start, "X-TUID: ", 8)) {
1183 1184 1185 1186 1187 1188
					extra -= (ebreak = i) - (sbreak = start) + nocr;
					goto mktid;
				}
				goto nloop;
			}
		/* invalid message */
R
Robert Shearman 已提交
1189
		free(fmap);
1190
		return DRV_MSG_BAD;
J
Junio C Hamano 已提交
1191
	mktid:
1192
		for (j = 0; j < TUIDL; j++)
R
Robert Shearman 已提交
1193
			sprintf(tuid + j * 2, "%02x", arc4_getbyte());
1194 1195 1196 1197 1198 1199 1200 1201
		extra += 8 + TUIDL * 2 + 2;
	}
	if (nocr)
		for (; i < len; i++)
			if (fmap[i] == '\n')
				extra++;

	cb.dlen = len + extra;
R
Robert Shearman 已提交
1202
	buf = cb.data = xmalloc(cb.dlen);
1203 1204 1205 1206 1207 1208 1209 1210 1211 1212
	i = 0;
	if (!CAP(UIDPLUS) && uid) {
		if (nocr) {
			for (; i < sbreak; i++)
				if (fmap[i] == '\n') {
					*buf++ = '\r';
					*buf++ = '\n';
				} else
					*buf++ = fmap[i];
		} else {
R
Robert Shearman 已提交
1213
			memcpy(buf, fmap, sbreak);
1214 1215
			buf += sbreak;
		}
R
Robert Shearman 已提交
1216
		memcpy(buf, "X-TUID: ", 8);
1217
		buf += 8;
R
Robert Shearman 已提交
1218
		memcpy(buf, tuid, TUIDL * 2);
1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231
		buf += TUIDL * 2;
		*buf++ = '\r';
		*buf++ = '\n';
		i = ebreak;
	}
	if (nocr) {
		for (; i < len; i++)
			if (fmap[i] == '\n') {
				*buf++ = '\r';
				*buf++ = '\n';
			} else
				*buf++ = fmap[i];
	} else
R
Robert Shearman 已提交
1232
		memcpy(buf, fmap + i, len - i);
1233

R
Robert Shearman 已提交
1234
	free(fmap);
1235 1236 1237

	d = 0;
	if (data->flags) {
R
Robert Shearman 已提交
1238
		d = imap_make_flags(data->flags, flagstr);
1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250
		flagstr[d++] = ' ';
	}
	flagstr[d] = 0;

	if (!uid) {
		box = gctx->conf->trash;
		prefix = ctx->prefix;
		cb.create = 1;
		if (ctx->trashnc)
			imap->caps = imap->rcaps & ~(1 << LITERALPLUS);
	} else {
		box = gctx->name;
R
Robert Shearman 已提交
1251
		prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
1252 1253 1254
		cb.create = 0;
	}
	cb.ctx = uid;
R
Robert Shearman 已提交
1255
	ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" %s", prefix, box, flagstr);
1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266
	imap->caps = imap->rcaps;
	if (ret != DRV_OK)
		return ret;
	if (!uid)
		ctx->trashnc = 0;
	else
		gctx->count++;

	return DRV_OK;
}

1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313
static void encode_html_chars(struct strbuf *p)
{
	int i;
	for (i = 0; i < p->len; i++) {
		if (p->buf[i] == '&')
			strbuf_splice(p, i, 1, "&amp;", 5);
		if (p->buf[i] == '<')
			strbuf_splice(p, i, 1, "&lt;", 4);
		if (p->buf[i] == '>')
			strbuf_splice(p, i, 1, "&gt;", 4);
		if (p->buf[i] == '"')
			strbuf_splice(p, i, 1, "&quot;", 6);
	}
}
static void wrap_in_html(struct msg_data *msg)
{
	struct strbuf buf = STRBUF_INIT;
	struct strbuf **lines;
	struct strbuf **p;
	static char *content_type = "Content-Type: text/html;\n";
	static char *pre_open = "<pre>\n";
	static char *pre_close = "</pre>\n";
	int added_header = 0;

	strbuf_attach(&buf, msg->data, msg->len, msg->len);
	lines = strbuf_split(&buf, '\n');
	strbuf_release(&buf);
	for (p = lines; *p; p++) {
		if (! added_header) {
			if ((*p)->len == 1 && *((*p)->buf) == '\n') {
				strbuf_addstr(&buf, content_type);
				strbuf_addbuf(&buf, *p);
				strbuf_addstr(&buf, pre_open);
				added_header = 1;
				continue;
			}
		}
		else
			encode_html_chars(*p);
		strbuf_addbuf(&buf, *p);
	}
	strbuf_addstr(&buf, pre_close);
	strbuf_list_free(lines);
	msg->len  = buf.len;
	msg->data = strbuf_detach(&buf, NULL);
}

1314 1315
#define CHUNKSIZE 0x1000

J
Junio C Hamano 已提交
1316
static int read_message(FILE *f, struct msg_data *msg)
1317
{
1318
	struct strbuf buf = STRBUF_INIT;
1319

1320 1321 1322 1323
	memset(msg, 0, sizeof(*msg));

	do {
		if (strbuf_fread(&buf, CHUNKSIZE, f) <= 0)
1324
			break;
1325 1326 1327
	} while (!feof(f));

	msg->len  = buf.len;
1328
	msg->data = strbuf_detach(&buf, NULL);
1329 1330 1331
	return msg->len;
}

J
Junio C Hamano 已提交
1332
static int count_messages(struct msg_data *msg)
1333 1334 1335 1336 1337
{
	int count = 0;
	char *p = msg->data;

	while (1) {
1338
		if (!prefixcmp(p, "From ")) {
1339 1340 1341
			count++;
			p += 5;
		}
R
Robert Shearman 已提交
1342
		p = strstr(p+5, "\nFrom ");
1343 1344 1345 1346 1347 1348 1349
		if (!p)
			break;
		p++;
	}
	return count;
}

J
Junio C Hamano 已提交
1350
static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
1351 1352 1353
{
	char *p, *data;

R
Robert Shearman 已提交
1354
	memset(msg, 0, sizeof *msg);
1355 1356 1357
	if (*ofs >= all_msgs->len)
		return 0;

R
Robert Shearman 已提交
1358
	data = &all_msgs->data[*ofs];
1359 1360
	msg->len = all_msgs->len - *ofs;

1361
	if (msg->len < 5 || prefixcmp(data, "From "))
1362 1363
		return 0;

R
Robert Shearman 已提交
1364
	p = strchr(data, '\n');
1365 1366 1367 1368 1369 1370 1371
	if (p) {
		p = &p[1];
		msg->len -= p-data;
		*ofs += p-data;
		data = p;
	}

R
Robert Shearman 已提交
1372
	p = strstr(data, "\nFrom ");
1373 1374 1375
	if (p)
		msg->len = &p[1] - data;

P
Pierre Habouzit 已提交
1376
	msg->data = xmemdupz(data, msg->len);
1377
	*ofs += msg->len;
J
Junio C Hamano 已提交
1378
	return 1;
1379 1380
}

J
Junio C Hamano 已提交
1381
static struct imap_server_conf server = {
1382 1383 1384 1385 1386 1387
	NULL,	/* name */
	NULL,	/* tunnel */
	NULL,	/* host */
	0,	/* port */
	NULL,	/* user */
	NULL,	/* pass */
R
Robert Shearman 已提交
1388 1389
	0,   	/* use_ssl */
	1,   	/* ssl_verify */
1390
	0,   	/* use_html */
1391 1392 1393 1394
};

static char *imap_folder;

R
Robert Shearman 已提交
1395
static int git_imap_config(const char *key, const char *val, void *cb)
1396 1397 1398
{
	char imap_key[] = "imap.";

R
Robert Shearman 已提交
1399
	if (strncmp(key, imap_key, sizeof imap_key - 1))
1400
		return 0;
1401 1402 1403 1404

	if (!val)
		return config_error_nonbool(key);

1405 1406
	key += sizeof imap_key - 1;

R
Robert Shearman 已提交
1407 1408 1409
	if (!strcmp("folder", key)) {
		imap_folder = xstrdup(val);
	} else if (!strcmp("host", key)) {
R
Robert Shearman 已提交
1410 1411 1412 1413 1414
		if (!prefixcmp(val, "imap:"))
			val += 5;
		else if (!prefixcmp(val, "imaps:")) {
			val += 6;
			server.use_ssl = 1;
1415
		}
1416
		if (!prefixcmp(val, "//"))
1417
			val += 2;
R
Robert Shearman 已提交
1418
		server.host = xstrdup(val);
J
Junio C Hamano 已提交
1419
	} else if (!strcmp("user", key))
R
Robert Shearman 已提交
1420 1421 1422 1423 1424 1425 1426 1427 1428
		server.user = xstrdup(val);
	else if (!strcmp("pass", key))
		server.pass = xstrdup(val);
	else if (!strcmp("port", key))
		server.port = git_config_int(key, val);
	else if (!strcmp("tunnel", key))
		server.tunnel = xstrdup(val);
	else if (!strcmp("sslverify", key))
		server.ssl_verify = git_config_bool(key, val);
1429 1430
	else if (!strcmp("preformattedHTML", key))
		server.use_html = git_config_bool(key, val);
1431 1432 1433
	return 0;
}

R
Robert Shearman 已提交
1434
int main(int argc, char **argv)
1435
{
J
Junio C Hamano 已提交
1436 1437
	struct msg_data all_msgs, msg;
	struct store *ctx = NULL;
1438 1439 1440 1441
	int uid = 0;
	int ofs = 0;
	int r;
	int total, n = 0;
1442
	int nongit_ok;
1443

1444 1445
	git_extract_argv0_path(argv[0]);

1446 1447 1448
	/* init the random number generator */
	arc4_init();

1449
	setup_git_directory_gently(&nongit_ok);
1450
	git_config(git_imap_config, NULL);
1451

R
Robert Shearman 已提交
1452 1453 1454
	if (!server.port)
		server.port = server.use_ssl ? 993 : 143;

1455
	if (!imap_folder) {
R
Robert Shearman 已提交
1456
		fprintf(stderr, "no imap store specified\n");
1457 1458
		return 1;
	}
1459
	if (!server.host) {
1460
		if (!server.tunnel) {
R
Robert Shearman 已提交
1461
			fprintf(stderr, "no imap host specified\n");
1462 1463 1464
			return 1;
		}
		server.host = "tunnel";
1465
	}
1466 1467

	/* read the messages */
R
Robert Shearman 已提交
1468
	if (!read_message(stdin, &all_msgs)) {
J
Junio C Hamano 已提交
1469
		fprintf(stderr, "nothing to send\n");
1470 1471 1472
		return 1;
	}

R
Robert Shearman 已提交
1473
	total = count_messages(&all_msgs);
1474
	if (!total) {
J
Junio C Hamano 已提交
1475
		fprintf(stderr, "no messages to send\n");
1476 1477 1478
		return 1;
	}

1479
	/* write it to the imap server */
R
Robert Shearman 已提交
1480
	ctx = imap_open_store(&server);
1481
	if (!ctx) {
J
Junio C Hamano 已提交
1482
		fprintf(stderr, "failed to open store\n");
1483 1484 1485
		return 1;
	}

J
Junio C Hamano 已提交
1486
	fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
1487 1488 1489
	ctx->name = imap_folder;
	while (1) {
		unsigned percent = n * 100 / total;
R
Robert Shearman 已提交
1490 1491
		fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
		if (!split_msg(&all_msgs, &msg, &ofs))
1492
			break;
1493 1494
		if (server.use_html)
			wrap_in_html(&msg);
R
Robert Shearman 已提交
1495
		r = imap_store_msg(ctx, &msg, &uid);
J
Junio C Hamano 已提交
1496 1497
		if (r != DRV_OK)
			break;
1498 1499
		n++;
	}
R
Robert Shearman 已提交
1500
	fprintf(stderr, "\n");
1501

R
Robert Shearman 已提交
1502
	imap_close_store(ctx);
1503 1504 1505

	return 0;
}