git

Форк
0
/
imap-send.c 
1592 строки · 36.7 Кб
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, see <https://www.gnu.org/licenses/>.
22
 */
23

24
#define USE_THE_REPOSITORY_VARIABLE
25

26
#include "git-compat-util.h"
27
#include "config.h"
28
#include "credential.h"
29
#include "gettext.h"
30
#include "run-command.h"
31
#include "parse-options.h"
32
#include "setup.h"
33
#include "strbuf.h"
34
#if defined(NO_OPENSSL) && !defined(HAVE_OPENSSL_CSPRNG)
35
typedef void *SSL;
36
#endif
37
#ifdef USE_CURL_FOR_IMAP_SEND
38
#include "http.h"
39
#endif
40

41
#if defined(USE_CURL_FOR_IMAP_SEND)
42
/* Always default to curl if it's available. */
43
#define USE_CURL_DEFAULT 1
44
#else
45
/* We don't have curl, so continue to use the historical implementation */
46
#define USE_CURL_DEFAULT 0
47
#endif
48

49
static int verbosity;
50
static int use_curl = USE_CURL_DEFAULT;
51

52
static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
53

54
static struct option imap_send_options[] = {
55
	OPT__VERBOSITY(&verbosity),
56
	OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
57
	OPT_END()
58
};
59

60
#undef DRV_OK
61
#define DRV_OK          0
62
#define DRV_MSG_BAD     -1
63
#define DRV_BOX_BAD     -2
64
#define DRV_STORE_BAD   -3
65

66
__attribute__((format (printf, 1, 2)))
67
static void imap_info(const char *, ...);
68
__attribute__((format (printf, 1, 2)))
69
static void imap_warn(const char *, ...);
70

71
static char *next_arg(char **);
72

73
struct imap_server_conf {
74
	char *tunnel;
75
	char *host;
76
	int port;
77
	char *folder;
78
	char *user;
79
	char *pass;
80
	int use_ssl;
81
	int ssl_verify;
82
	int use_html;
83
	char *auth_method;
84
};
85

86
struct imap_socket {
87
	int fd[2];
88
	SSL *ssl;
89
};
90

91
struct imap_buffer {
92
	struct imap_socket sock;
93
	int bytes;
94
	int offset;
95
	char buf[1024];
96
};
97

98
struct imap_cmd;
99

100
struct imap {
101
	int uidnext; /* from SELECT responses */
102
	unsigned caps, rcaps; /* CAPABILITY results */
103
	/* command queue */
104
	int nexttag, num_in_progress, literal_pending;
105
	struct imap_cmd *in_progress, **in_progress_append;
106
	struct imap_buffer buf; /* this is BIG, so put it last */
107
};
108

109
struct imap_store {
110
	const struct imap_server_conf *cfg;
111
	/* currently open mailbox */
112
	const char *name; /* foreign! maybe preset? */
113
	int uidvalidity;
114
	struct imap *imap;
115
	const char *prefix;
116
};
117

118
struct imap_cmd_cb {
119
	int (*cont)(struct imap_store *ctx, const char *prompt);
120
	void *ctx;
121
	char *data;
122
	int dlen;
123
};
124

125
struct imap_cmd {
126
	struct imap_cmd *next;
127
	struct imap_cmd_cb cb;
128
	char *cmd;
129
	int tag;
130
};
131

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

134
enum CAPABILITY {
135
	NOLOGIN = 0,
136
	UIDPLUS,
137
	LITERALPLUS,
138
	NAMESPACE,
139
	STARTTLS,
140
	AUTH_CRAM_MD5
141
};
142

143
static const char *cap_list[] = {
144
	"LOGINDISABLED",
145
	"UIDPLUS",
146
	"LITERAL+",
147
	"NAMESPACE",
148
	"STARTTLS",
149
	"AUTH=CRAM-MD5",
150
};
151

152
#define RESP_OK    0
153
#define RESP_NO    1
154
#define RESP_BAD   2
155

156
static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd);
157

158

159
#ifndef NO_OPENSSL
160
static void ssl_socket_perror(const char *func)
161
{
162
	fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), NULL));
163
}
164
#endif
165

166
static void socket_perror(const char *func, struct imap_socket *sock, int ret)
167
{
168
#ifndef NO_OPENSSL
169
	if (sock->ssl) {
170
		int sslerr = SSL_get_error(sock->ssl, ret);
171
		switch (sslerr) {
172
		case SSL_ERROR_NONE:
173
			break;
174
		case SSL_ERROR_SYSCALL:
175
			perror("SSL_connect");
176
			break;
177
		default:
178
			ssl_socket_perror("SSL_connect");
179
			break;
180
		}
181
	} else
182
#endif
183
	{
184
		if (ret < 0)
185
			perror(func);
186
		else
187
			fprintf(stderr, "%s: unexpected EOF\n", func);
188
	}
189
	/* mark as used to appease -Wunused-parameter with NO_OPENSSL */
190
	(void)sock;
191
}
192

193
#ifdef NO_OPENSSL
194
static int ssl_socket_connect(struct imap_socket *sock UNUSED,
195
			      const struct imap_server_conf *cfg UNUSED,
196
			      int use_tls_only UNUSED)
197
{
198
	fprintf(stderr, "SSL requested but SSL support not compiled in\n");
199
	return -1;
200
}
201

202
#else
203

204
static int host_matches(const char *host, const char *pattern)
205
{
206
	if (pattern[0] == '*' && pattern[1] == '.') {
207
		pattern += 2;
208
		if (!(host = strchr(host, '.')))
209
			return 0;
210
		host++;
211
	}
212

213
	return *host && *pattern && !strcasecmp(host, pattern);
214
}
215

216
static int verify_hostname(X509 *cert, const char *hostname)
217
{
218
	int len;
219
	X509_NAME *subj;
220
	char cname[1000];
221
	int i, found;
222
	STACK_OF(GENERAL_NAME) *subj_alt_names;
223

224
	/* try the DNS subjectAltNames */
225
	found = 0;
226
	if ((subj_alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL))) {
227
		int num_subj_alt_names = sk_GENERAL_NAME_num(subj_alt_names);
228
		for (i = 0; !found && i < num_subj_alt_names; i++) {
229
			GENERAL_NAME *subj_alt_name = sk_GENERAL_NAME_value(subj_alt_names, i);
230
			if (subj_alt_name->type == GEN_DNS &&
231
			    strlen((const char *)subj_alt_name->d.ia5->data) == (size_t)subj_alt_name->d.ia5->length &&
232
			    host_matches(hostname, (const char *)(subj_alt_name->d.ia5->data)))
233
				found = 1;
234
		}
235
		sk_GENERAL_NAME_pop_free(subj_alt_names, GENERAL_NAME_free);
236
	}
