Skip to content

Commit

Permalink
imap-send: create target mailbox if it is missing
Browse files Browse the repository at this point in the history
Some MUAs delete their "drafts" folder when it is empty, so
git imap-send should be able to create it if necessary.

This change checks that the folder exists immediately after
login and tries to create it if it is missing.

There was some vestigial code to handle a [TRYCREATE] response
from the server when an APPEND target is missing. However this
code never ran (the create and trycreate flags were never set)
and when I tried to make it run I found that the code had already
thrown away the contents of the message it was trying to append.

Signed-off-by: Tony Finch <dot@dotat.at>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Tony Finch authored and Junio C Hamano committed Aug 25, 2014
1 parent f07243f commit e0d8e30
Showing 1 changed file with 31 additions and 50 deletions.
81 changes: 31 additions & 50 deletions imap-send.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ struct imap_cmd_cb {
char *data;
int dlen;
int uid;
unsigned create:1, trycreate:1;
};

struct imap_cmd {
Expand Down Expand Up @@ -493,9 +492,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;
Expand Down Expand Up @@ -558,20 +557,6 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
return cmd;
}

__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, ...)
Expand All @@ -580,7 +565,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;
Expand All @@ -596,7 +581,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;
Expand Down Expand Up @@ -714,8 +699,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 (;;) {
Expand Down Expand Up @@ -801,30 +786,9 @@ 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) ?
Expand All @@ -833,7 +797,6 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)
}
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);
Expand Down Expand Up @@ -944,7 +907,7 @@ 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;
Expand Down Expand Up @@ -1156,6 +1119,25 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc)
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;

Expand All @@ -1164,6 +1146,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc)
credential_reject(&cred);
credential_clear(&cred);

out:
imap_close_store(ctx);
return NULL;
}
Expand Down Expand Up @@ -1219,7 +1202,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)
Expand Down Expand Up @@ -1422,14 +1404,13 @@ int main(int argc, char **argv)
}

/* write it to the imap server */
ctx = imap_open_store(&server);
ctx = imap_open_store(&server, imap_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;

Expand Down

0 comments on commit e0d8e30

Please sign in to comment.