diff options
Diffstat (limited to 'imap-send.c')
-rw-r--r-- | imap-send.c | 227 |
1 files changed, 94 insertions, 133 deletions
diff --git a/imap-send.c b/imap-send.c index d6b65e204c..70bcc7a4e5 100644 --- a/imap-send.c +++ b/imap-send.c @@ -23,25 +23,11 @@ */ #include "cache.h" +#include "credential.h" #include "exec_cmd.h" #include "run-command.h" -#include "prompt.h" #ifdef NO_OPENSSL typedef void *SSL; -#else -#ifdef APPLE_COMMON_CRYPTO -#include <CommonCrypto/CommonHMAC.h> -#define HMAC_CTX CCHmacContext -#define HMAC_Init(hmac, key, len, algo) CCHmacInit(hmac, algo, key, len) -#define HMAC_Update CCHmacUpdate -#define HMAC_Final(hmac, hash, ptr) CCHmacFinal(hmac, hash) -#define HMAC_CTX_cleanup(ignore) -#define EVP_md5() kCCHmacAlgMD5 -#else -#include <openssl/evp.h> -#include <openssl/hmac.h> -#endif -#include <openssl/x509v3.h> #endif static const char imap_send_usage[] = "git imap-send < <mbox>"; @@ -83,6 +69,7 @@ struct imap_server_conf { char *tunnel; char *host; int port; + char *folder; char *user; char *pass; int use_ssl; @@ -96,6 +83,7 @@ static struct imap_server_conf server = { NULL, /* tunnel */ NULL, /* host */ 0, /* port */ + NULL, /* folder */ NULL, /* user */ NULL, /* pass */ 0, /* use_ssl */ @@ -142,7 +130,6 @@ struct imap_cmd_cb { char *data; int dlen; int uid; - unsigned create:1, trycreate:1; }; struct imap_cmd { @@ -507,9 +494,9 @@ static int nfsnprintf(char *buf, int blen, const char *fmt, ...) return ret; } -static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx, - struct imap_cmd_cb *cb, - const char *fmt, va_list ap) +static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx, + struct imap_cmd_cb *cb, + const char *fmt, va_list ap) { struct imap *imap = ctx->imap; struct imap_cmd *cmd; @@ -538,7 +525,7 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx, if (Verbose) { if (imap->num_in_progress) printf("(%d in progress) ", imap->num_in_progress); - if (memcmp(cmd->cmd, "LOGIN", 5)) + if (!starts_with(cmd->cmd, "LOGIN")) printf(">>> %s", buf); else printf(">>> %d LOGIN <user> <pass>\n", cmd->tag); @@ -573,20 +560,6 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx, } __attribute__((format (printf, 3, 4))) -static struct imap_cmd *issue_imap_cmd(struct imap_store *ctx, - struct imap_cmd_cb *cb, - const char *fmt, ...) -{ - struct imap_cmd *ret; - va_list ap; - - va_start(ap, fmt); - ret = v_issue_imap_cmd(ctx, cb, fmt, ap); - va_end(ap); - return ret; -} - -__attribute__((format (printf, 3, 4))) static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb, const char *fmt, ...) { @@ -594,7 +567,7 @@ static int imap_exec(struct imap_store *ctx, struct imap_cmd_cb *cb, struct imap_cmd *cmdp; va_start(ap, fmt); - cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap); + cmdp = issue_imap_cmd(ctx, cb, fmt, ap); va_end(ap); if (!cmdp) return RESP_BAD; @@ -610,7 +583,7 @@ static int imap_exec_m(struct imap_store *ctx, struct imap_cmd_cb *cb, struct imap_cmd *cmdp; va_start(ap, fmt); - cmdp = v_issue_imap_cmd(ctx, cb, fmt, ap); + cmdp = issue_imap_cmd(ctx, cb, fmt, ap); va_end(ap); if (!cmdp) return DRV_STORE_BAD; @@ -728,8 +701,8 @@ static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb, static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd) { struct imap *imap = ctx->imap; - struct imap_cmd *cmdp, **pcmdp, *ncmdp; - char *cmd, *arg, *arg1, *p; + struct imap_cmd *cmdp, **pcmdp; + char *cmd, *arg, *arg1; int n, resp, resp2, tag; for (;;) { @@ -815,39 +788,17 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd) if (!strcmp("OK", arg)) resp = DRV_OK; else { - 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\"", (int)(strchr(p + 1, '"') - p + 1), p)) { - 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; - if (!(ncmdp = issue_imap_cmd(ctx, &cmdp->cb, "%s", cmdp->cmd))) { - resp = RESP_BAD; - goto normal; - } - free(cmdp->cmd); - free(cmdp); - if (!tcmd) - return 0; /* ignored */ - if (cmdp == tcmd) - tcmd = ncmdp; - continue; - } + if (!strcmp("NO", arg)) resp = RESP_NO; - } else /*if (!strcmp("BAD", arg))*/ + else /*if (!strcmp("BAD", arg))*/ resp = RESP_BAD; fprintf(stderr, "IMAP command '%s' returned response (%s) - %s\n", - memcmp(cmdp->cmd, "LOGIN", 5) ? + !starts_with(cmdp->cmd, "LOGIN") ? cmdp->cmd : "LOGIN <user> <pass>", arg, cmd ? cmd : ""); } if ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp) resp = resp2; - normal: if (cmdp->cb.done) cmdp->cb.done(ctx, cmdp, resp); free(cmdp->cb.data); @@ -958,33 +909,33 @@ static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const cha return 0; } -static struct imap_store *imap_open_store(struct imap_server_conf *srvc) +static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *folder) { + struct credential cred = CREDENTIAL_INIT; struct imap_store *ctx; struct imap *imap; char *arg, *rsp; int s = -1, preauth; - ctx = xcalloc(sizeof(*ctx), 1); + ctx = xcalloc(1, sizeof(*ctx)); - ctx->imap = imap = xcalloc(sizeof(*imap), 1); + ctx->imap = imap = xcalloc(1, sizeof(*imap)); imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1; imap->in_progress_append = &imap->in_progress; /* open connection to IMAP server */ if (srvc->tunnel) { - const char *argv[] = { srvc->tunnel, NULL }; - struct child_process tunnel = {NULL}; + struct child_process tunnel = CHILD_PROCESS_INIT; imap_info("Starting tunnel '%s'... ", srvc->tunnel); - tunnel.argv = argv; + argv_array_push(&tunnel.args, srvc->tunnel); tunnel.use_shell = 1; tunnel.in = -1; tunnel.out = -1; if (start_command(&tunnel)) - die("cannot start proxy %s", argv[0]); + die("cannot start proxy %s", srvc->tunnel); imap->buf.sock.fd[0] = tunnel.out; imap->buf.sock.fd[1] = tunnel.in; @@ -1110,25 +1061,23 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc) } #endif imap_info("Logging in...\n"); - if (!srvc->user) { - fprintf(stderr, "Skipping server %s, no user\n", srvc->host); - goto bail; - } - if (!srvc->pass) { - struct strbuf prompt = STRBUF_INIT; - strbuf_addf(&prompt, "Password (%s@%s): ", srvc->user, srvc->host); - arg = git_getpass(prompt.buf); - strbuf_release(&prompt); - if (!*arg) { - fprintf(stderr, "Skipping account %s@%s, no password\n", srvc->user, srvc->host); - goto bail; - } - /* - * getpass() returns a pointer to a static buffer. make a copy - * for long term storage. - */ - srvc->pass = xstrdup(arg); + if (!srvc->user || !srvc->pass) { + cred.protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap"); + cred.host = xstrdup(srvc->host); + + if (srvc->user) + cred.username = xstrdup(srvc->user); + if (srvc->pass) + cred.password = xstrdup(srvc->pass); + + credential_fill(&cred); + + if (!srvc->user) + srvc->user = xstrdup(cred.username); + if (!srvc->pass) + srvc->pass = xstrdup(cred.password); } + if (CAP(NOLOGIN)) { fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host); goto bail; @@ -1167,10 +1116,38 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc) } } /* !preauth */ + if (cred.username) + credential_approve(&cred); + credential_clear(&cred); + + /* check the target mailbox exists */ + ctx->name = folder; + switch (imap_exec(ctx, NULL, "EXAMINE \"%s\"", ctx->name)) { + case RESP_OK: + /* ok */ + break; + case RESP_BAD: + fprintf(stderr, "IMAP error: could not check mailbox\n"); + goto out; + case RESP_NO: + if (imap_exec(ctx, NULL, "CREATE \"%s\"", ctx->name) == RESP_OK) { + imap_info("Created missing mailbox\n"); + } else { + fprintf(stderr, "IMAP error: could not create missing mailbox\n"); + goto out; + } + break; + } + ctx->prefix = ""; return ctx; bail: + if (cred.username) + credential_reject(&cred); + credential_clear(&cred); + + out: imap_close_store(ctx); return NULL; } @@ -1226,7 +1203,6 @@ static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg) box = ctx->name; prefix = !strcmp(box, "INBOX") ? "" : ctx->prefix; - cb.create = 0; ret = imap_exec_m(ctx, &cb, "APPEND \"%s%s\" ", prefix, box); imap->caps = imap->rcaps; if (ret != DRV_OK) @@ -1277,7 +1253,7 @@ static int count_messages(struct strbuf *all_msgs) char *p = all_msgs->buf; while (1) { - if (!prefixcmp(p, "From ")) { + if (starts_with(p, "From ")) { p = strstr(p+5, "\nFrom: "); if (!p) break; p = strstr(p+7, "\nDate: "); @@ -1311,7 +1287,7 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs) data = &all_msgs->buf[*ofs]; len = all_msgs->len - *ofs; - if (len < 5 || prefixcmp(data, "From ")) + if (len < 5 || !starts_with(data, "From ")) return 0; p = strchr(data, '\n'); @@ -1331,49 +1307,35 @@ static int split_msg(struct strbuf *all_msgs, struct strbuf *msg, int *ofs) return 1; } -static char *imap_folder; - -static int git_imap_config(const char *key, const char *val, void *cb) +static void git_imap_config(void) { - char imap_key[] = "imap."; + const char *val = NULL; - if (strncmp(key, imap_key, sizeof imap_key - 1)) - return 0; + git_config_get_bool("imap.sslverify", &server.ssl_verify); + git_config_get_bool("imap.preformattedhtml", &server.use_html); + git_config_get_string("imap.folder", &server.folder); - key += sizeof imap_key - 1; - - /* check booleans first, and barf on others */ - if (!strcmp("sslverify", key)) - server.ssl_verify = git_config_bool(key, val); - else if (!strcmp("preformattedhtml", key)) - server.use_html = git_config_bool(key, val); - else if (!val) - return config_error_nonbool(key); - - if (!strcmp("folder", key)) { - imap_folder = xstrdup(val); - } else if (!strcmp("host", key)) { - if (!prefixcmp(val, "imap:")) - val += 5; - else if (!prefixcmp(val, "imaps:")) { - val += 6; - server.use_ssl = 1; + if (!git_config_get_value("imap.host", &val)) { + if (!val) { + git_die_config("imap.host", "Missing value for 'imap.host'"); + } else { + if (starts_with(val, "imap:")) + val += 5; + else if (starts_with(val, "imaps:")) { + val += 6; + server.use_ssl = 1; + } + if (starts_with(val, "//")) + val += 2; + server.host = xstrdup(val); } - if (!prefixcmp(val, "//")) - val += 2; - server.host = xstrdup(val); - } else if (!strcmp("user", key)) - 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("authmethod", key)) - server.auth_method = xstrdup(val); + } - return 0; + git_config_get_string("imap.user", &server.user); + git_config_get_string("imap.pass", &server.pass); + git_config_get_int("imap.port", &server.port); + git_config_get_string("imap.tunnel", &server.tunnel); + git_config_get_string("imap.authmethod", &server.auth_method); } int main(int argc, char **argv) @@ -1394,12 +1356,12 @@ int main(int argc, char **argv) usage(imap_send_usage); setup_git_directory_gently(&nongit_ok); - git_config(git_imap_config, NULL); + git_imap_config(); if (!server.port) server.port = server.use_ssl ? 993 : 143; - if (!imap_folder) { + if (!server.folder) { fprintf(stderr, "no imap store specified\n"); return 1; } @@ -1429,14 +1391,13 @@ int main(int argc, char **argv) } /* write it to the imap server */ - ctx = imap_open_store(&server); + ctx = imap_open_store(&server, server.folder); if (!ctx) { fprintf(stderr, "failed to open store\n"); return 1; } fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : ""); - ctx->name = imap_folder; while (1) { unsigned percent = n * 100 / total; |