237
	if (found)
238
		return 0;
239

240
	/* try the common name */
241
	if (!(subj = X509_get_subject_name(cert)))
242
		return error("cannot get certificate subject");
243
	if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
244
		return error("cannot get certificate common name");
245
	if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
246
		return 0;
247
	return error("certificate owner '%s' does not match hostname '%s'",
248
		     cname, hostname);
249
}
250

251
static int ssl_socket_connect(struct imap_socket *sock,
252
			      const struct imap_server_conf *cfg,
253
			      int use_tls_only)
254
{
255
#if (OPENSSL_VERSION_NUMBER >= 0x10000000L)
256
	const SSL_METHOD *meth;
257
#else
258
	SSL_METHOD *meth;
259
#endif
260
	SSL_CTX *ctx;
261
	int ret;
262
	X509 *cert;
263

264
	SSL_library_init();
265
	SSL_load_error_strings();
266

267
	meth = SSLv23_method();
268
	if (!meth) {
269
		ssl_socket_perror("SSLv23_method");
270
		return -1;
271
	}
272

273
	ctx = SSL_CTX_new(meth);
274
	if (!ctx) {
275
		ssl_socket_perror("SSL_CTX_new");
276
		return -1;
277
	}
278

279
	if (use_tls_only)
280
		SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
281

282
	if (cfg->ssl_verify)
283
		SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
284

285
	if (!SSL_CTX_set_default_verify_paths(ctx)) {
286
		ssl_socket_perror("SSL_CTX_set_default_verify_paths");
287
		return -1;
288
	}
289
	sock->ssl = SSL_new(ctx);
290
	if (!sock->ssl) {
291
		ssl_socket_perror("SSL_new");
292
		return -1;
293
	}
294
	if (!SSL_set_rfd(sock->ssl, sock->fd[0])) {
295
		ssl_socket_perror("SSL_set_rfd");
296
		return -1;
297
	}
298
	if (!SSL_set_wfd(sock->ssl, sock->fd[1])) {
299
		ssl_socket_perror("SSL_set_wfd");
300
		return -1;
301
	}
302

303
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
304
	/*
305
	 * SNI (RFC4366)
306
	 * OpenSSL does not document this function, but the implementation
307
	 * returns 1 on success, 0 on failure after calling SSLerr().
308
	 */
309
	ret = SSL_set_tlsext_host_name(sock->ssl, cfg->host);
310
	if (ret != 1)
311
		warning("SSL_set_tlsext_host_name(%s) failed.", cfg->host);
312
#endif
313

314
	ret = SSL_connect(sock->ssl);
315
	if (ret <= 0) {
316
		socket_perror("SSL_connect", sock, ret);
317
		return -1;
318
	}
319

320
	if (cfg->ssl_verify) {
321
		/* make sure the hostname matches that of the certificate */
322
		cert = SSL_get_peer_certificate(sock->ssl);
323
		if (!cert)
324
			return error("unable to get peer certificate.");
325
		if (verify_hostname(cert, cfg->host) < 0)
326
			return -1;
327
	}
328

329
	return 0;
330
}
331
#endif
332

333
static int socket_read(struct imap_socket *sock, char *buf, int len)
334
{
335
	ssize_t n;
336
#ifndef NO_OPENSSL
337
	if (sock->ssl)
338
		n = SSL_read(sock->ssl, buf, len);
339
	else
340
#endif
341
		n = xread(sock->fd[0], buf, len);
342
	if (n <= 0) {
343
		socket_perror("read", sock, n);
344
		close(sock->fd[0]);
345
		close(sock->fd[1]);
346
		sock->fd[0] = sock->fd[1] = -1;
347
	}
348
	return n;
349
}
350

351
static int socket_write(struct imap_socket *sock, const char *buf, int len)
352
{
353
	int n;
354
#ifndef NO_OPENSSL
355
	if (sock->ssl)
356
		n = SSL_write(sock->ssl, buf, len);
357
	else
358
#endif
359
		n = write_in_full(sock->fd[1], buf, len);
360
	if (n != len) {
361
		socket_perror("write", sock, n);
362
		close(sock->fd[0]);
363
		close(sock->fd[1]);
364
		sock->fd[0] = sock->fd[1] = -1;
365
	}
366
	return n;
367
}
368

369
static void socket_shutdown(struct imap_socket *sock)
370
{
371
#ifndef NO_OPENSSL
372
	if (sock->ssl) {
373
		SSL_shutdown(sock->ssl);
374
		SSL_free(sock->ssl);
375
	}
376
#endif
377
	close(sock->fd[0]);
378
	close(sock->fd[1]);
379
}
380

381
/* simple line buffering */
382
static int buffer_gets(struct imap_buffer *b, char **s)
383
{
384
	int n;
385
	int start = b->offset;
386

387
	*s = b->buf + start;
388

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

396
				assert(start <= b->bytes);
397
				n = b->bytes - start;
398

399
				if (n)
400
					memmove(b->buf, b->buf + start, n);
401
				b->offset -= start;
402
				b->bytes = n;
403
				start = 0;
404
			}
405

406
			n = socket_read(&b->sock, b->buf + b->bytes,
407
					 sizeof(b->buf) - b->bytes);
408

409
			if (n <= 0)
410
				return -1;
411

412
			b->bytes += n;
413
		}
414

415
		if (b->buf[b->offset] == '\r') {
416
			assert(b->offset + 1 < b->bytes);
417
			if (b->buf[b->offset + 1] == '\n') {
418
				b->buf[b->offset] = 0;  /* terminate the string */
419
				b->offset += 2; /* next line */
420
				if (0 < verbosity)
421
					puts(*s);
422
				return 0;
423
			}
424
		}
425

426
		b->offset++;
427
	}
428
	/* not reached */
429
}
430

431
__attribute__((format (printf, 1, 2)))
432
static void imap_info(const char *msg, ...)
433
{
434
	va_list va;
435

436
	if (0 <= verbosity) {
437
		va_start(va, msg);
438
		vprintf(msg, va);
439
		va_end(va);
440
		fflush(stdout);
441
	}
442
}
443

444
__attribute__((format (printf, 1, 2)))
445
static void imap_warn(const char *msg, ...)
446
{
447
	va_list va;
448

449
	if (-2 < verbosity) {
450
		va_start(va, msg);
451
		vfprintf(stderr, msg, va);
452
		va_end(va);
453
	}
454
}
455

