{"thread":{"id":"37227","subject":"[PATCH] imap-send: create target mailbox if it is missing","startedAt":"2014-07-28T10:48:46Z","lastAt":"2014-07-28T10:48:46Z","messageCount":1,"participants":["Tony Finch"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"246780","messageId":"alpine.LSU.2.00.1407281148410.13901@hermes-1.csi.cam.ac.uk","threadId":"37227","inReplyTo":null,"subject":"[PATCH] imap-send: create target mailbox if it is missing","fromName":"Tony Finch","fromEmail":"dot@dotat.at","sentAt":"2014-07-28T10:48:46Z","receivedAt":"2014-07-28T10:48:46Z","isPatch":true,"sender":{"key":"dot@dotat.at","avatar":"https://avatars.githubusercontent.com/u/68429?v=4"},"body":"Some MUAs delete their \"drafts\" folder when it is empty, so\ngit imap-send should be able to create it if necessary.\n\nThis change checks that the folder exists immediately after\nlogin and tries to create it if it is missing.\n\nThere was some vestigial code to handle a [TRYCREATE] response\nfrom the server when an APPEND target is missing. However this\ncode never ran (the create and trycreate flags were never set)\nand when I tried to make it run I found that the code had already\nthrown away the contents of the message it was trying to append.\n---\n imap-send.c | 56 +++++++++++++++++++++++++-------------------------------\n 1 file changed, 25 insertions(+), 31 deletions(-)\n\ndiff --git a/imap-send.c b/imap-send.c\nindex 524fbab..5e4a24e 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -128,7 +128,6 @@ struct imap_cmd_cb {\n \tchar *data;\n \tint dlen;\n \tint uid;\n-\tunsigned create:1, trycreate:1;\n };\n\n struct imap_cmd {\n@@ -714,8 +713,8 @@ static int parse_response_code(struct imap_store *ctx, struct imap_cmd_cb *cb,\n static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)\n {\n \tstruct imap *imap = ctx->imap;\n-\tstruct imap_cmd *cmdp, **pcmdp, *ncmdp;\n-\tchar *cmd, *arg, *arg1, *p;\n+\tstruct imap_cmd *cmdp, **pcmdp;\n+\tchar *cmd, *arg, *arg1;\n \tint n, resp, resp2, tag;\n\n \tfor (;;) {\n@@ -801,30 +800,9 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)\n \t\t\tif (!strcmp(\"OK\", arg))\n \t\t\t\tresp = DRV_OK;\n \t\t\telse {\n-\t\t\t\tif (!strcmp(\"NO\", arg)) {\n-\t\t\t\t\tif (cmdp->cb.create && cmd && (cmdp->cb.trycreate || !memcmp(cmd, \"[TRYCREATE]\", 11))) { /* SELECT, APPEND or UID COPY */\n-\t\t\t\t\t\tp = strchr(cmdp->cmd, '\"');\n-\t\t\t\t\t\tif (!issue_imap_cmd(ctx, NULL, \"CREATE \\\"%.*s\\\"\", (int)(strchr(p + 1, '\"') - p + 1), p)) {\n-\t\t\t\t\t\t\tresp = RESP_BAD;\n-\t\t\t\t\t\t\tgoto normal;\n-\t\t\t\t\t\t}\n-\t\t\t\t\t\t/* not waiting here violates the spec, but a server that does not\n-\t\t\t\t\t\t   grok this nonetheless violates it too. */\n-\t\t\t\t\t\tcmdp->cb.create = 0;\n-\t\t\t\t\t\tif (!(ncmdp = issue_imap_cmd(ctx, &cmdp->cb, \"%s\", cmdp->cmd))) {\n-\t\t\t\t\t\t\tresp = RESP_BAD;\n-\t\t\t\t\t\t\tgoto normal;\n-\t\t\t\t\t\t}\n-\t\t\t\t\t\tfree(cmdp->cmd);\n-\t\t\t\t\t\tfree(cmdp);\n-\t\t\t\t\t\tif (!tcmd)\n-\t\t\t\t\t\t\treturn 0;\t/* ignored */\n-\t\t\t\t\t\tif (cmdp == tcmd)\n-\t\t\t\t\t\t\ttcmd = ncmdp;\n-\t\t\t\t\t\tcontinue;\n-\t\t\t\t\t}\n+\t\t\t\tif (!strcmp(\"NO\", arg))\n \t\t\t\t\tresp = RESP_NO;\n-\t\t\t\t} else /*if (!strcmp(\"BAD\", arg))*/\n+\t\t\t\telse /*if (!strcmp(\"BAD\", arg))*/\n \t\t\t\t\tresp = RESP_BAD;\n \t\t\t\tfprintf(stderr, \"IMAP command '%s' returned response (%s) - %s\\n\",\n \t\t\t\t\t memcmp(cmdp->cmd, \"LOGIN\", 5) ?\n@@ -833,7 +811,6 @@ static int get_cmd_result(struct imap_store *ctx, struct imap_cmd *tcmd)\n \t\t\t}\n \t\t\tif ((resp2 = parse_response_code(ctx, &cmdp->cb, cmd)) > resp)\n \t\t\t\tresp = resp2;\n-\t\tnormal:\n \t\t\tif (cmdp->cb.done)\n \t\t\t\tcmdp->cb.done(ctx, cmdp, resp);\n \t\t\tfree(cmdp->cb.data);\n@@ -944,7 +921,7 @@ static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const cha\n \treturn 0;\n }\n\n-static struct imap_store *imap_open_store(struct imap_server_conf *srvc)\n+static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *folder)\n {\n \tstruct credential cred = CREDENTIAL_INIT;\n \tstruct imap_store *ctx;\n@@ -1156,6 +1133,25 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc)\n \t\tcredential_approve(&cred);\n \tcredential_clear(&cred);\n\n+\t/* check the target mailbox exists */\n+\tctx->name = folder;\n+\tswitch (imap_exec(ctx, NULL, \"EXAMINE \\\"%s\\\"\", ctx->name)) {\n+\tcase RESP_OK:\n+\t\t/* ok */\n+\t\tbreak;\n+\tcase RESP_BAD:\n+\t\tfprintf(stderr, \"IMAP error: could not check mailbox\\n\");\n+\t\tgoto bail;\n+\tcase RESP_NO:\n+\t\tif (imap_exec(ctx, NULL, \"CREATE \\\"%s\\\"\", ctx->name) == RESP_OK) {\n+\t\t\timap_info(\"Created missing mailbox\\n\");\n+\t\t} else {\n+\t\t\tfprintf(stderr, \"IMAP error: could not create missing mailbox\\n\");\n+\t\t\tgoto bail;\n+\t\t}\n+\t\tbreak;\n+\t}\n+\n \tctx->prefix = \"\";\n \treturn ctx;\n\n@@ -1219,7 +1215,6 @@ static int imap_store_msg(struct imap_store *ctx, struct strbuf *msg)\n\n \tbox = ctx->name;\n \tprefix = !strcmp(box, \"INBOX\") ? \"\" : ctx->prefix;\n-\tcb.create = 0;\n \tret = imap_exec_m(ctx, &cb, \"APPEND \\\"%s%s\\\" \", prefix, box);\n \timap->caps = imap->rcaps;\n \tif (ret != DRV_OK)\n@@ -1418,14 +1413,13 @@ int main(int argc, char **argv)\n \t}\n\n \t/* write it to the imap server */\n-\tctx = imap_open_store(&server);\n+\tctx = imap_open_store(&server, imap_folder);\n \tif (!ctx) {\n \t\tfprintf(stderr, \"failed to open store\\n\");\n \t\treturn 1;\n \t}\n\n \tfprintf(stderr, \"sending %d message%s\\n\", total, (total != 1) ? \"s\" : \"\");\n-\tctx->name = imap_folder;\n \twhile (1) {\n \t\tunsigned percent = n * 100 / total;\n\n-- \n2.0.3.dirty\n"}]}