Merge branch 'tf/imap-send-create'
authorJunio C Hamano <gitster@pobox.com>
Tue, 9 Sep 2014 19:54:08 +0000 (12:54 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 9 Sep 2014 19:54:09 +0000 (12:54 -0700)
* tf/imap-send-create:
imap-send: create target mailbox if it is missing
imap-send: clarify CRAM-MD5 vs LOGIN documentation

1  2 
Documentation/git-imap-send.txt
imap-send.c
index eabcaf0388b354baa9e321050122d3c75caf6809,770cbe80a385b50c018a7f3c95a6cb61bc91e2e0..7d991d919ccf3378fdc924418aede45f8cb38632
@@@ -38,17 -38,18 +38,17 @@@ Variable
  imap.folder::
        The folder to drop the mails into, which is typically the Drafts
        folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
 -      "[Gmail]/Drafts". Required to use imap-send.
 +      "[Gmail]/Drafts". Required.
  
  imap.tunnel::
        Command used to setup a tunnel to the IMAP server through which
        commands will be piped instead of using a direct network connection
 -      to the server. Required when imap.host is not set to use imap-send.
 +      to the server. Required when imap.host is not set.
  
  imap.host::
        A URL identifying the server. Use a `imap://` prefix for non-secure
        connections and a `imaps://` prefix for secure connections.
 -      Ignored when imap.tunnel is set, but required to use imap-send
 -      otherwise.
 +      Ignored when imap.tunnel is set, but required otherwise.
  
  imap.user::
        The username to use when logging in to the server.
@@@ -75,7 -76,8 +75,8 @@@ imap.preformattedHTML:
  
  imap.authMethod::
        Specify authenticate method for authentication with IMAP server.
-       Current supported method is 'CRAM-MD5' only.
+       Current supported method is 'CRAM-MD5' only. If this is not set
+       then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
  
  Examples
  ~~~~~~~~
diff --combined imap-send.c
index 524fbabc96f450f1faed196c1743d2f8d697b587,48733ccc45139400cdd3276dd2e16d547f8ea7fa..87f9bb18f6983a99499f170cef1f319f6c63d6f1
@@@ -128,7 -128,6 +128,6 @@@ struct imap_cmd_cb 
        char *data;
        int dlen;
        int uid;
-       unsigned create:1, trycreate:1;
  };
  
  struct imap_cmd {
@@@ -493,9 -492,9 +492,9 @@@ static int nfsnprintf(char *buf, int bl
        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;
        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, ...)
        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;
@@@ -596,7 -581,7 +581,7 @@@ static int imap_exec_m(struct imap_stor
        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;
@@@ -714,8 -699,8 +699,8 @@@ static int parse_response_code(struct i
  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 (;;) {
                        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) ?
                        }
                        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);
@@@ -944,7 -907,7 +907,7 @@@ static int auth_cram_md5(struct imap_st
        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;
        char *arg, *rsp;
        int s = -1, preauth;
  
 -      ctx = xcalloc(sizeof(*ctx), 1);
 +      ctx = xcalloc(1, sizeof(*ctx));
  
        ctx->imap = imap = xcalloc(sizeof(*imap), 1);
        imap->buf.sock.fd[0] = imap->buf.sock.fd[1] = -1;
                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;
  
@@@ -1164,6 -1146,7 +1146,7 @@@ bail
                credential_reject(&cred);
        credential_clear(&cred);
  
+  out:
        imap_close_store(ctx);
        return NULL;
  }
@@@ -1219,7 -1202,6 +1202,6 @@@ static int imap_store_msg(struct imap_s
  
        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)
@@@ -1328,9 -1310,13 +1310,9 @@@ static char *imap_folder
  
  static int git_imap_config(const char *key, const char *val, void *cb)
  {
 -      char imap_key[] = "imap.";
 -
 -      if (strncmp(key, imap_key, sizeof imap_key - 1))
 +      if (!skip_prefix(key, "imap.", &key))
                return 0;
  
 -      key += sizeof imap_key - 1;
 -
        /* check booleans first, and barf on others */
        if (!strcmp("sslverify", key))
                server.ssl_verify = git_config_bool(key, val);
@@@ -1418,14 -1404,13 +1400,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;