456
static char *next_arg(char **s)
457
{
458
	char *ret;
459

460
	if (!s || !*s)
461
		return NULL;
462
	while (isspace((unsigned char) **s))
463
		(*s)++;
464
	if (!**s) {
465
		*s = NULL;
466
		return NULL;
467
	}
468
	if (**s == '"') {
469
		++*s;
470
		ret = *s;
471
		*s = strchr(*s, '"');
472
	} else {
473
		ret = *s;
474
		while (**s && !isspace((unsigned char) **s))
475
			(*s)++;
476
	}
477
	if (*s) {
478
		if (**s)
479
			*(*s)++ = 0;
480
		if (!**s)
481
			*s = NULL;
482
	}
483
	return ret;
484
}
485

486
static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx,
487
				       struct imap_cmd_cb *cb,
488
				       const char *fmt, va_list ap)
489
{
490
	struct imap *imap = ctx->imap;
491
	struct imap_cmd *cmd;
492
	int n;
493
	struct strbuf buf = STRBUF_INIT;
494

495
	cmd = xmalloc(sizeof(struct imap_cmd));
496
	cmd->cmd = xstrvfmt(fmt, ap);
497
	cmd->tag = ++imap->nexttag;
498

499
	if (cb)
500
		cmd->cb = *cb;
501
	else
502
		memset(&cmd->cb, 0, sizeof(cmd->cb));
503

504
	while (imap->literal_pending)
505
		get_cmd_result(ctx, NULL);
506

507
	if (!cmd->cb.data)
508
		strbuf_addf(&buf, "%d %s\r\n", cmd->tag, cmd->cmd);
509
	else
510
		strbuf_addf(&buf, "%d %s{%d%s}\r\n", cmd->tag, cmd->cmd,
511
			    cmd->cb.dlen, CAP(LITERALPLUS) ? "+" : "");
512
	if (buf.len > INT_MAX)
513
		die("imap command overflow!");
514

515
	if (0 < verbosity) {
516
		if (imap->num_in_progress)
517
			printf("(%d in progress) ", imap->num_in_progress);
518
		if (!starts_with(cmd->cmd, "LOGIN"))
519
			printf(">>> %s", buf.buf);
520
		else
521
			printf(">>> %d LOGIN <user> <pass>\n", cmd->tag);
522
	}
523
	if (socket_write(&imap->buf.sock, buf.buf, buf.len) != buf.len) {
524
		free(cmd->cmd);
525
		free(cmd);
526
		if (cb)
527
			free(cb->data);
528
		strbuf_release(&buf);
529
		return NULL;
530
	}
531
	strbuf_release(&buf);
532
	if (cmd->cb.data) {
533
		if (CAP(LITERALPLUS)) {
534
			n = socket_write(&imap->buf.sock, cmd->cb.data, cmd->cb.dlen);
535
			free(cmd->cb.data);
536
			if (n != cmd->cb.dlen ||
537
			    socket_write(&imap->buf.sock, "\r\n", 2) != 2) {
538
				free(cmd->cmd);
539
				free(cmd);
540
				return NULL;
541
			}
542
			cmd->cb.data = NULL;
543
		} else
544
			imap->literal_pending = 1;
545
	} else if (cmd->cb.cont)
546
		imap->literal_pending = 1;
547
	cmd->next = NULL;
548
	*imap->in_progress_append = cmd;
549
	imap->in_progress_append = &cmd->next;
550
	imap->num_in_progress++;
551
	return cmd;
552
}
553

554
__attribute__((format (printf, 3, 4)))
555
static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb,
556
		     const char *fmt, ...)
557
{
558
	va_list ap;
559
	struct imap_cmd *cmdp;
560

561
	va_start(ap, fmt);
562
	cmdp = issue_imap_cmd(ctx, cb, fmt, ap);
563
	va_end(ap);
564
	if (!cmdp)
565
		return RESP_BAD;
566

567
	return get_cmd_result(ctx, cmdp);
568
}
569

570
__attribute__((format (printf, 3, 4)))
571
static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb,
572
		       const char *fmt, ...)
573
{
574
	va_list ap;
575
	struct imap_cmd *cmdp;
576

577
	va_start(ap, fmt);
578
	cmdp = issue_imap_cmd(ctx, cb, fmt, ap);
579
	va_end(ap);
580
	if (!cmdp)
581
		return DRV_STORE_BAD;
582

583
	switch (get_cmd_result(ctx, cmdp)) {
584
	case RESP_BAD: return DRV_STORE_BAD;
585
	case RESP_NO: return DRV_MSG_BAD;
586
	default: return DRV_OK;
587
	}
588
}
589

590
static int skip_imap_list_l(char **sp, int level)
591
{
592
	char *s = *sp;
593

594
	for (;;) {
595
		while (isspace((unsigned char)*s))
596
			s++;
597
		if (level && *s == ')') {
598
			s++;
599
			break;
600
		}
601
		if (*s == '(') {
602
			/* sublist */
603
			s++;
604
			if (skip_imap_list_l(&s, level + 1))
605
				goto bail;
606
		} else if (*s == '"') {
607
			/* quoted string */
608
			s++;
609
			for (; *s != '"'; s++)
610
				if (!*s)
611
					goto bail;
612
			s++;
613
		} else {
614
			/* atom */
615
			for (; *s && !isspace((unsigned char)*s); s++)
616
				if (level && *s == ')')
617
					break;
618
		}
619

620
		if (!level)
621
			break;
622
		if (!*s)
623
			goto bail;
624
	}
625
	*sp = s;
626
	return 0;
627

628
bail:
629
	return -1;
630
}
631

632
static void skip_list(char **sp)
633
{
634
	skip_imap_list_l(sp, 0);
635
}
636

637
static void parse_capability(struct imap *imap, char *cmd)
638
{
639
	char *arg;
640
	unsigned i;
641

642
	imap->caps = 0x80000000;
643
	while ((arg = next_arg(&cmd)))
644
		for (i = 0; i < ARRAY_SIZE(cap_list); i++)
645
			if (!strcmp(cap_list[i], arg))
646
				imap->caps |= 1 << i;
647
	imap->rcaps = imap->caps;
648
}
649

650
static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb,
651
			       char *s)
