Code

Merge branch 'rr/imap-send-unconfuse-from-line'
authorJunio C Hamano <gitster@pobox.com>
Sat, 3 Apr 2010 19:28:42 +0000 (12:28 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sat, 3 Apr 2010 19:28:42 +0000 (12:28 -0700)
* rr/imap-send-unconfuse-from-line:
  imap-send: Remove limitation on message body

1  2 
Documentation/git-imap-send.txt
imap-send.c

index 6cafbe2ec191b2e2b35e7855c71837fa0f231785,ad446b0e8b521d6fb8f779d943889b091b29e701..57aba42e6654e3d32617c3c7b17d18e8dd85852b
@@@ -16,7 -16,9 +16,9 @@@ DESCRIPTIO
  This command uploads a mailbox generated with 'git format-patch'
  into an IMAP drafts folder.  This allows patches to be sent as
  other email is when using mail clients that cannot read mailbox
- files directly.
+ files directly. The command also works with any general mailbox
+ in which emails have the fields "From", "Date", and "Subject" in
+ that order.
  
  Typical usage is something like:
  
@@@ -71,10 -73,6 +73,10 @@@ imap.preformattedHTML:
        option causes Thunderbird to send the patch as a plain/text,
        format=fixed email.  Default is `false`.
  
 +imap.authMethod::
 +      Specify authenticate method for authentication with IMAP server.
 +      Current supported method is 'CRAM-MD5' only.
 +
  Examples
  ~~~~~~~~
  
@@@ -122,12 -120,6 +124,6 @@@ Thunderbird in particular is known to b
  users may wish to visit this web page for more information:
    http://kb.mozillazine.org/Plain_text_e-mail_-_Thunderbird#Completely_plain_email
  
- BUGS
- ----
- Doesn't handle lines starting with "From " in the message body.
  Author
  ------
  Derived from isync 1.0.1 by Mike McCormack.
diff --combined imap-send.c
index 7107923a39247b5c18ad443dc2309971e44abdb4,379dec491f63c24406644c40f94390a021519ddb..9d0097ca02960460ff3a104f1739982fee453987
@@@ -27,9 -27,6 +27,9 @@@
  #include "run-command.h"
  #ifdef NO_OPENSSL
  typedef void *SSL;
 +#else
 +#include <openssl/evp.h>
 +#include <openssl/hmac.h>
  #endif
  
  struct store_conf {
@@@ -142,20 -139,6 +142,20 @@@ struct imap_server_conf 
        int use_ssl;
        int ssl_verify;
        int use_html;
 +      char *auth_method;
 +};
 +
 +static struct imap_server_conf server = {
 +      NULL,   /* name */
 +      NULL,   /* tunnel */
 +      NULL,   /* host */
 +      0,      /* port */
 +      NULL,   /* user */
 +      NULL,   /* pass */
 +      0,      /* use_ssl */
 +      1,      /* ssl_verify */
 +      0,      /* use_html */
 +      NULL,   /* auth_method */
  };
  
  struct imap_store_conf {
@@@ -230,7 -213,6 +230,7 @@@ enum CAPABILITY 
        LITERALPLUS,
        NAMESPACE,
        STARTTLS,
 +      AUTH_CRAM_MD5,
  };
  
  static const char *cap_list[] = {
        "LITERAL+",
        "NAMESPACE",
        "STARTTLS",
 +      "AUTH=CRAM-MD5",
  };
  
  #define RESP_OK    0
@@@ -967,87 -948,6 +967,87 @@@ static void imap_close_store(struct sto
        free(ctx);
  }
  
 +#ifndef NO_OPENSSL
 +
 +/*
 + * hexchar() and cram() functions are based on the code from the isync
 + * project (http://isync.sf.net/).
 + */
 +static char hexchar(unsigned int b)
 +{
 +      return b < 10 ? '0' + b : 'a' + (b - 10);
 +}
 +
 +#define ENCODED_SIZE(n) (4*((n+2)/3))
 +static char *cram(const char *challenge_64, const char *user, const char *pass)
 +{
 +      int i, resp_len, encoded_len, decoded_len;
 +      HMAC_CTX hmac;
 +      unsigned char hash[16];
 +      char hex[33];
 +      char *response, *response_64, *challenge;
 +
 +      /*
 +       * length of challenge_64 (i.e. base-64 encoded string) is a good
 +       * enough upper bound for challenge (decoded result).
 +       */
 +      encoded_len = strlen(challenge_64);
 +      challenge = xmalloc(encoded_len);
 +      decoded_len = EVP_DecodeBlock((unsigned char *)challenge,
 +                                    (unsigned char *)challenge_64, encoded_len);
 +      if (decoded_len < 0)
 +              die("invalid challenge %s", challenge_64);
 +      HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
 +      HMAC_Update(&hmac, (unsigned char *)challenge, decoded_len);
 +      HMAC_Final(&hmac, hash, NULL);
 +      HMAC_CTX_cleanup(&hmac);
 +
 +      hex[32] = 0;
 +      for (i = 0; i < 16; i++) {
 +              hex[2 * i] = hexchar((hash[i] >> 4) & 0xf);
 +              hex[2 * i + 1] = hexchar(hash[i] & 0xf);
 +      }
 +
 +      /* response: "<user> <digest in hex>" */
 +      resp_len = strlen(user) + 1 + strlen(hex) + 1;
 +      response = xmalloc(resp_len);
 +      sprintf(response, "%s %s", user, hex);
 +
 +      response_64 = xmalloc(ENCODED_SIZE(resp_len) + 1);
 +      encoded_len = EVP_EncodeBlock((unsigned char *)response_64,
 +                                    (unsigned char *)response, resp_len);
 +      if (encoded_len < 0)
 +              die("EVP_EncodeBlock error");
 +      response_64[encoded_len] = '\0';
 +      return (char *)response_64;
 +}
 +
 +#else
 +
 +static char *cram(const char *challenge_64, const char *user, const char *pass)
 +{
 +      die("If you want to use CRAM-MD5 authenticate method, "
 +          "you have to build git-imap-send with OpenSSL library.");
 +}
 +
 +#endif
 +
 +static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt)
 +{
 +      int ret;
 +      char *response;
 +
 +      response = cram(prompt, server.user, server.pass);
 +
 +      ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
 +      if (ret != strlen(response))
 +              return error("IMAP error: sending response failed\n");
 +
 +      free(response);
 +
 +      return 0;
 +}
 +
  static struct store *imap_open_store(struct imap_server_conf *srvc)
  {
        struct imap_store *ctx;
                if (!srvc->pass) {
                        char prompt[80];
                        sprintf(prompt, "Password (%s@%s): ", srvc->user, srvc->host);
 -                      arg = getpass(prompt);
 +                      arg = git_getpass(prompt);
                        if (!arg) {
                                perror("getpass");
                                exit(1);
                        fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host);
                        goto bail;
                }
 -              if (!imap->buf.sock.ssl)
 -                      imap_warn("*** IMAP Warning *** Password is being "
 -                                "sent in the clear\n");
 -              if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
 -                      fprintf(stderr, "IMAP error: LOGIN failed\n");
 -                      goto bail;
 +
 +              if (srvc->auth_method) {
 +                      struct imap_cmd_cb cb;
 +
 +                      if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
 +                              if (!CAP(AUTH_CRAM_MD5)) {
 +                                      fprintf(stderr, "You specified"
 +                                              "CRAM-MD5 as authentication method, "
 +                                              "but %s doesn't support it.\n", srvc->host);
 +                                      goto bail;
 +                              }
 +                              /* CRAM-MD5 */
 +
 +                              memset(&cb, 0, sizeof(cb));
 +                              cb.cont = auth_cram_md5;
 +                              if (imap_exec(ctx, &cb, "AUTHENTICATE CRAM-MD5") != RESP_OK) {
 +                                      fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
 +                                      goto bail;
 +                              }
 +                      } else {
 +                              fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
 +                              goto bail;
 +                      }
 +              } else {
 +                      if (!imap->buf.sock.ssl)
 +                              imap_warn("*** IMAP Warning *** Password is being "
 +                                        "sent in the clear\n");
 +                      if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
 +                              fprintf(stderr, "IMAP error: LOGIN failed\n");
 +                              goto bail;
 +                      }
                }
        } /* !preauth */
  
@@@ -1431,8 -1306,14 +1431,14 @@@ static int count_messages(struct msg_da
  
        while (1) {
                if (!prefixcmp(p, "From ")) {
+                       p = strstr(p+5, "\nFrom: ");
+                       if (!p) break;
+                       p = strstr(p+7, "\nDate: ");
+                       if (!p) break;
+                       p = strstr(p+7, "\nSubject: ");
+                       if (!p) break;
+                       p += 10;
                        count++;
-                       p += 5;
                }
                p = strstr(p+5, "\nFrom ");
                if (!p)
@@@ -1473,6 -1354,18 +1479,6 @@@ static int split_msg(struct msg_data *a
        return 1;
  }
  
 -static struct imap_server_conf server = {
 -      NULL,   /* name */
 -      NULL,   /* tunnel */
 -      NULL,   /* host */
 -      0,      /* port */
 -      NULL,   /* user */
 -      NULL,   /* pass */
 -      0,      /* use_ssl */
 -      1,      /* ssl_verify */
 -      0,      /* use_html */
 -};
 -
  static char *imap_folder;
  
  static int git_imap_config(const char *key, const char *val, void *cb)
                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;
  }