THIS IS A TEST INSTANCE ONLY! REPOSITORIES CAN BE DELETED AT ANY TIME!

Git Source Code Mirror - This is a publish-only repository and all pull requests are ignored. Please follow Documentation/SubmittingPatches procedure for any of your improvements.
git
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1605 lines
37KB

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