652
{
653
	struct imap *imap = ctx->imap;
654
	char *arg, *p;
655

656
	if (!s || *s != '[')
657
		return RESP_OK;		/* no response code */
658
	s++;
659
	if (!(p = strchr(s, ']'))) {
660
		fprintf(stderr, "IMAP error: malformed response code\n");
661
		return RESP_BAD;
662
	}
663
	*p++ = 0;
664
	arg = next_arg(&s);
665
	if (!arg) {
666
		fprintf(stderr, "IMAP error: empty response code\n");
667
		return RESP_BAD;
668
	}
669
	if (!strcmp("UIDVALIDITY", arg)) {
670
		if (!(arg = next_arg(&s)) || !(ctx->uidvalidity = atoi(arg))) {
671
			fprintf(stderr, "IMAP error: malformed UIDVALIDITY status\n");
672
			return RESP_BAD;
673
		}
674
	} else if (!strcmp("UIDNEXT", arg)) {
675
		if (!(arg = next_arg(&s)) || !(imap->uidnext = atoi(arg))) {
676
			fprintf(stderr, "IMAP error: malformed NEXTUID status\n");
677
			return RESP_BAD;
678
		}
679
	} else if (!strcmp("CAPABILITY", arg)) {
680
		parse_capability(imap, s);
681
	} else if (!strcmp("ALERT", arg)) {
682
		/* RFC2060 says that these messages MUST be displayed
683
		 * to the user
684
		 */
685
		for (; isspace((unsigned char)*p); p++);
686
		fprintf(stderr, "*** IMAP ALERT *** %s\n", p);
687
	} else if (cb && cb->ctx && !strcmp("APPENDUID", arg)) {
688
		if (!(arg = next_arg(&s)) || !(ctx->uidvalidity = atoi(arg)) ||
689
		    !(arg = next_arg(&s)) || !(*(int *)cb->ctx = atoi(arg))) {
690
			fprintf(stderr, "IMAP error: malformed APPENDUID status\n");
691
			return RESP_BAD;
692
		}
693
	}
694
	return RESP_OK;
695
}
696

697
static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
698
{
699
	struct imap *imap = ctx->imap;
700
	struct imap_cmd *cmdp, **pcmdp;
701
	char *cmd;
702
	const char *arg, *arg1;
703
	int n, resp, resp2, tag;
704

705
	for (;;) {
706
		if (buffer_gets(&imap->buf, &cmd))
707
			return RESP_BAD;
708

709
		arg = next_arg(&cmd);
710
		if (!arg) {
711
			fprintf(stderr, "IMAP error: empty response\n");
712
			return RESP_BAD;
713
		}
714
		if (*arg == '*') {
715
			arg = next_arg(&cmd);
716
			if (!arg) {
717
				fprintf(stderr, "IMAP error: unable to parse untagged response\n");
718
				return RESP_BAD;
719
			}
720

721
			if (!strcmp("NAMESPACE", arg)) {
722
				/* rfc2342 NAMESPACE response. */
723
				skip_list(&cmd); /* Personal mailboxes */
724
				skip_list(&cmd); /* Others' mailboxes */
725
				skip_list(&cmd); /* Shared mailboxes */
726
			} else if (!strcmp("OK", arg) || !strcmp("BAD", arg) ||
727
				   !strcmp("NO", arg) || !strcmp("BYE", arg)) {
728
				if ((resp = parse_response_code(ctx, NULL, cmd)) != RESP_OK)
729
					return resp;
730
			} else if (!strcmp("CAPABILITY", arg)) {
731
				parse_capability(imap, cmd);
732
			} else if ((arg1 = next_arg(&cmd))) {
733
				; /*
734
				   * Unhandled response-data with at least two words.
735
				   * Ignore it.
736
				   *
737
				   * NEEDSWORK: Previously this case handled '<num> EXISTS'
738
				   * and '<num> RECENT' but as a probably-unintended side
739
				   * effect it ignores other unrecognized two-word
740
				   * responses.  imap-send doesn't ever try to read
741
				   * messages or mailboxes these days, so consider
742
				   * eliminating this case.
743
				   */
744
			} else {
745
				fprintf(stderr, "IMAP error: unable to parse untagged response\n");
746
				return RESP_BAD;
747
			}
748
		} else if (!imap->in_progress) {
749
			fprintf(stderr, "IMAP error: unexpected reply: %s %s\n", arg, cmd ? cmd : "");
750
			return RESP_BAD;
751
		} else if (*arg == '+') {
752
			/* This can happen only with the last command underway, as
753
			   it enforces a round-trip. */
754
			cmdp = (struct imap_cmd *)((char *)imap->in_progress_append -
755
			       offsetof(struct imap_cmd, next));
756
			if (cmdp->cb.data) {
757
				n = socket_write(&imap->buf.sock, cmdp->cb.data, cmdp->cb.dlen);
758
				FREE_AND_NULL(cmdp->cb.data);
759
				if (n != (int)cmdp->cb.dlen)
760
					return RESP_BAD;
761
			} else if (cmdp->cb.cont) {
762
				if (cmdp->cb.cont(ctx, cmd))
763
					return RESP_BAD;
764
			} else {
765
				fprintf(stderr, "IMAP error: unexpected command continuation request\n");
766
				return RESP_BAD;
767
			}
768
			if (socket_write(&imap->buf.sock, "\r\n", 2) != 2)
769
				return RESP_BAD;
770
			if (!cmdp->cb.cont)
771
				imap->literal_pending = 0;
772
			if (!tcmd)
773
				return DRV_OK;
774
		} else {
775
			tag = atoi(arg);
776
			for (pcmdp = &imap->in_progress; (cmdp = *pcmdp); pcmdp = &cmdp->next)
777
				if (cmdp->tag == tag)
778
					goto gottag;
779
			fprintf(stderr, "IMAP error: unexpected tag %s\n", arg);
780
			return RESP_BAD;
781
		gottag:
782
			if (!(*pcmdp = cmdp->next))
783
				imap->in_progress_append = pcmdp;
784
			imap->num_in_progress--;
785
			if (cmdp->cb.cont || cmdp->cb.data)
786
				imap->literal_pending = 0;
787
			arg = next_arg(&cmd);
788
			if (!arg)
789
				arg = "";
790
			if (!strcmp("OK", arg))
791
				resp = DRV_OK;
792
			else {
793
				if (!strcmp("NO", arg))
794
					resp = RESP_NO;
795
				else /*if (!strcmp("BAD", arg))*/
796
					resp = RESP_BAD;
797
				fprintf(stderr, "IMAP command '%s' returned response (%s) - %s\n",
798
					!starts_with(cmdp->cmd, "LOGIN") ?
799
							cmdp->cmd : "LOGIN <user> <pass>",
800
							arg, cmd ? cmd : "");
801
			}
802
			if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp)
803
				resp = resp2;
804
			free(cmdp->cb.data);
805
			free(cmdp->cmd);
806
			free(cmdp);
807
			if (!tcmd || tcmd == cmdp)
808
				return resp;
809
		}
810
	}
811
	/* not reached */
812
}
813

814
static void imap_close_server(struct imap_store *ictx)
815
{
816
	struct imap *imap = ictx->imap;
817

818
	if (imap->buf.sock.fd[0] != -1) {
819
		imap_exec(ictx, NULL, "LOGOUT");
820
		socket_shutdown(&imap->buf.sock);
821
	}
822
	free(imap);
823
}
824

825
static void imap_close_store(struct imap_store *ctx)
826
{
827
	imap_close_server(ctx);
828
	free(ctx);
829
}
830

831
#ifndef NO_OPENSSL
832

833
/*
834
 * hexchar() and cram() functions are based on the code from the isync
835
 * project (https://isync.sourceforge.io/).
836
 */
837
static char hexchar(unsigned int b)
838
{
839
	return b < 10 ? '0' + b : 'a' + (b - 10);
840
}
841

842
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
843
static char *cram(const char *challenge_64, const char *user, const char *pass)
844
{
845
	int i, resp_len, encoded_len, decoded_len;
846
	unsigned char hash[16];
847
	char hex[33];
848
	char *response, *response_64, *challenge;
849

850
	/*
851
	 * length of challenge_64 (i.e. base-64 encoded string) is a good
852
	 * enough upper bound for challenge (decoded result).
853
	 */
854
	encoded_len = strlen(challenge_64);
855
	challenge = xmalloc(encoded_len);
856
	decoded_len = EVP_DecodeBlock((unsigned char *)challenge,
857
				      (unsigned char *)challenge_64, encoded_len);
858
	if (decoded_len < 0)
859
		die("invalid challenge %s", challenge_64);
860
	if (!HMAC(EVP_md5(), pass, strlen(pass), (unsigned char *)challenge, decoded_len, hash, NULL))
861
		die("HMAC error");
862

863
	hex[32] = 0;
864
	for (i = 0; i < 16; i++) {
865
		hex[2 * i] = hexchar((hash[i] >> 4) & 0xf);
866
		hex[2 * i + 1] = hexchar(hash[i] & 0xf);
867
	}
868

869
	/* response: "<user> <digest in hex>" */
870
	response = xstrfmt("%s %s", user, hex);
871
	resp_len = strlen(response);
872

873
	response_64 = xmallocz(ENCODED_SIZE(resp_len));
874
	encoded_len = EVP_EncodeBlock((unsigned char *)response_64,
875
				      (unsigned char *)response, resp_len);
876
	if (encoded_len < 0)
877
		die("EVP_EncodeBlock error");
878
	return (char *)response_64;
879
}
880

881
#else
882

883
static char *cram(const char *challenge_64 UNUSED,
884
		  const char *user UNUSED,
885
		  const char *pass UNUSED)
886
{
887
	die("If you want to use CRAM-MD5 authenticate method, "
888
	    "you have to build git-imap-send with OpenSSL library.");
889
}
890

891
#endif
892

893
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
894
{
895
	int ret;
896
	char *response;
897

898
	response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
899

900
	ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
901
	if (ret != strlen(response))
902
		return error("IMAP error: sending response failed");
903

904
	free(response);
905

906
	return 0;
907
}
908

909
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
910
{
911
	if (srvc->user && srvc->pass)
912
		return;
913

914
	cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
915
	cred->host = xstrdup(srvc->host);
916

917
	cred->username = xstrdup_or_null(srvc->user);
918
	cred->password = xstrdup_or_null(srvc->pass);
919

920
	credential_fill(cred, 1);
921

922
	if (!srvc->user)
923
		srvc->user = xstrdup(cred->username);
924
	if (!srvc->pass)
925
		srvc->pass = xstrdup(cred->password);
926
}
927

928
static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const char *folder)
929
{
930
	struct credential cred = CREDENTIAL_INIT;
931
	struct imap_store *ctx;
932
	struct imap *imap;
933
	char *arg, *rsp;
934
	int s = -1, preauth;
935

936
	CALLOC_ARRAY(ctx, 1);
937

938
	ctx->cfg = srvc;
939
	ctx->imap = CALLOC_ARRAY(imap, 1);
940
	imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
941
	imap->in_progress_append = &imap->in_progress;
942

943
	/* open connection to IMAP server */
944

945
	if (srvc->tunnel) {
946
		struct child_process tunnel = CHILD_PROCESS_INIT;
947

948
		imap_info("Starting tunnel '%s'... ", srvc->tunnel);
949

950
		strvec_push(&tunnel.args, srvc->tunnel);
951
		tunnel.use_shell = 1;
952
		tunnel.in = -1;
953
		tunnel.out = -1;
954
		if (start_command(&tunnel))
955
			die("cannot start proxy %s", srvc->tunnel);
956

957
		imap->buf.sock.fd[0] = tunnel.out;
958
		imap->buf.sock.fd[1] = tunnel.in;
959

960
		imap_info("ok\n");
961
	} else {
962
#ifndef NO_IPV6
963
		struct addrinfo hints, *ai0, *ai;
964
		int gai;
965
		char portstr[6];
966

967
		xsnprintf(portstr, sizeof(portstr), "%d", srvc->port);
968

969
		memset(&hints, 0, sizeof(hints));
970
		hints.ai_socktype = SOCK_STREAM;
971
		hints.ai_protocol = IPPROTO_TCP;
972

973
		imap_info("Resolving %s... ", srvc->host);
974
		gai = getaddrinfo(srvc->host, portstr, &hints, &ai);
975
		if (gai) {
976
			fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
977
			goto bail;
978
		}
979
		imap_info("ok\n");
980

981
		for (ai0 = ai; ai; ai = ai->ai_next) {
982
			char addr[NI_MAXHOST];
983

984
			s = socket(ai->ai_family, ai->ai_socktype,
985
				   ai->ai_protocol);
986
			if (s < 0)
987
				continue;
988

989
			getnameinfo(ai->ai_addr, ai->ai_addrlen, addr,
990
				    sizeof(addr), NULL, 0, NI_NUMERICHOST);
991
			imap_info("Connecting to [%s]:%s... ", addr, portstr);
992

993
			if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
994
				close(s);
995
				s = -1;
996
				perror("connect");
997
				continue;
998
			}
999

1000
			break;
1001
		}
1002
		freeaddrinfo(ai0);
1003
#else /* NO_IPV6 */
1004
		struct hostent *he;
1005
		struct sockaddr_in addr;
1006

1007
		memset(&addr, 0, sizeof(addr));
1008
		addr.sin_port = htons(srvc->port);
1009
		addr.sin_family = AF_INET;
1010

1011
		imap_info("Resolving %s... ", srvc->host);
1012
		he = gethostbyname(srvc->host);
1013
		if (!he) {
1014
			perror("gethostbyname");
1015
			goto bail;
1016
		}
1017
		imap_info("ok\n");
1018

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

1021
		s = socket(PF_INET, SOCK_STREAM, 0);
1022

1023
		imap_info("Connecting to %s:%hu... ", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
1024
		if (connect(s, (struct sockaddr *)&addr, sizeof(addr))) {
1025
			close(s);
1026
			s = -1;
1027
			perror("connect");
1028
		}
1029
#endif
1030
		if (s < 0) {
1031
			fputs("Error: unable to connect to server.\n", stderr);
1032
			goto bail;
1033
		}
1034

1035
		imap->buf.sock.fd[0] = s;
1036
		imap->buf.sock.fd[1] = dup(s);
1037

1038
		if (srvc->use_ssl &&
1039
		    ssl_socket_connect(&imap->buf.sock, srvc, 0)) {
1040
			close(s);
1041
			goto bail;
1042
		}
1043
		imap_info("ok\n");
1044
	}
1045

1046
	/* read the greeting string */
1047
	if (buffer_gets(&imap->buf, &rsp)) {
1048
		fprintf(stderr, "IMAP error: no greeting response\n");
1049
		goto bail;
1050
	}
1051
	arg = next_arg(&rsp);
1052
	if (!arg || *arg != '*' || (arg = next_arg(&rsp)) == NULL) {
1053
		fprintf(stderr, "IMAP error: invalid greeting response\n");
1054
		goto bail;
1055
	}
1056
	preauth = 0;
1057
	if (!strcmp("PREAUTH", arg))
1058
		preauth = 1;
1059
	else if (strcmp("OK", arg) != 0) {
1060
		fprintf(stderr, "IMAP error: unknown greeting response\n");
1061
		goto bail;
1062
	}
1063
	parse_response_code(ctx, NULL, rsp);
1064
	if (!imap->caps && imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
1065
		goto bail;
1066

1067
	if (!preauth) {
1068
#ifndef NO_OPENSSL
1069
		if (!srvc->use_ssl && CAP(STARTTLS)) {
1070
			if (imap_exec(ctx, NULL, "STARTTLS") != RESP_OK)
1071
				goto bail;
1072
			if (ssl_socket_connect(&imap->buf.sock, srvc, 1))
1073
				goto bail;
1074
			/* capabilities may have changed, so get the new capabilities */
1075
			if (imap_exec(ctx, NULL, "CAPABILITY") != RESP_OK)
1076
				goto bail;
1077
		}
1078
#endif
1079
		imap_info("Logging in...\n");
1080
		server_fill_credential(srvc, &cred);
1081

1082
		if (srvc->auth_method) {
1083
			struct imap_cmd_cb cb;
1084

1085
			if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
1086
				if (!CAP(AUTH_CRAM_MD5)) {
1087
					fprintf(stderr, "You specified "
1088
						"CRAM-MD5 as authentication method, "
1089
						"but %s doesn't support it.\n", srvc->host);
1090
					goto bail;
1091
				}
1092
				/* CRAM-MD5 */
1093

1094
				memset(&cb, 0, sizeof(cb));
1095
				cb.cont = auth_cram_md5;
1096
				if (imap_exec(ctx, &cb, "AUTHENTICATE CRAM-MD5") != RESP_OK) {
1097
					fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
1098
					goto bail;
1099
				}
1100
			} else {
1101
				fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
1102
				goto bail;
1103
			}
1104
		} else {
1105
			if (CAP(NOLOGIN)) {
1106
				fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
1107
					srvc->user, srvc->host);
1108
				goto bail;
1109
			}
1110
			if (!imap->buf.sock.ssl)
1111
				imap_warn("*** IMAP Warning *** Password is being "
1112
					  "sent in the clear\n");
1113
			if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
1114
				fprintf(stderr, "IMAP error: LOGIN failed\n");
1115
				goto bail;
1116
			}
1117
		}
1118
	} /* !preauth */
1119

1120
	if (cred.username)
1121
		credential_approve(&cred);
1122
	credential_clear(&cred);
1123

1124
	/* check the target mailbox exists */
1125
	ctx->name = folder;
1126
	switch (imap_exec(ctx, NULL, "EXAMINE \"%s\"", ctx->name)) {
1127
	case RESP_OK:
1128
		/* ok */
1129
		break;
1130
	case RESP_BAD:
1131
		fprintf(stderr, "IMAP error: could not check mailbox\n");
1132
		goto out;
1133
	case RESP_NO:
1134
		if (imap_exec(ctx, NULL, "CREATE \"%s\"", ctx->name) == RESP_OK) {
1135
			imap_info("Created missing mailbox\n");
1136
		} else {
1137
			fprintf(stderr, "IMAP error: could not create missing mailbox\n");
1138
			goto out;
1139
		}
1140
		break;
1141
	}
1142

1143
	ctx->prefix = "";
1144
	return ctx;
1145

1146
bail:
1147
	if (cred.username)
1148
		credential_reject(&cred);
1149
	credential_clear(&cred);
1150

1151
 out:
1152
	imap_close_store(ctx);
1153
	return NULL;
1154
}
1155

1156
/*
1157
 * Insert CR characters as necessary in *msg to ensure that every LF
1158
 * character in *msg is preceded by a CR.
1159
 */
1160
static void lf_to_crlf(struct strbuf *msg)
1161
{
1162
	char *new_msg;
1163
	size_t i, j;
1164
	char lastc;
1165

1166
	/* First pass: tally, in j, the size of the new_msg string: */
1167
	for (i = j = 0, lastc = '\0'; i < msg->len; i++) {
1168
		if (msg->buf[i] == '\n' && lastc != '\r')
1169
			j++; /* a CR will need to be added here */
1170
		lastc = msg->buf[i];
1171
		j++;
1172
	}
1173

1174
	new_msg = xmallocz(j);
1175

1176
	/*
1177
	 * Second pass: write the new_msg string.  Note that this loop is
1178
	 * otherwise identical to the first pass.
1179
	 */
1180
	for (i = j = 0, lastc = '\0'; i < msg->len; i++) {
1181
		if (msg->buf[i] == '\n' && lastc != '\r')
1182
			new_msg[j++] = '\r';
1183
		lastc = new_msg[j++] = msg->buf[i];
1184
	}
1185
	strbuf_attach(msg, new_msg, j, j + 1);
1186
}
1187

1188
/*
1189
 * Store msg to IMAP.  Also detach and free the data from msg->data,
1190
 * leaving msg->data empty.
1191
 */
1192
static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)
1193
{
1194
	struct imap *imap = ctx->imap;
1195
	struct imap_cmd_cb cb;
1196
	const char *prefix, *box;
1197
	int ret;
1198

1199
	lf_to_crlf(msg);
1200
	memset(&cb, 0, sizeof(cb));
1201

1202
	cb.dlen = msg->len;
1203
	cb.data = strbuf_detach(msg, NULL);
1204

1205
	box = ctx->name;
1206
	prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix;
1207
	ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" ", prefix, box);
1208
	imap->caps = imap->rcaps;
1209
	if (ret != DRV_OK)
1210
		return ret;
1211

1212
	return DRV_OK;
1213
}
1214

1215
static void wrap_in_html(struct strbuf *msg)
1216
{
1217
	struct strbuf buf = STRBUF_INIT;
1218
	static const char *content_type = "Content-Type: text/html;\n";
1219
	static const char *pre_open = "<pre>\n";
1220
	static const char *pre_close = "</pre>\n";
1221
	const char *body = strstr(msg->buf, "\n\n");
1222

1223
	if (!body)
1224
		return; /* Headers but no body; no wrapping needed */
1225

1226
	body += 2;
1227

1228
	strbuf_add(&buf, msg->buf, body - msg->buf - 1);
1229
	strbuf_addstr(&buf, content_type);
1230
	strbuf_addch(&buf, '\n');
1231
	strbuf_addstr(&buf, pre_open);
1232
	strbuf_addstr_xml_quoted(&buf, body);
1233
	strbuf_addstr(&buf, pre_close);
1234

1235
	strbuf_release(msg);
1236
	*msg = buf;
1237
}
1238

1239
static int count_messages(struct strbuf *all_msgs)
1240
{
1241
	int count = 0;
1242
	char *p = all_msgs->buf;
1243

1244
	while (1) {
1245
		if (starts_with(p, "From ")) {
1246
			p = strstr(p+5, "\nFrom: ");
1247
			if (!p) break;
1248
			p = strstr(p+7, "\nDate: ");
1249
			if (!p) break;
1250
			p = strstr(p+7, "\nSubject: ");
1251
			if (!p) break;
1252
			p += 10;
1253
			count++;
1254
		}
1255
		p = strstr(p+5, "\nFrom ");
1256
		if (!p)
1257
			break;
1258
		p++;
1259
	}
1260
	return count;
1261
}
1262

1263
/*
1264
 * Copy the next message from all_msgs, starting at offset *ofs, to
1265
 * msg.  Update *ofs to the start of the following message.  Return
1266
 * true iff a message was successfully copied.
1267
 */
1268
static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs)
1269
{
1270
	char *p, *data;
1271
	size_t len;
1272

1273
	if (*ofs >= all_msgs->len)
1274
		return 0;
1275

1276
	data = &all_msgs->buf[*ofs];
1277
	len = all_msgs->len - *ofs;
1278

1279
	if (len < 5 || !starts_with(data, "From "))
1280
		return 0;
1281

1282
	p = strchr(data, '\n');
1283
	if (p) {
1284
		p++;
1285
		len -= p - data;
1286
		*ofs += p - data;
1287
		data = p;
1288
	}
1289

1290
	p = strstr(data, "\nFrom ");
1291
	if (p)
1292
		len = &p[1] - data;
1293

1294
	strbuf_add(msg, data, len);
1295
	*ofs += len;
1296
	return 1;
1297
}
1298

1299
static int git_imap_config(const char *var, const char *val,
1300
			   const struct config_context *ctx, void *cb)
1301
{
1302
	struct imap_server_conf *cfg = cb;
1303

1304
	if (!strcmp("imap.sslverify", var)) {
1305
		cfg->ssl_verify = git_config_bool(var, val);
1306
	} else if (!strcmp("imap.preformattedhtml", var)) {
1307
		cfg->use_html = git_config_bool(var, val);
1308
	} else if (!strcmp("imap.folder", var)) {
1309
		FREE_AND_NULL(cfg->folder);
1310
		return git_config_string(&cfg->folder, var, val);
1311
	} else if (!strcmp("imap.user", var)) {
1312
		FREE_AND_NULL(cfg->folder);
1313
		return git_config_string(&cfg->user, var, val);
1314
	} else if (!strcmp("imap.pass", var)) {
1315
		FREE_AND_NULL(cfg->folder);
1316
		return git_config_string(&cfg->pass, var, val);
1317
	} else if (!strcmp("imap.tunnel", var)) {
1318
		FREE_AND_NULL(cfg->folder);
1319
		return git_config_string(&cfg->tunnel, var, val);
1320
	} else if (!strcmp("imap.authmethod", var)) {
1321
		FREE_AND_NULL(cfg->folder);
1322
		return git_config_string(&cfg->auth_method, var, val);
1323
	} else if (!strcmp("imap.port", var)) {
1324
		cfg->port = git_config_int(var, val, ctx->kvi);
1325
	} else if (!strcmp("imap.host", var)) {
1326
		if (!val) {
1327
			return config_error_nonbool(var);
1328
		} else {
1329
			if (starts_with(val, "imap:"))
1330
				val += 5;
1331
			else if (starts_with(val, "imaps:")) {
1332
				val += 6;
1333
				cfg->use_ssl = 1;
1334
			}
1335
			if (starts_with(val, "//"))
1336
				val += 2;
1337
			cfg->host = xstrdup(val);
1338
		}
1339
	} else {
1340
		return git_default_config(var, val, ctx, cb);
1341
	}
1342

1343
	return 0;
1344
}
1345

1346
static int append_msgs_to_imap(struct imap_server_conf *server,
1347
			       struct strbuf* all_msgs, int total)
1348
{
1349
	struct strbuf msg = STRBUF_INIT;
1350
	struct imap_store *ctx = NULL;
1351
	int ofs = 0;
1352
	int r;
1353
	int n = 0;
1354

1355
	ctx = imap_open_store(server, server->folder);
1356
	if (!ctx) {
1357
		fprintf(stderr, "failed to open store\n");
1358
		return 1;
1359
	}
1360
	ctx->name = server->folder;
1361

1362
	fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
1363
	while (1) {
1364
		unsigned percent = n * 100 / total;
1365

1366
		fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
1367

1368
		if (!split_msg(all_msgs, &msg, &ofs))
1369
			break;
1370
		if (server->use_html)
1371
			wrap_in_html(&msg);
1372
		r = imap_store_msg(ctx, &msg);
1373
		if (r != DRV_OK)
1374
			break;
1375
		n++;
1376
	}
1377
	fprintf(stderr, "\n");
1378

1379
	imap_close_store(ctx);
1380

1381
	return 0;
1382
}
1383

1384
#ifdef USE_CURL_FOR_IMAP_SEND
1385
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
1386
{
1387
	CURL *curl;
1388
	struct strbuf path = STRBUF_INIT;
1389
	char *uri_encoded_folder;
1390

1391
	if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
1392
		die("curl_global_init failed");
1393

1394
	curl = curl_easy_init();
1395

1396
	if (!curl)
1397
		die("curl_easy_init failed");
1398

1399
	server_fill_credential(srvc, cred);
1400
	curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
1401
	curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
1402

1403
	strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
1404
	strbuf_addstr(&path, srvc->host);
1405
	if (!path.len || path.buf[path.len - 1] != '/')
1406
		strbuf_addch(&path, '/');
1407

1408
	uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
1409
	if (!uri_encoded_folder)
1410
		die("failed to encode server folder");
1411
	strbuf_addstr(&path, uri_encoded_folder);
1412
	curl_free(uri_encoded_folder);
1413

1414
	curl_easy_setopt(curl, CURLOPT_URL, path.buf);
1415
	strbuf_release(&path);
1416
	curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
1417

1418
	if (srvc->auth_method) {
1419
#ifndef GIT_CURL_HAVE_CURLOPT_LOGIN_OPTIONS
1420
		warning("No LOGIN_OPTIONS support in this cURL version");
1421
#else
1422
		struct strbuf auth = STRBUF_INIT;
1423
		strbuf_addstr(&auth, "AUTH=");
1424
		strbuf_addstr(&auth, srvc->auth_method);
1425
		curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
1426
		strbuf_release(&auth);
1427
#endif
1428
	}
1429

1430
	if (!srvc->use_ssl)
1431
		curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_TRY);
1432

1433
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
1434
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
1435

1436
	curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
1437

1438
	curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
1439

1440
	if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
1441
		http_trace_curl_no_data();
1442
	setup_curl_trace(curl);
1443

1444
	return curl;
1445
}
1446

1447
static int curl_append_msgs_to_imap(struct imap_server_conf *server,
1448
				    struct strbuf* all_msgs, int total)
1449
{
1450
	int ofs = 0;
1451
	int n = 0;
1452
	struct buffer msgbuf = { STRBUF_INIT, 0 };
1453
	CURL *curl;
1454
	CURLcode res = CURLE_OK;
1455
	struct credential cred = CREDENTIAL_INIT;
1456

1457
	curl = setup_curl(server, &cred);
1458
	curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
1459

1460
	fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
1461
	while (1) {
1462
		unsigned percent = n * 100 / total;
1463
		int prev_len;
1464

1465
		fprintf(stderr, "%4u%% (%d/%d) done\r", percent, n, total);
1466

1467
		prev_len = msgbuf.buf.len;
1468
		if (!split_msg(all_msgs, &msgbuf.buf, &ofs))
1469
			break;
1470
		if (server->use_html)
1471
			wrap_in_html(&msgbuf.buf);
1472
		lf_to_crlf(&msgbuf.buf);
1473

1474
		curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE,
1475
				 (curl_off_t)(msgbuf.buf.len-prev_len));
1476

1477
		res = curl_easy_perform(curl);
1478

1479
		if(res != CURLE_OK) {
1480
			fprintf(stderr, "curl_easy_perform() failed: %s\n",
1481
					curl_easy_strerror(res));
1482
			break;
1483
		}
1484

1485
		n++;
1486
	}
1487
	fprintf(stderr, "\n");
1488

1489
	curl_easy_cleanup(curl);
1490
	curl_global_cleanup();
1491

1492
	if (cred.username) {
1493
		if (res == CURLE_OK)
1494
			credential_approve(&cred);
1495
		else if (res == CURLE_LOGIN_DENIED)
1496
			credential_reject(&cred);
1497
	}
1498

1499
	credential_clear(&cred);
1500

1501
	return res != CURLE_OK;
1502
}
1503
#endif
1504

1505
int cmd_main(int argc, const char **argv)
1506
{
1507
	struct imap_server_conf server = {
1508
		.ssl_verify = 1,
1509
	};
1510
	struct strbuf all_msgs = STRBUF_INIT;
1511
	int total;
1512
	int nongit_ok;
1513
	int ret;
1514

1515
	setup_git_directory_gently(&nongit_ok);
1516
	git_config(git_imap_config, &server);
1517

1518
	argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
1519

1520
	if (argc)
1521
		usage_with_options(imap_send_usage, imap_send_options);
1522

1523
#ifndef USE_CURL_FOR_IMAP_SEND
1524
	if (use_curl) {
1525
		warning("--curl not supported in this build");
1526
		use_curl = 0;
1527
	}
1528
#elif defined(NO_OPENSSL)
1529
	if (!use_curl) {
1530
		warning("--no-curl not supported in this build");
1531
		use_curl = 1;
1532
	}
1533
#endif
1534

1535
	if (!server.port)
1536
		server.port = server.use_ssl ? 993 : 143;
1537

1538
	if (!server.folder) {
1539
		fprintf(stderr, "no imap store specified\n");
1540
		ret = 1;
1541
		goto out;
1542
	}
1543
	if (!server.host) {
1544
		if (!server.tunnel) {
1545
			fprintf(stderr, "no imap host specified\n");
1546
			ret = 1;
1547
			goto out;
1548
		}
1549
		server.host = xstrdup("tunnel");
1550
	}
1551

1552
	/* read the messages */
1553
	if (strbuf_read(&all_msgs, 0, 0) < 0) {
1554
		error_errno(_("could not read from stdin"));
1555
		ret = 1;
1556
		goto out;
1557
	}
1558

1559
	if (all_msgs.len == 0) {
1560
		fprintf(stderr, "nothing to send\n");
1561
		ret = 1;
1562
		goto out;
1563
	}
1564

1565
	total = count_messages(&all_msgs);
1566
	if (!total) {
1567
		fprintf(stderr, "no messages to send\n");
1568
		ret = 1;
1569
		goto out;
1570
	}
1571

1572
	/* write it to the imap server */
1573

1574
	if (server.tunnel)
1575
		ret = append_msgs_to_imap(&server, &all_msgs, total);
1576
#ifdef USE_CURL_FOR_IMAP_SEND
1577
	else if (use_curl)
1578
		ret = curl_append_msgs_to_imap(&server, &all_msgs, total);
1579
#endif
1580
	else
1581
		ret = append_msgs_to_imap(&server, &all_msgs, total);
1582

1583
out:
1584
	free(server.tunnel);
1585
	free(server.host);
1586
	free(server.folder);
1587
	free(server.user);
1588
	free(server.pass);
1589
	free(server.auth_method);
1590
	strbuf_release(&all_msgs);
1591
	return ret;
1592
}
1593

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.