All of lore.kernel.org
 help / color / mirror / Atom feed
* imap.preformattedHTML and imap.sslverify
@ 2010-02-06 19:26 Junio C Hamano
  2010-02-08 22:31 ` Jeremy White
  0 siblings, 1 reply; 49+ messages in thread
From: Junio C Hamano @ 2010-02-06 19:26 UTC (permalink / raw)
  To: git; +Cc: Jeremy White, Robert Shearman

I hate to bring up a topic that is almost a year old, but has either of
these configuration variables ever worked?

The code does this while reading its configuration file:

        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))
                        return 0;

                if (!val)
                        return config_error_nonbool(key);
                ...
                else 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);

Two issues:

 - The body of the function is protected by "nonbool" written back in the
   days when there was no boolean variables in imap.* namespace.  Hence,
   a user cannot write

           [imap]
                sslverify

   and turn it on.  The user needs to write


           [imap]
                sslverify = True

   which is against the parsing rules for boolean variables.

 - The config parser downcases the key before calling the parse callback
   function, so !strcmp("preformattedHTML", key) will never trigger.

The fix is obvious (see below), but I am far more disturbed by the
apparent lack of testing.  Especially, preformattedHTML one would have
never worked as setting the configuration is the only way to trigger this.

Could peole _test_ this patch and report, as I don't use this program at
all.

Thanks.

 imap-send.c |   15 ++++++++-------
 1 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index de8114b..ea769a9 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1335,11 +1335,16 @@ static int git_imap_config(const char *key, const char *val, void *cb)
 	if (strncmp(key, imap_key, sizeof imap_key - 1))
 		return 0;
 
-	if (!val)
-		return config_error_nonbool(key);
-
 	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)) {
@@ -1360,10 +1365,6 @@ 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("sslverify", key))
-		server.ssl_verify = git_config_bool(key, val);
-	else if (!strcmp("preformattedHTML", key))
-		server.use_html = git_config_bool(key, val);
 	return 0;
 }
 

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: imap.preformattedHTML and imap.sslverify
  2010-02-06 19:26 imap.preformattedHTML and imap.sslverify Junio C Hamano
@ 2010-02-08 22:31 ` Jeremy White
  2010-02-08 23:05   ` Junio C Hamano
  0 siblings, 1 reply; 49+ messages in thread
From: Jeremy White @ 2010-02-08 22:31 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Robert Shearman

Hi Junio,

>            [imap]
>                 sslverify = True

*blush* - I never looked at the parsing rules; = True, or = 1 would have seemed fine to me.
I blame Rob.  He led me astray.  That's my story, and I'm sticking to it <grin>.

>  - The config parser downcases the key before calling the parse callback
>    function, so !strcmp("preformattedHTML", key) will never trigger.

Um...the patch I submitted used the (admittedly badly named) key 'html':
  http://marc.info/?l=git&m=123445427011604&w=2

I'm guessing that you changed it to preformattedHTML prior to committing;
that was something we discussed:
  http://marc.info/?l=git&m=123453315529656&w=2

So I think I can claim 'not guilty!' on that one (but only that one :-/).

> Could peole _test_ this patch and report, as I don't use this program at
> all.

I did confirm that your patch does work.

However, I assumed that my original patch was rejected.  
I never realized that it had been applied.

That clearly means that no one has ever used this option.

I'll remain unhurt if you revert it (c64d84f1452ec56fd1586493a0b0707bf7442c42), but
let me know if you choose to apply your patch instead; I'll make a point
to use it.

Cheers,

Jeremy

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: imap.preformattedHTML and imap.sslverify
  2010-02-08 22:31 ` Jeremy White
@ 2010-02-08 23:05   ` Junio C Hamano
  2010-02-09 12:09     ` [PATCH 0/4] Some improvements for git-imap-send Hitoshi Mitake
                       ` (4 more replies)
  0 siblings, 5 replies; 49+ messages in thread
From: Junio C Hamano @ 2010-02-08 23:05 UTC (permalink / raw)
  To: Jeremy White; +Cc: git, Robert Shearman

Jeremy White <jwhite@codeweavers.com> writes:

> I did confirm that your patch does work.
>
> However, I assumed that my original patch was rejected.  
> I never realized that it had been applied.
>
> That clearly means that no one has ever used this option.

Thanks.  I didn't mean to witch-hunt (did I ever say "the originally
submitted patches were untested"?), and I apologise if you took it that
way.  You were Cc'ed because as the origin of the patch you were likely
to be using that feature.

I just wanted to make sure that the problem I thought I saw was real, and
wanted to make sure that I was not overlooking subtleties in the current
code people who are using the feature are relying upon.

Will apply to maint and merge upwards.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH 0/4] Some improvements for git-imap-send
  2010-02-08 23:05   ` Junio C Hamano
@ 2010-02-09 12:09     ` Hitoshi Mitake
  2010-02-09 15:06       ` Jeff King
                         ` (6 more replies)
  2010-02-09 12:09     ` [PATCH 1/4] Add base64 encoder and decoder Hitoshi Mitake
                       ` (3 subsequent siblings)
  4 siblings, 7 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-09 12:09 UTC (permalink / raw)
  To: gitster; +Cc: git, Hitoshi Mitake, jwhite, robertshearman

Hi Git folks,

Thanks for your patch
( http://article.gmane.org/gmane.comp.version-control.git/139186 ),
now I can enable boolean values
with no assign statement in imap section like "preformattedHTML" in .gitconfig.

This patch series adds two new options to git-imap-send,
"auth-method" and "lf-to-crlf".

Users can specify authenticate method with "auth-method".
This patch also adds new auth method "CRAM-MD5".

Some strict IMAP servers (e.g. Cyrus) don't allow
"bare newlines" in messages.
If "lf-to-crlf" enabled, git-imap-send
converts LF to CRLF("\r\n") before storing messages
to draft folder.

If you think these are useful, please use.

Thanks,

Hitoshi Mitake (4):
  Add base64 encoder and decoder
  Add stuffs for MD5 hash algorithm
  git-imap-send: Implement CRAM-MD5 auth method
  git-imap-send: Add method to convert from LF to CRLF

 Documentation/git-imap-send.txt |    9 +
 Makefile                        |    6 +
 base64.c                        |  122 ++++++++
 base64.h                        |   36 +++
 imap-send.c                     |  137 ++++++++--
 md5.c                           |  600 +++++++++++++++++++++++++++++++++++++++
 md5.h                           |   61 ++++
 md5_hmac.c                      |  137 +++++++++
 md5_hmac.h                      |   36 +++
 9 files changed, 1127 insertions(+), 17 deletions(-)
 create mode 100644 base64.c
 create mode 100644 base64.h
 create mode 100644 md5.c
 create mode 100644 md5.h
 create mode 100644 md5_hmac.c
 create mode 100644 md5_hmac.h

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH 1/4] Add base64 encoder and decoder
  2010-02-08 23:05   ` Junio C Hamano
  2010-02-09 12:09     ` [PATCH 0/4] Some improvements for git-imap-send Hitoshi Mitake
@ 2010-02-09 12:09     ` Hitoshi Mitake
  2010-02-09 14:45       ` Erik Faye-Lund
  2010-02-09 12:09     ` [PATCH 2/4] Add stuffs for MD5 hash algorithm Hitoshi Mitake
                       ` (2 subsequent siblings)
  4 siblings, 1 reply; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-09 12:09 UTC (permalink / raw)
  To: gitster; +Cc: git, Hitoshi Mitake, Jeremy White, Robert Shearman

This patch imports base64 encoder and decoder from libsylph 2.5.0 .
Main purpose is implementing CRAM-MD5 auth method for git-imap-send.

Cc: Jeremy White <jwhite@codeweavers.com>
Cc: Robert Shearman <robertshearman@gmail.com>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
---
 Makefile |    2 +
 base64.c |  122 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 base64.h |   36 ++++++++++++++++++
 3 files changed, 160 insertions(+), 0 deletions(-)
 create mode 100644 base64.c
 create mode 100644 base64.h

diff --git a/Makefile b/Makefile
index 7bf2fca..2b6b8b2 100644
--- a/Makefile
+++ b/Makefile
@@ -421,6 +421,7 @@ XDIFF_LIB=xdiff/lib.a
 LIB_H += advice.h
 LIB_H += archive.h
 LIB_H += attr.h
+LIB_H += base64.h
 LIB_H += blob.h
 LIB_H += builtin.h
 LIB_H += cache.h
@@ -488,6 +489,7 @@ LIB_OBJS += archive.o
 LIB_OBJS += archive-tar.o
 LIB_OBJS += archive-zip.o
 LIB_OBJS += attr.o
+LIB_OBJS += base64.o
 LIB_OBJS += base85.o
 LIB_OBJS += bisect.o
 LIB_OBJS += blob.o
diff --git a/base64.c b/base64.c
new file mode 100644
index 0000000..4504643
--- /dev/null
+++ b/base64.c
@@ -0,0 +1,122 @@
+/*
+ * base64.c
+ *
+ * imported from libsylph 2.5.0
+ * by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
+ * 
+ */
+
+/*
+ * LibSylph -- E-Mail client library
+ * Copyright (C) 1999-2005 Hiroyuki Yamamoto
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "base64.h"
+
+static const char base64char[64] =
+	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+static const char base64val[128] = {
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+	-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
+	-1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
+	15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+	-1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+	41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1
+};
+
+#define BASE64VAL(c)	(isascii((char)c) ? base64val[(int)(c)] : -1)
+
+void base64_encode(char *out, const char *in, int inlen)
+{
+	const char *inp = in;
+	char *outp = out;
+
+	while (inlen >= 3) {
+		*outp++ = base64char[(inp[0] >> 2) & 0x3f];
+		*outp++ = base64char[((inp[0] & 0x03) << 4) |
+				     ((inp[1] >> 4) & 0x0f)];
+		*outp++ = base64char[((inp[1] & 0x0f) << 2) |
+				     ((inp[2] >> 6) & 0x03)];
+		*outp++ = base64char[inp[2] & 0x3f];
+
+		inp += 3;
+		inlen -= 3;
+	}
+
+	if (inlen > 0) {
+		*outp++ = base64char[(inp[0] >> 2) & 0x3f];
+		if (inlen == 1) {
+			*outp++ = base64char[(inp[0] & 0x03) << 4];
+			*outp++ = '=';
+		} else {
+			*outp++ = base64char[((inp[0] & 0x03) << 4) |
+					     ((inp[1] >> 4) & 0x0f)];
+			*outp++ = base64char[((inp[1] & 0x0f) << 2)];
+		}
+		*outp++ = '=';
+	}
+
+	*outp = '\0';
+}
+
+int base64_decode(char *out, const char *in, int inlen)
+{
+	const char *inp = in;
+	char *outp = out;
+	char buf[4];
+
+	if (inlen < 0)
+		inlen = INT_MAX;
+
+	while (inlen >= 4 && *inp != '\0') {
+		buf[0] = *inp++;
+		inlen--;
+		if (BASE64VAL(buf[0]) == -1) break;
+
+		buf[1] = *inp++;
+		inlen--;
+		if (BASE64VAL(buf[1]) == -1) break;
+
+		buf[2] = *inp++;
+		inlen--;
+		if (buf[2] != '=' && BASE64VAL(buf[2]) == -1) break;
+
+		buf[3] = *inp++;
+		inlen--;
+		if (buf[3] != '=' && BASE64VAL(buf[3]) == -1) break;
+
+		*outp++ = ((BASE64VAL(buf[0]) << 2) & 0xfc) |
+			  ((BASE64VAL(buf[1]) >> 4) & 0x03);
+		if (buf[2] != '=') {
+			*outp++ = ((BASE64VAL(buf[1]) & 0x0f) << 4) |
+				  ((BASE64VAL(buf[2]) >> 2) & 0x0f);
+			if (buf[3] != '=') {
+				*outp++ = ((BASE64VAL(buf[2]) & 0x03) << 6) |
+					   (BASE64VAL(buf[3]) & 0x3f);
+			}
+		}
+	}
+
+	return outp - out;
+}
diff --git a/base64.h b/base64.h
new file mode 100644
index 0000000..5771c5a
--- /dev/null
+++ b/base64.h
@@ -0,0 +1,36 @@
+/*
+ * base64.h
+ *
+ * imported from libsylph 2.5.0
+ * by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
+ * 
+ */
+
+/*
+ * LibSylph -- E-Mail client library
+ * Copyright (C) 1999-2005 Hiroyuki Yamamoto
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef BASE64_H
+#define BASE64_H
+
+#include <limits.h>
+
+void base64_encode(char *out, const char *in, int inlen);
+int base64_decode(char *out, const char *in, int inlen);
+
+#endif /* BASE64_H */
-- 
1.7.0.rc1.52.gf7cc2.dirty

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 2/4] Add stuffs for MD5 hash algorithm
  2010-02-08 23:05   ` Junio C Hamano
  2010-02-09 12:09     ` [PATCH 0/4] Some improvements for git-imap-send Hitoshi Mitake
  2010-02-09 12:09     ` [PATCH 1/4] Add base64 encoder and decoder Hitoshi Mitake
@ 2010-02-09 12:09     ` Hitoshi Mitake
  2010-02-09 12:09     ` [PATCH 3/4] git-imap-send: Implement CRAM-MD5 auth method Hitoshi Mitake
  2010-02-09 12:09     ` [PATCH 4/4] git-imap-send: Add method to convert from LF to CRLF Hitoshi Mitake
  4 siblings, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-09 12:09 UTC (permalink / raw)
  To: gitster; +Cc: git, Hitoshi Mitake, Jeremy White, Robert Shearman

This patch imports stuffs related to MD5 hash algorithm
from libsylph 2.5.0 .
Main purpose is implementing CRAM-MD5 auth method for git-imap-send.

Cc: Jeremy White <jwhite@codeweavers.com>
Cc: Robert Shearman <robertshearman@gmail.com>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
---
 Makefile   |    4 +
 md5.c      |  600 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 md5.h      |   61 ++++++
 md5_hmac.c |  137 ++++++++++++++
 md5_hmac.h |   36 ++++
 5 files changed, 838 insertions(+), 0 deletions(-)
 create mode 100644 md5.c
 create mode 100644 md5.h
 create mode 100644 md5_hmac.c
 create mode 100644 md5_hmac.h

diff --git a/Makefile b/Makefile
index 2b6b8b2..6c28413 100644
--- a/Makefile
+++ b/Makefile
@@ -448,6 +448,8 @@ LIB_H += list-objects.h
 LIB_H += ll-merge.h
 LIB_H += log-tree.h
 LIB_H += mailmap.h
+LIB_H += md5.h
+LIB_H += md5_hmac.h
 LIB_H += merge-recursive.h
 LIB_H += notes.h
 LIB_H += object.h
@@ -535,6 +537,8 @@ LIB_OBJS += lockfile.o
 LIB_OBJS += log-tree.o
 LIB_OBJS += mailmap.o
 LIB_OBJS += match-trees.o
+LIB_OBJS += md5.o
+LIB_OBJS += md5_hmac.o
 LIB_OBJS += merge-file.o
 LIB_OBJS += merge-recursive.o
 LIB_OBJS += name-hash.o
diff --git a/md5.c b/md5.c
new file mode 100644
index 0000000..35230e2
--- /dev/null
+++ b/md5.c
@@ -0,0 +1,600 @@
+/*
+ * md5.c
+ *
+ * imported from libsylph 2.5.0
+ * by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
+ * 
+ */
+
+/*
+
+  GNet API added by David Helder <dhelder@umich.edu> 2000-6-11.  All
+  additions and changes placed in the public domain.
+
+  Files originally from: http://www.gxsnmp.org/CVS/gxsnmp/
+
+  Modified the prefix of functions to prevent conflict with original GNet.
+
+ */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#include "md5.h"
+#include <string.h>
+#include <stdlib.h>
+
+
+/* ************************************************************ */
+/* Code below is from Colin Plumb implementation 		*/
+
+
+
+struct MD5Context {
+	unsigned int buf[4];
+	unsigned int bits[2];
+	unsigned char  in[64];
+	int     doByteReverse;
+};
+
+static void MD5Init(struct MD5Context *context);
+static void MD5Update(struct MD5Context *context, unsigned char const *buf,
+		      unsigned int len);
+static void MD5Final(unsigned char digest[16], struct MD5Context *context);
+static void MD5Transform(unsigned int buf[4], unsigned int const in[16]);
+
+/*
+ * This is needed to make RSAREF happy on some MS-DOS compilers.
+ */
+typedef struct MD5Context MD5_CTX;
+
+static void byteReverse(unsigned char *buf, unsigned int longs);
+
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+void  byteReverse(unsigned char *buf, unsigned int longs)
+{
+	unsigned int t;
+	do {
+		t = (unsigned int) ((unsigned int) buf[3] << 8 | buf[2]) << 16 |
+			((unsigned int) buf[1] << 8 | buf[0]);
+		*(unsigned int *) buf = t;
+		buf += 4;
+	} while (--longs);
+}
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+	ctx->buf[0] = 0x67452301;
+	ctx->buf[1] = 0xefcdab89;
+	ctx->buf[2] = 0x98badcfe;
+	ctx->buf[3] = 0x10325476;
+
+	ctx->bits[0] = 0;
+	ctx->bits[1] = 0;
+
+#if (BYTE_ORDER == BIG_ENDIAN)
+	ctx->doByteReverse = 1;
+#else
+	ctx->doByteReverse = 0;
+#endif
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void  MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned int len)
+{
+	unsigned int t;
+
+	/* Update bitcount */
+	t = ctx->bits[0];
+	if ((ctx->bits[0] = t + ((unsigned int) len << 3)) < t)
+		ctx->bits[1]++;		/* Carry from low to high */
+	ctx->bits[1] += len >> 29;
+
+	t = (t >> 3) & 0x3f;	/* Bytes already in shsInfo->data */
+
+	/* Handle any leading odd-sized chunks */
+
+	if (t) {
+		unsigned char *p = (unsigned char *) ctx->in + t;
+
+		t = 64 - t;
+		if (len < t) {
+			memmove(p, buf, len);
+			return;
+		}
+		memmove(p, buf, t);
+		if (ctx->doByteReverse)
+			byteReverse(ctx->in, 16);
+		MD5Transform(ctx->buf, (unsigned int *) ctx->in);
+		buf += t;
+		len -= t;
+	}
+	/* Process data in 64-byte chunks */
+
+	while (len >= 64) {
+		memmove(ctx->in, buf, 64);
+		if (ctx->doByteReverse)
+			byteReverse(ctx->in, 16);
+		MD5Transform(ctx->buf, (unsigned int *) ctx->in);
+		buf += 64;
+		len -= 64;
+	}
+
+	/* Handle any remaining bytes of data. */
+
+	memmove(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern 
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void  MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+	unsigned int count;
+	unsigned char *p;
+
+	/* Compute number of bytes mod 64 */
+	count = (ctx->bits[0] >> 3) & 0x3F;
+
+	/* Set the first char of padding to 0x80.  This is safe since there is
+	   always at least one byte free */
+	p = ctx->in + count;
+	*p++ = 0x80;
+
+	/* Bytes of padding needed to make 64 bytes */
+	count = 64 - 1 - count;
+
+	/* Pad out to 56 mod 64 */
+	if (count < 8) {
+		/* Two lots of padding:  Pad the first block to 64 bytes */
+		memset(p, 0, count);
+		if (ctx->doByteReverse)
+			byteReverse(ctx->in, 16);
+		MD5Transform(ctx->buf, (unsigned int *) ctx->in);
+
+		/* Now fill the next block with 56 bytes */
+		memset(ctx->in, 0, 56);
+	} else {
+		/* Pad block to 56 bytes */
+		memset(p, 0, count - 8);
+	}
+	if (ctx->doByteReverse)
+		byteReverse(ctx->in, 14);
+
+	/* Append length in bits and transform */
+	((unsigned int *) ctx->in)[14] = ctx->bits[0];
+	((unsigned int *) ctx->in)[15] = ctx->bits[1];
+
+	MD5Transform(ctx->buf, (unsigned int *) ctx->in);
+	if (ctx->doByteReverse)
+		byteReverse((unsigned char *) ctx->buf, 4);
+	memmove(digest, ctx->buf, 16);
+	memset(ctx, 0, sizeof(ctx));	/* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+	( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+void MD5Transform(unsigned int buf[4], unsigned int const in[16])
+{
+	register unsigned int a, b, c, d;
+
+	a = buf[0];
+	b = buf[1];
+	c = buf[2];
+	d = buf[3];
+
+	MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+	MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+	MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+	MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+	MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+	MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+	MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+	MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+	MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+	MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+	MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+	MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+	MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+	MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+	MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+	MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+	MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+	MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+	MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+	MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+	MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+	MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+	MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+	MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+	MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+	MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+	MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+	MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+	MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+	MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+	MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+	MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+	MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+	MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+	MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+	MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+	MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+	MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+	MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+	MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+	MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+	MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+	MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+	MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+	MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+	MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+	MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+	MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+	MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+	MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+	MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+	MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+	MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+	MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+	MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+	MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+	MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+	MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+	MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+	MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+	MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+	MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+	MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+	MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+	buf[0] += a;
+	buf[1] += b;
+	buf[2] += c;
+	buf[3] += d;
+}
+
+/* ************************************************************ */
+/* Code below is David Helder's API for GNet			*/
+
+struct _SMD5
+{
+	struct MD5Context ctx;
+	char digest[S_GNET_MD5_HASH_LENGTH];
+};
+
+/**
+ *  s_gnet_md5_new:
+ *  @buffer: buffer to hash
+ *  @length: length of @buffer
+ * 
+ *  Creates a #SMD5 from @buffer.
+ *
+ *  Returns: a new #SMD5.
+ *
+ **/
+SMD5 *s_gnet_md5_new(const unsigned char *buffer, unsigned int length)
+{
+	SMD5 *md5;
+
+	md5 = calloc(sizeof(SMD5), 1);
+	MD5Init (&md5->ctx);
+	MD5Update (&md5->ctx, buffer, length);
+	MD5Final ((void *) &md5->digest, &md5->ctx);
+
+	return md5;
+}
+
+/**
+ *  s_gnet_md5_new_string:
+ *  @str: hexidecimal string
+ * 
+ *  Creates a #SMD5 from @str.  @str is a hexidecimal string
+ *  representing the digest.
+ *
+ *  Returns: a new #SMD5.
+ *
+ **/
+SMD5 *s_gnet_md5_new_string (const char *str)
+{
+	SMD5 *md5;
+	unsigned int i;
+
+	if (!str)
+		return NULL;
+	if (!(strlen(str) >= (S_GNET_MD5_HASH_LENGTH * 2)))
+		return NULL;
+
+	md5 = calloc(sizeof(SMD5), 1);
+
+	for (i = 0; i < (S_GNET_MD5_HASH_LENGTH * 2); ++i) {
+		unsigned int val = 0;
+
+		switch (str[i]) {
+		case '0':	val = 0;	break;
+		case '1':	val = 1;	break;
+		case '2':	val = 2;	break;
+		case '3':	val = 3;	break;
+		case '4':	val = 4;	break;
+		case '5':	val = 5;	break;
+		case '6':	val = 6;	break;
+		case '7':	val = 7;	break;
+		case '8':	val = 8;	break;
+		case '9':	val = 9;	break;
+		case 'A':
+		case 'a':	val = 10;	break;
+		case 'B':
+		case 'b':	val = 11;	break;
+		case 'C':
+		case 'c':	val = 12;	break;
+		case 'D':
+		case 'd':	val = 13;	break;
+		case 'E':
+		case 'e':	val = 14;	break;
+		case 'F':
+		case 'f':	val = 15;	break;
+		default:
+			return NULL;
+		}
+
+		if (i % 2)
+			md5->digest[i / 2] |= val;
+		else
+			md5->digest[i / 2] = val << 4;
+	}
+
+	return md5;
+}
+
+/**
+ *  s_gnet_md5_clone
+ *  @md5: a #SMD5
+ * 
+ *  Copies a #SMD5.
+ *
+ *  Returns: a copy of @md5.
+ *
+ **/
+SMD5 *s_gnet_md5_clone(const SMD5 *md5)
+{
+	SMD5 *md52;
+
+	if (!md5)
+		return NULL;
+
+	md52 = calloc (sizeof(SMD5), 1);
+	md52->ctx = md5->ctx;
+	memcpy(md52->digest, md5->digest, sizeof(md5->digest));
+
+	return md52;
+}
+
+/** 
+ *  s_gnet_md5_delete
+ *  @md5: a #SMD5
+ *
+ *  Deletes a #SMD5.
+ *
+ **/
+void s_gnet_md5_delete(SMD5 *md5)
+{
+	if (md5)
+		free (md5);
+}
+
+/**
+ *  s_gnet_md5_new_incremental
+ *
+ *  Creates a #SMD5 incrementally.  After creating a #SMD5, call
+ *  s_gnet_md5_update() one or more times to hash data.  Finally, call
+ *  s_gnet_md5_final() to compute the final hash value.
+ *
+ *  Returns: a new #SMD5.
+ *
+ **/
+SMD5 *s_gnet_md5_new_incremental(void)
+{
+	SMD5 *md5;
+
+	md5 = calloc (sizeof(SMD5), 1);
+	MD5Init (&md5->ctx);
+	return md5;
+}
+
+/**
+ *  s_gnet_md5_update
+ *  @md5: a #SMD5
+ *  @buffer: buffer to add
+ *  @length: length of @buffer
+ *
+ *  Updates the hash with @buffer.  This may be called several times
+ *  on a hash created by s_gnet_md5_new_incremental() before being
+ *  finalized by calling s_gnet_md5_final().
+ * 
+ **/
+void s_gnet_md5_update(SMD5 *md5, const unsigned char *buffer, unsigned int length)
+{
+	if (!md5)
+		return;
+
+	MD5Update (&md5->ctx, buffer, length);
+}
+
+/**
+ *  s_gnet_md5_final
+ *  @md5: a #SMD5
+ *
+ *  Calcuates the final hash value of a #SMD5.  This should only be
+ *  called on an #SMD5 created by s_gnet_md5_new_incremental().
+ *
+ **/
+void s_gnet_md5_final(SMD5 *md5)
+{
+	if (!md5)
+		return;
+
+	MD5Final ((void *) &md5->digest, &md5->ctx);
+}
+
+/**
+ *  s_gnet_md5_equal
+ *  @p1: first #SMD5.
+ *  @p2: second #SMD5.
+ *
+ *  Compares two #SMD5's for equality.
+ *
+ *  Returns: TRUE if they are equal; FALSE otherwise.
+ *
+ **/
+int s_gnet_md5_equal(const void *p1, const void *p2)
+{
+	SMD5 *md5a = (SMD5*) p1;
+	SMD5 *md5b = (SMD5*) p2;
+	unsigned int i;
+
+	for (i = 0; i < S_GNET_MD5_HASH_LENGTH; ++i)
+		if (md5a->digest[i] != md5b->digest[i])
+			return FALSE;
+	return TRUE;
+}
+
+/**
+ *  s_gnet_md5_hash
+ *  @p: a #SMD5
+ *
+ *  Creates a hash code for a #SMD5 for use with GHashTable.  This
+ *  hash value is not the same as the MD5 digest.
+ *
+ *  Returns: the hash code for @p.
+ *
+ **/
+unsigned int s_gnet_md5_hash(const void *p)
+{
+	const SMD5 *md5 = (const SMD5*) p;
+	const unsigned int *q;
+
+	if (!md5)
+		return 0;
+
+	q = (const unsigned int*) md5->digest;
+
+	return (q[0] ^ q[1] ^ q[2] ^ q[3]);
+}
+
+/**
+ *  s_gnet_md5_get_digest
+ *  @md5: a #SMD5
+ *
+ *  Gets the raw MD5 digest.
+ *
+ *  Returns: a callee-owned buffer containing the MD5 hash digest.
+ *  The buffer is %S_GNET_MD5_HASH_LENGTH bytes long.
+ *
+ **/
+char *s_gnet_md5_get_digest (const SMD5 *md5)
+{
+	if (!md5)
+		return NULL;
+  
+	return (char*) md5->digest;
+}
+
+static char bits2hex[16] = { '0', '1', '2', '3', 
+			      '4', '5', '6', '7',
+			      '8', '9', 'a', 'b',
+			      'c', 'd', 'e', 'f' };
+
+/**
+ *  s_gnet_md5_get_string
+ *  @md5: a #SMD5
+ *
+ *  Gets the digest represented a human-readable string.
+ *
+ *  Returns: a hexadecimal string representing the digest.  The string
+ *  is 2 * %S_GNET_MD5_HASH_LENGTH bytes long and NULL terminated.  The
+ *  string is caller owned.
+ *
+ **/
+char *s_gnet_md5_get_string (const SMD5 *md5)
+{
+	char *str;
+	unsigned int i;
+
+	if (!md5)
+		return NULL;
+
+	str = calloc(sizeof(char), S_GNET_MD5_HASH_LENGTH * 2 + 1);
+	str[S_GNET_MD5_HASH_LENGTH * 2] = '\0';
+
+	for (i = 0; i < S_GNET_MD5_HASH_LENGTH; ++i) {
+		str[i * 2]       = bits2hex[(md5->digest[i] & 0xF0) >> 4];
+		str[(i * 2) + 1] = bits2hex[(md5->digest[i] & 0x0F)     ];
+	}
+
+	return str;
+}
+
+/**
+ * s_gnet_md5_copy_string
+ * @md5: a #SMD5
+ * @buffer: buffer at least 2 * %S_GNET_MD5_HASH_LENGTH bytes long
+ *
+ * Copies the digest, represented as a string, into @buffer.  The
+ * string is not NULL terminated.
+ * 
+ **/
+void s_gnet_md5_copy_string (const SMD5 *md5, char *buffer)
+{
+	unsigned int i;
+
+	if (!md5)
+		return;
+	if (!buffer)
+		return;
+
+	for (i = 0; i < S_GNET_MD5_HASH_LENGTH; ++i) {
+		buffer[i * 2]       = bits2hex[(md5->digest[i] & 0xF0) >> 4];
+		buffer[(i * 2) + 1] = bits2hex[(md5->digest[i] & 0x0F)     ];
+	}
+}
diff --git a/md5.h b/md5.h
new file mode 100644
index 0000000..958e383
--- /dev/null
+++ b/md5.h
@@ -0,0 +1,61 @@
+/*
+ * md5.h
+ *
+ * imported from libsylph 2.5.0
+ * by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
+ * 
+ */
+
+/**
+
+   This code is in the public domain.  See md5.c for details.
+
+   Authors:
+     Colin Plumb [original author]
+     David Helder [GNet API]
+
+   Modified the prefix of functions to prevent conflict with original GNet.
+
+ */
+
+
+#ifndef MD5_H
+#define MD5_H
+
+/**
+ *  SMD5
+ *
+ *  SMD5 is a MD5 hash.
+ *
+ **/
+typedef struct _SMD5 SMD5;
+
+/**
+ *  S_GNET_MD5_HASH_LENGTH
+ *
+ *  Length of the MD5 hash in bytes.
+ **/
+#define S_GNET_MD5_HASH_LENGTH	16
+#define DIGEST_HEX_LEN S_GNET_MD5_HASH_LENGTH * 2 + 1
+
+SMD5 *s_gnet_md5_new(const unsigned char *buffer, unsigned int length);
+SMD5 *s_gnet_md5_new_string(const char *str);
+SMD5 *s_gnet_md5_clone(const SMD5 *md5);
+void s_gnet_md5_delete(SMD5 *md5);
+	
+SMD5 *s_gnet_md5_new_incremental(void);
+void s_gnet_md5_update(SMD5 *md5, const unsigned char *buffer, unsigned int length);
+void s_gnet_md5_final(SMD5 *md5);
+	
+int s_gnet_md5_equal(const void *p1, const void *p2);
+unsigned int s_gnet_md5_hash(const void *p);
+	
+char *s_gnet_md5_get_digest(const SMD5 *md5);
+char *s_gnet_md5_get_string(const SMD5 *md5);
+	
+void s_gnet_md5_copy_string(const SMD5 *md5, char *buffer);
+
+#define FALSE 0
+#define TRUE 1
+
+#endif /* MD5_H */
diff --git a/md5_hmac.c b/md5_hmac.c
new file mode 100644
index 0000000..3f0c6de
--- /dev/null
+++ b/md5_hmac.c
@@ -0,0 +1,137 @@
+/*
+ * md5_hmac.h
+ *
+ * imported from libsylph 2.5.0
+ * by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
+ * 
+ */
+
+/*
+ * LibSylph -- E-Mail client library
+ * Copyright (C) 1999-2006 Hiroyuki Yamamoto
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "md5.h"
+#include "md5_hmac.h"
+
+/*
+** Function: md5_hmac_get
+** taken from the file rfc2104.txt
+** originally written by Martin Schaaf <mascha@ma-scha.de>
+** rewritten by Hiroyuki Yamamoto <hiro-y@kcn.ne.jp>
+*/
+static SMD5*
+md5_hmac_get(const unsigned char *text, int text_len,
+	     const unsigned char *key, int key_len)
+{
+	SMD5 *md5;
+	unsigned char k_ipad[64];    /* inner padding -
+			       * key XORd with ipad
+			       */
+	unsigned char k_opad[64];    /* outer padding -
+			       * key XORd with opad
+			       */
+	unsigned char digest[S_GNET_MD5_HASH_LENGTH];
+	int i;
+
+	/* start out by storing key in pads */
+	memset(k_ipad, 0, sizeof k_ipad);
+	memset(k_opad, 0, sizeof k_opad);
+
+	if (key_len > 64) {
+		/* if key is longer than 64 bytes reset it to key=MD5(key) */
+		SMD5 *tmd5;
+
+		tmd5 = s_gnet_md5_new(key, key_len);
+		memcpy(k_ipad, s_gnet_md5_get_digest(tmd5),
+		       S_GNET_MD5_HASH_LENGTH);
+		memcpy(k_opad, s_gnet_md5_get_digest(tmd5),
+		       S_GNET_MD5_HASH_LENGTH);
+		s_gnet_md5_delete(tmd5);
+	} else {
+		memcpy(k_ipad, key, key_len);
+		memcpy(k_opad, key, key_len);
+	}
+
+	/*
+	 * the HMAC_MD5 transform looks like:
+	 *
+	 * MD5(K XOR opad, MD5(K XOR ipad, text))
+	 *
+	 * where K is an n byte key
+	 * ipad is the byte 0x36 repeated 64 times
+	 * opad is the byte 0x5c repeated 64 times
+	 * and text is the data being protected
+	 */
+
+	/* XOR key with ipad and opad values */
+	for (i = 0; i < 64; i++) {
+		k_ipad[i] ^= 0x36;
+		k_opad[i] ^= 0x5c;
+	}
+
+	/*
+	 * perform inner MD5
+	 */
+	md5 = s_gnet_md5_new_incremental();	/* init context for 1st
+						 * pass */
+	s_gnet_md5_update(md5, k_ipad, 64);	/* start with inner pad */
+	s_gnet_md5_update(md5, text, text_len);	/* then text of datagram */
+	s_gnet_md5_final(md5);			/* finish up 1st pass */
+	memcpy(digest, s_gnet_md5_get_digest(md5), S_GNET_MD5_HASH_LENGTH);
+	s_gnet_md5_delete(md5);
+
+	/*
+	 * perform outer MD5
+	 */
+	md5 = s_gnet_md5_new_incremental();	/* init context for 2nd
+						 * pass */
+	s_gnet_md5_update(md5, k_opad, 64);	/* start with outer pad */
+	s_gnet_md5_update(md5, digest, 16);	/* then results of 1st
+						 * hash */
+	s_gnet_md5_final(md5);			/* finish up 2nd pass */
+
+	return md5;
+}
+
+void
+md5_hmac(unsigned char *digest,
+	 const unsigned char *text, int text_len,
+	 const unsigned char *key, int key_len)
+{
+	SMD5 *md5;
+
+	md5 = md5_hmac_get(text, text_len, key, key_len);
+	memcpy(digest, s_gnet_md5_get_digest(md5), S_GNET_MD5_HASH_LENGTH);
+	s_gnet_md5_delete(md5);
+}
+
+void
+md5_hex_hmac(char *hexdigest,
+	     const unsigned char *text, int text_len,
+	     const unsigned char *key, int key_len)
+{
+	SMD5 *md5;
+
+	md5 = md5_hmac_get(text, text_len, key, key_len);
+	s_gnet_md5_copy_string(md5, hexdigest);
+	hexdigest[S_GNET_MD5_HASH_LENGTH * 2] = '\0';
+	s_gnet_md5_delete(md5);
+}
diff --git a/md5_hmac.h b/md5_hmac.h
new file mode 100644
index 0000000..27619af
--- /dev/null
+++ b/md5_hmac.h
@@ -0,0 +1,36 @@
+/*
+ * md5_hmac.h
+ *
+ * imported from libsylph 2.5.0
+ * by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
+ * 
+ */
+
+/*
+ * LibSylph -- E-Mail client library
+ * Copyright (C) 1999-2005 Hiroyuki Yamamoto
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef MD5_HMAC_H
+#define MD5_HMAC_H
+
+#include "md5.h"
+
+void md5_hmac(unsigned char *digest, const unsigned char* text, int text_len, const unsigned char* key, int key_len);
+void md5_hex_hmac(char *hexdigest, const unsigned char *text, int text_len, const unsigned char *key, int key_len);
+
+#endif /* MD5_HMAC_H */
-- 
1.7.0.rc1.52.gf7cc2.dirty

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 3/4] git-imap-send: Implement CRAM-MD5 auth method
  2010-02-08 23:05   ` Junio C Hamano
                       ` (2 preceding siblings ...)
  2010-02-09 12:09     ` [PATCH 2/4] Add stuffs for MD5 hash algorithm Hitoshi Mitake
@ 2010-02-09 12:09     ` Hitoshi Mitake
  2010-02-09 14:22       ` Erik Faye-Lund
  2010-02-09 12:09     ` [PATCH 4/4] git-imap-send: Add method to convert from LF to CRLF Hitoshi Mitake
  4 siblings, 1 reply; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-09 12:09 UTC (permalink / raw)
  To: gitster; +Cc: git, Hitoshi Mitake, Jeremy White, Robert Shearman

Some IMAP servers require CRAM-MD5 authenticate method
to their clients for secure connection.

git-imap-send didn't provided this auth method,
so I implemented it. Now git-imap-send users can store
their patches to IMAP server if server requires CRAM-MD5
auth method.

If you want to use it, add line:
	auth-method = CRAM-MD5
to [imap] section of your .gitconfig .

This patch also adds description to Documentation/git-imap-send.txt .

Cc: Jeremy White <jwhite@codeweavers.com>
Cc: Robert Shearman <robertshearman@gmail.com>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
---
 Documentation/git-imap-send.txt |    4 ++
 imap-send.c                     |  107 ++++++++++++++++++++++++++++++++------
 2 files changed, 94 insertions(+), 17 deletions(-)

diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index 57db955..9b2052f 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -71,6 +71,10 @@ imap.preformattedHTML::
 	option causes Thunderbird to send the patch as a plain/text,
 	format=fixed email.  Default is `false`.
 
+imap.auth-method::
+	Specify authenticate method for authentication with IMAP server.
+	Current supported method is 'CRAM-MD5' only.
+
 Examples
 ~~~~~~~~
 
diff --git a/imap-send.c b/imap-send.c
index ba72fa4..3ed9fc2 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -25,10 +25,16 @@
 #include "cache.h"
 #include "exec_cmd.h"
 #include "run-command.h"
+
+#include "base64.h"
+#include "md5_hmac.h"
+
 #ifdef NO_OPENSSL
 typedef void *SSL;
 #endif
 
+static int login;
+
 struct store_conf {
 	char *name;
 	const char *path; /* should this be here? its interpretation is driver-specific */
@@ -140,6 +146,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 {
@@ -214,6 +234,7 @@ enum CAPABILITY {
 	LITERALPLUS,
 	NAMESPACE,
 	STARTTLS,
+	AUTH_CRAM_MD5,
 };
 
 static const char *cap_list[] = {
@@ -222,6 +243,7 @@ static const char *cap_list[] = {
 	"LITERAL+",
 	"NAMESPACE",
 	"STARTTLS",
+	"AUTH=CRAM-MD5",
 };
 
 #define RESP_OK    0
@@ -526,8 +548,9 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
 		get_cmd_result(ctx, NULL);
 
 	bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
-			   "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
-			   cmd->tag, cmd->cmd, cmd->cb.dlen);
+			  "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
+			  cmd->tag, cmd->cmd, cmd->cb.dlen);
+
 	if (Verbose) {
 		if (imap->num_in_progress)
 			printf("(%d in progress) ", imap->num_in_progress);
@@ -949,6 +972,35 @@ static void imap_close_store(struct store *ctx)
 	free(ctx);
 }
 
+static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt)
+{
+	int ret;
+	char digest[DIGEST_HEX_LEN];
+	char buf[256], base64_out[256];
+
+	memset(buf, 0, 256);
+	base64_decode(buf, prompt, strlen(prompt));
+
+	memset(digest, 0, DIGEST_HEX_LEN);
+	md5_hex_hmac(digest, (const unsigned char *)buf, strlen(buf),
+		     (const unsigned char *)server.pass, strlen(server.pass));
+
+	memset(buf, 0, 256);
+	strcpy(buf, server.user);
+	strcpy(buf + strlen(buf), " ");
+	strcpy(buf + strlen(buf), digest);
+	memset(base64_out, 0, 256);
+	base64_encode(base64_out, buf, strlen(buf));
+
+	ret = socket_write(&ctx->imap->buf.sock, base64_out, strlen(base64_out));
+	if (ret != strlen(base64_out)) {
+		fprintf(stderr, "IMAP error: sending response failed\n");
+		return -1;
+	}
+
+	return 0;
+}
+
 static struct store *imap_open_store(struct imap_server_conf *srvc)
 {
 	struct imap_store *ctx;
@@ -1101,6 +1153,7 @@ static struct 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;
@@ -1130,10 +1183,37 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
 		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;
+				}
+				login = 1;
+			} else {
+				fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+				goto bail;
+			}
 		}
+
+		if (!login)
+			if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+				fprintf(stderr, "IMAP error: LOGIN failed\n");
+				goto bail;
+			}
 	} /* !preauth */
 
 	ctx->prefix = "";
@@ -1258,6 +1338,7 @@ static int read_message(FILE *f, struct msg_data *msg)
 
 	msg->len  = buf.len;
 	msg->data = strbuf_detach(&buf, NULL);
+
 	return msg->len;
 }
 
@@ -1307,21 +1388,10 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
 
 	msg->data = xmemdupz(data, msg->len);
 	*ofs += msg->len;
+
 	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)
@@ -1361,6 +1431,9 @@ 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("auth-method", key))
+		server.auth_method = xstrdup(val);
+
 	return 0;
 }
 
-- 
1.7.0.rc1.52.gf7cc2.dirty

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH 4/4] git-imap-send: Add method to convert from LF to CRLF
  2010-02-08 23:05   ` Junio C Hamano
                       ` (3 preceding siblings ...)
  2010-02-09 12:09     ` [PATCH 3/4] git-imap-send: Implement CRAM-MD5 auth method Hitoshi Mitake
@ 2010-02-09 12:09     ` Hitoshi Mitake
  2010-02-09 16:15       ` Jakub Narebski
  2010-02-09 17:24       ` Linus Torvalds
  4 siblings, 2 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-09 12:09 UTC (permalink / raw)
  To: gitster; +Cc: git, Hitoshi Mitake, Jeremy White, Robert Shearman

Some strict IMAP servers (e.g. Cyrus) don't
allow "bare newlines ('\n')" in messages.
So I added new boolean option "lf-to-crlf" to imap section.
If this option enabled, git-imap-send converts LF to CRLF("\r\n").

If you want to use it, add line:
	lf-to-crlf
to [imap] section of your .gitconfig .

This patch also adds description to Documentation/git-imap-send.txt .

Cc: Jeremy White <jwhite@codeweavers.com>
Cc: Robert Shearman <robertshearman@gmail.com>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
---
 Documentation/git-imap-send.txt |    5 +++++
 imap-send.c                     |   30 ++++++++++++++++++++++++++++++
 2 files changed, 35 insertions(+), 0 deletions(-)

diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index 9b2052f..c4c0670 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -75,6 +75,11 @@ imap.auth-method::
 	Specify authenticate method for authentication with IMAP server.
 	Current supported method is 'CRAM-MD5' only.
 
+imap.lf-to-crlf::
+	If you use strict IMAP server (e.g. Cyrus),
+	"bare newlines ('\n')" in messages are not allowed.
+	If this option enabled, git-imap-send converts LF to CRLF("\r\n").
+
 Examples
 ~~~~~~~~
 
diff --git a/imap-send.c b/imap-send.c
index 3ed9fc2..5329e28 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -147,6 +147,7 @@ struct imap_server_conf {
 	int ssl_verify;
 	int use_html;
 	char *auth_method;
+	int lf_to_crlf;
 };
 
 static struct imap_server_conf server = {
@@ -160,6 +161,7 @@ static struct imap_server_conf server = {
 	1,   	/* ssl_verify */
 	0,   	/* use_html */
 	NULL,	/* auth_method */
+	0,	/* lf_to_crlf */
 };
 
 struct imap_store_conf {
@@ -1242,6 +1244,29 @@ static int imap_make_flags(int flags, char *buf)
 	return d;
 }
 
+static void lf_to_crlf(struct msg_data *msg)
+{
+	char *new;
+	int i, j, lfnum = 0;
+
+	for (i = 0; i < msg->len; i++) {
+		if (msg->data[i] == '\n')
+			lfnum++;
+	}
+	new = xcalloc(msg->len + lfnum, sizeof(char));
+	for (i = 0, j = 0; i < msg->len; i++) {
+		if (msg->data[i] != '\n') {
+			new[j++] = msg->data[i];
+			continue;
+		}
+		new[j++] = '\r';
+		new[j++] = '\n';
+	}
+	msg->len += lfnum;
+	free(msg->data);
+	msg->data = new;
+}
+
 static int imap_store_msg(struct store *gctx, struct msg_data *data)
 {
 	struct imap_store *ctx = (struct imap_store *)gctx;
@@ -1253,6 +1278,9 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data)
 
 	memset(&cb, 0, sizeof(cb));
 
+	if (server.lf_to_crlf)
+		lf_to_crlf(data);
+
 	cb.dlen = data->len;
 	cb.data = xmalloc(cb.dlen);
 	memcpy(cb.data, data->data, data->len);
@@ -1408,6 +1436,8 @@ static int git_imap_config(const char *key, const char *val, void *cb)
 		server.ssl_verify = git_config_bool(key, val);
 	else if (!strcmp("preformattedhtml", key))
 		server.use_html = git_config_bool(key, val);
+	else if (!strcmp("lf-to-crlf", key))
+		server.lf_to_crlf = git_config_bool(key, val);
 	else if (!val)
 		return config_error_nonbool(key);
 
-- 
1.7.0.rc1.52.gf7cc2.dirty

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH 3/4] git-imap-send: Implement CRAM-MD5 auth method
  2010-02-09 12:09     ` [PATCH 3/4] git-imap-send: Implement CRAM-MD5 auth method Hitoshi Mitake
@ 2010-02-09 14:22       ` Erik Faye-Lund
  2010-02-11 14:55         ` Hitoshi Mitake
  0 siblings, 1 reply; 49+ messages in thread
From: Erik Faye-Lund @ 2010-02-09 14:22 UTC (permalink / raw)
  To: Hitoshi Mitake; +Cc: gitster, git, Jeremy White, Robert Shearman

On Tue, Feb 9, 2010 at 1:09 PM, Hitoshi Mitake
<mitake@dcl.info.waseda.ac.jp> wrote:
> +static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt)
> +{
> +       int ret;
> +       char digest[DIGEST_HEX_LEN];
> +       char buf[256], base64_out[256];
> +
> +       memset(buf, 0, 256);
> +       base64_decode(buf, prompt, strlen(prompt));
> +
> +       memset(digest, 0, DIGEST_HEX_LEN);
> +       md5_hex_hmac(digest, (const unsigned char *)buf, strlen(buf),
> +                    (const unsigned char *)server.pass, strlen(server.pass));
> +
> +       memset(buf, 0, 256);
> +       strcpy(buf, server.user);
> +       strcpy(buf + strlen(buf), " ");
> +       strcpy(buf + strlen(buf), digest);
> +       memset(base64_out, 0, 256);
> +       base64_encode(base64_out, buf, strlen(buf));
> +
> +       ret = socket_write(&ctx->imap->buf.sock, base64_out, strlen(base64_out));

Since this is the only location in this function that accesses
anything inside ctx, how about just passing the imap_socket itself to
the function? That'd make it a bit simpler if, say, I was rewriting
send-email in C and wanted to add CRAM-MD5 AUTH support (given that
I'd done the work to use imap_socket first)...

-- 
Erik "kusma" Faye-Lund

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 1/4] Add base64 encoder and decoder
  2010-02-09 12:09     ` [PATCH 1/4] Add base64 encoder and decoder Hitoshi Mitake
@ 2010-02-09 14:45       ` Erik Faye-Lund
  2010-02-11 14:37         ` Hitoshi Mitake
  0 siblings, 1 reply; 49+ messages in thread
From: Erik Faye-Lund @ 2010-02-09 14:45 UTC (permalink / raw)
  To: Hitoshi Mitake; +Cc: gitster, git, Jeremy White, Robert Shearman

On Tue, Feb 9, 2010 at 1:09 PM, Hitoshi Mitake
<mitake@dcl.info.waseda.ac.jp> wrote:
> +void base64_encode(char *out, const char *in, int inlen)
> +{
> +       const char *inp = in;
> +       char *outp = out;

...Why? It's copying the pointers to pointers of identical type with
different names, and never using the originals again... Looks like a
sloppy extraction from another code-base to me.

> +
> +       while (inlen >= 3) {
> +               *outp++ = base64char[(inp[0] >> 2) & 0x3f];
> +               *outp++ = base64char[((inp[0] & 0x03) << 4) |
> +                                    ((inp[1] >> 4) & 0x0f)];
> +               *outp++ = base64char[((inp[1] & 0x0f) << 2) |
> +                                    ((inp[2] >> 6) & 0x03)];
> +               *outp++ = base64char[inp[2] & 0x3f];
> +
> +               inp += 3;
> +               inlen -= 3;
> +       }
> +
> +       if (inlen > 0) {
> +               *outp++ = base64char[(inp[0] >> 2) & 0x3f];
> +               if (inlen == 1) {
> +                       *outp++ = base64char[(inp[0] & 0x03) << 4];
> +                       *outp++ = '=';
> +               } else {
> +                       *outp++ = base64char[((inp[0] & 0x03) << 4) |
> +                                            ((inp[1] >> 4) & 0x0f)];
> +                       *outp++ = base64char[((inp[1] & 0x0f) << 2)];
> +               }
> +               *outp++ = '=';
> +       }
> +
> +       *outp = '\0';
> +}

If inlen is 0, a single '=' should be emitted (plus the obvious zero
termination). It could be that the code deals with that by making sure
that inlen never is zero, though.

-- 
Erik "kusma" Faye-Lund

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 0/4] Some improvements for git-imap-send
  2010-02-09 12:09     ` [PATCH 0/4] Some improvements for git-imap-send Hitoshi Mitake
@ 2010-02-09 15:06       ` Jeff King
  2010-02-09 15:13         ` Erik Faye-Lund
  2010-02-11 14:38       ` [PATCH v2 1/2] git-imap-send: Add CRAM-MD5 authenticate method support Hitoshi Mitake
                         ` (5 subsequent siblings)
  6 siblings, 1 reply; 49+ messages in thread
From: Jeff King @ 2010-02-09 15:06 UTC (permalink / raw)
  To: Hitoshi Mitake; +Cc: gitster, git, jwhite, robertshearman

On Tue, Feb 09, 2010 at 09:09:01PM +0900, Hitoshi Mitake wrote:

>  base64.c                        |  122 ++++++++
>  base64.h                        |   36 +++
>  md5.c                           |  600 +++++++++++++++++++++++++++++++++++++++
>  md5.h                           |   61 ++++
>  md5_hmac.c                      |  137 +++++++++
>  md5_hmac.h                      |   36 +++

That's a lot of extra code. Doesn't imap-send already conditionally
compile against openssl for starttls support? Can't we just get all
three of these algorithms from openssl?

-Peff

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 0/4] Some improvements for git-imap-send
  2010-02-09 15:06       ` Jeff King
@ 2010-02-09 15:13         ` Erik Faye-Lund
  2010-02-09 16:57           ` Jeff King
  0 siblings, 1 reply; 49+ messages in thread
From: Erik Faye-Lund @ 2010-02-09 15:13 UTC (permalink / raw)
  To: Jeff King; +Cc: Hitoshi Mitake, gitster, git, jwhite, robertshearman

On Tue, Feb 9, 2010 at 4:06 PM, Jeff King <peff@peff.net> wrote:
> On Tue, Feb 09, 2010 at 09:09:01PM +0900, Hitoshi Mitake wrote:
>
>>  base64.c                        |  122 ++++++++
>>  base64.h                        |   36 +++
>>  md5.c                           |  600 +++++++++++++++++++++++++++++++++++++++
>>  md5.h                           |   61 ++++
>>  md5_hmac.c                      |  137 +++++++++
>>  md5_hmac.h                      |   36 +++
>
> That's a lot of extra code. Doesn't imap-send already conditionally
> compile against openssl for starttls support? Can't we just get all
> three of these algorithms from openssl?
>

I don't think OpenSSL includes SASL-support that is needed for
STARTTLS. But it might make sense to use something like GSASL[1]
instead of rolling all the SASL-mechanisms ourselves.

[1]: http://www.gnu.org/software/gsasl/

-- 
Erik "kusma" Faye-Lund

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 4/4] git-imap-send: Add method to convert from LF to CRLF
  2010-02-09 12:09     ` [PATCH 4/4] git-imap-send: Add method to convert from LF to CRLF Hitoshi Mitake
@ 2010-02-09 16:15       ` Jakub Narebski
  2010-02-11 14:37         ` Hitoshi Mitake
  2010-02-09 17:24       ` Linus Torvalds
  1 sibling, 1 reply; 49+ messages in thread
From: Jakub Narebski @ 2010-02-09 16:15 UTC (permalink / raw)
  To: Hitoshi Mitake; +Cc: gitster, git, Jeremy White, Robert Shearman

Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> writes:

> Some strict IMAP servers (e.g. Cyrus) don't
> allow "bare newlines ('\n')" in messages.
> So I added new boolean option "lf-to-crlf" to imap section.
> If this option enabled, git-imap-send converts LF to CRLF("\r\n").
> 
> If you want to use it, add line:
> 	lf-to-crlf
> to [imap] section of your .gitconfig .
> 
> This patch also adds description to Documentation/git-imap-send.txt .

> +imap.lf-to-crlf::
> +	If you use strict IMAP server (e.g. Cyrus),
> +	"bare newlines ('\n')" in messages are not allowed.
> +	If this option enabled, git-imap-send converts LF to CRLF("\r\n").
> +

If you take a look at Documentation/config.txt at the names of other
config variables, you would see that they have

  core.fileMode::
  core.ignoreCygwinFSTricks::
  core.quotepath::
  core.safecrlf::

names, i.e. either camelCase or allsmallcase, and not

  imap.lf-to-crlf::

with '-' to separate parts.  Values can and do use this syntax, like
e.g. `blank-at-eol` for core.whitespace.

The only outlier is add.ignore-errors (not add.ignoreerrors or
add.ignoreErrors).


The same is true for the other config variable you propsed in 3/4
patch.
-- 
Jakub Narebski
Poland
ShadeHawk on #git

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 0/4] Some improvements for git-imap-send
  2010-02-09 15:13         ` Erik Faye-Lund
@ 2010-02-09 16:57           ` Jeff King
  2010-02-09 18:37             ` Erik Faye-Lund
  0 siblings, 1 reply; 49+ messages in thread
From: Jeff King @ 2010-02-09 16:57 UTC (permalink / raw)
  To: kusmabite; +Cc: Hitoshi Mitake, gitster, git, jwhite, robertshearman

On Tue, Feb 09, 2010 at 04:13:26PM +0100, Erik Faye-Lund wrote:

> On Tue, Feb 9, 2010 at 4:06 PM, Jeff King <peff@peff.net> wrote:
> > On Tue, Feb 09, 2010 at 09:09:01PM +0900, Hitoshi Mitake wrote:
> >
> >>  base64.c                        |  122 ++++++++
> >>  base64.h                        |   36 +++
> >>  md5.c                           |  600 +++++++++++++++++++++++++++++++++++++++
> >>  md5.h                           |   61 ++++
> >>  md5_hmac.c                      |  137 +++++++++
> >>  md5_hmac.h                      |   36 +++
> >
> > That's a lot of extra code. Doesn't imap-send already conditionally
> > compile against openssl for starttls support? Can't we just get all
> > three of these algorithms from openssl?
> >
> 
> I don't think OpenSSL includes SASL-support that is needed for
> STARTTLS. But it might make sense to use something like GSASL[1]
> instead of rolling all the SASL-mechanisms ourselves.

Did you mean "SASL-support that is needed for CRAM-MD5"? The SASL needed
for that is pretty simple. Hitoshi's patch 3/4 does all of that already
in less than 100 lines.  Using a "real" sasl library might get us more
authentication methods than CRAM-MD5, but I don't know that anyone
necessarily cares about them.

But using openssl to replace the low-level routines in patches 1+2 would
drop almost 1000 lines, and not significantly change his 3/4.

Personally, I don't care either way about using a SASL library. It's an
extra dependency, but one that is optional for this feature. But
somebody will have to do the work to integrate it, whereas I think using
openssl is only a few lines of change. If somebody wants to do that
work, then great.

-Peff

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 4/4] git-imap-send: Add method to convert from LF to CRLF
  2010-02-09 12:09     ` [PATCH 4/4] git-imap-send: Add method to convert from LF to CRLF Hitoshi Mitake
  2010-02-09 16:15       ` Jakub Narebski
@ 2010-02-09 17:24       ` Linus Torvalds
  2010-02-09 18:20         ` Junio C Hamano
  1 sibling, 1 reply; 49+ messages in thread
From: Linus Torvalds @ 2010-02-09 17:24 UTC (permalink / raw)
  To: Hitoshi Mitake; +Cc: gitster, git, Jeremy White, Robert Shearman



On Tue, 9 Feb 2010, Hitoshi Mitake wrote:
>
> Some strict IMAP servers (e.g. Cyrus) don't
> allow "bare newlines ('\n')" in messages.
> So I added new boolean option "lf-to-crlf" to imap section.
> If this option enabled, git-imap-send converts LF to CRLF("\r\n").
> 
> If you want to use it, add line:
> 	lf-to-crlf
> to [imap] section of your .gitconfig .

Hmm. Should this even be an option? Maybe we should _always_ do CRLF. That 
does seem to be the technically correct thing to do for SMTP and IMAP.

rfc2822 (smtp) is pretty clear that CRLF is the line ending, and neither 
CR nor LF must ever be sent individually. That's true both for headers and 
the body of the email. The same goes for rfc3501 (imap).

So I suspect that CRLF should be unconditional.

		Linus

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 4/4] git-imap-send: Add method to convert from LF to CRLF
  2010-02-09 17:24       ` Linus Torvalds
@ 2010-02-09 18:20         ` Junio C Hamano
  2010-02-11 14:38           ` Hitoshi Mitake
  0 siblings, 1 reply; 49+ messages in thread
From: Junio C Hamano @ 2010-02-09 18:20 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Hitoshi Mitake, gitster, git, Jeremy White, Robert Shearman

Linus Torvalds <torvalds@linux-foundation.org> writes:

> So I suspect that CRLF should be unconditional.

That matches my reading of the RFC.  Thanks.

Hitoshi, can we have a modified version of 4/4 as a separate patch, so
that we can apply it independently from the rest of the series?

As to the MD5 implementation, I am somewhat torn.

Even if your md5 implementation were vastly superiour and faster than
OpenSSL one (I don't know), the use of MD5 is not performance critical
like SHA-1 is (for which we uniformly use Linus's block SHA-1 these days);
the only thing it would be buying us to have our own implementation is one
less dependency for people who do want to use imap-send with CRAM-MD5 but
without SSL support.  How common is that combination?

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 0/4] Some improvements for git-imap-send
  2010-02-09 16:57           ` Jeff King
@ 2010-02-09 18:37             ` Erik Faye-Lund
  2010-02-09 18:54               ` Jeff King
  0 siblings, 1 reply; 49+ messages in thread
From: Erik Faye-Lund @ 2010-02-09 18:37 UTC (permalink / raw)
  To: Jeff King; +Cc: Hitoshi Mitake, gitster, git, jwhite, robertshearman

On Tue, Feb 9, 2010 at 5:57 PM, Jeff King <peff@peff.net> wrote:
> On Tue, Feb 09, 2010 at 04:13:26PM +0100, Erik Faye-Lund wrote:
>
>> On Tue, Feb 9, 2010 at 4:06 PM, Jeff King <peff@peff.net> wrote:
>> > On Tue, Feb 09, 2010 at 09:09:01PM +0900, Hitoshi Mitake wrote:
>> >
>> >>  base64.c                        |  122 ++++++++
>> >>  base64.h                        |   36 +++
>> >>  md5.c                           |  600 +++++++++++++++++++++++++++++++++++++++
>> >>  md5.h                           |   61 ++++
>> >>  md5_hmac.c                      |  137 +++++++++
>> >>  md5_hmac.h                      |   36 +++
>> >
>> > That's a lot of extra code. Doesn't imap-send already conditionally
>> > compile against openssl for starttls support? Can't we just get all
>> > three of these algorithms from openssl?
>> >
>>
>> I don't think OpenSSL includes SASL-support that is needed for
>> STARTTLS. But it might make sense to use something like GSASL[1]
>> instead of rolling all the SASL-mechanisms ourselves.
>
> Did you mean "SASL-support that is needed for CRAM-MD5"? The SASL needed
> for that is pretty simple. Hitoshi's patch 3/4 does all of that already
> in less than 100 lines.  Using a "real" sasl library might get us more
> authentication methods than CRAM-MD5, but I don't know that anyone
> necessarily cares about them.
>

No, that's not what I meant. I agree that CRAM-MD5 should be
sufficient, but to be honest I'd already thought that once you have an
SSL connection, plaintext would also be sufficient. So I'm thinking of
this addition as a "hmpf, some server requires stuff that is really
over the top - perhaps we'll have this problem later with other
servers, and we'd be better off just using some well-tested
implementation". But that's kinda philosophical.

> But using openssl to replace the low-level routines in patches 1+2 would
> drop almost 1000 lines, and not significantly change his 3/4.
>
> Personally, I don't care either way about using a SASL library. It's an
> extra dependency, but one that is optional for this feature. But
> somebody will have to do the work to integrate it, whereas I think using
> openssl is only a few lines of change. If somebody wants to do that
> work, then great.
>

I agree.


-- 
Erik "kusma" Faye-Lund

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 0/4] Some improvements for git-imap-send
  2010-02-09 18:37             ` Erik Faye-Lund
@ 2010-02-09 18:54               ` Jeff King
  0 siblings, 0 replies; 49+ messages in thread
From: Jeff King @ 2010-02-09 18:54 UTC (permalink / raw)
  To: kusmabite; +Cc: Hitoshi Mitake, gitster, git, jwhite, robertshearman

On Tue, Feb 09, 2010 at 07:37:44PM +0100, Erik Faye-Lund wrote:

> > Did you mean "SASL-support that is needed for CRAM-MD5"? The SASL needed
> > for that is pretty simple. Hitoshi's patch 3/4 does all of that already
> > in less than 100 lines.  Using a "real" sasl library might get us more
> > authentication methods than CRAM-MD5, but I don't know that anyone
> > necessarily cares about them.
> 
> No, that's not what I meant. I agree that CRAM-MD5 should be
> sufficient, but to be honest I'd already thought that once you have an
> SSL connection, plaintext would also be sufficient. So I'm thinking of
> this addition as a "hmpf, some server requires stuff that is really
> over the top - perhaps we'll have this problem later with other
> servers, and we'd be better off just using some well-tested
> implementation". But that's kinda philosophical.

Ah, I see. Yes, it's possible that we may want to support other
authentication methods later. In my experience, CRAM-MD5 is the only
common non-plain IMAP mechanism used by IMAP, but I admit it has been
quite a number of years since I actively paid attention to such things.

I'd leave that choice to whoever feels like implementing it. :)

-Peff

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 1/4] Add base64 encoder and decoder
  2010-02-09 14:45       ` Erik Faye-Lund
@ 2010-02-11 14:37         ` Hitoshi Mitake
  0 siblings, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-11 14:37 UTC (permalink / raw)
  To: kusmabite
  Cc: Erik Faye-Lund, gitster, git, Jeremy White, Robert Shearman, peff

(2010年02月09日 23:45), Erik Faye-Lund wrote:
> On Tue, Feb 9, 2010 at 1:09 PM, Hitoshi Mitake
> <mitake@dcl.info.waseda.ac.jp>  wrote:
>> +void base64_encode(char *out, const char *in, int inlen)
>> +{
>> +       const char *inp = in;
>> +       char *outp = out;
>
> ...Why? It's copying the pointers to pointers of identical type with
> different names, and never using the originals again... Looks like a
> sloppy extraction from another code-base to me.
>
>> +
>> +       while (inlen>= 3) {
>> +               *outp++ = base64char[(inp[0]>>  2)&  0x3f];
>> +               *outp++ = base64char[((inp[0]&  0x03)<<  4) |
>> +                                    ((inp[1]>>  4)&  0x0f)];
>> +               *outp++ = base64char[((inp[1]&  0x0f)<<  2) |
>> +                                    ((inp[2]>>  6)&  0x03)];
>> +               *outp++ = base64char[inp[2]&  0x3f];
>> +
>> +               inp += 3;
>> +               inlen -= 3;
>> +       }
>> +
>> +       if (inlen>  0) {
>> +               *outp++ = base64char[(inp[0]>>  2)&  0x3f];
>> +               if (inlen == 1) {
>> +                       *outp++ = base64char[(inp[0]&  0x03)<<  4];
>> +                       *outp++ = '=';
>> +               } else {
>> +                       *outp++ = base64char[((inp[0]&  0x03)<<  4) |
>> +                                            ((inp[1]>>  4)&  0x0f)];
>> +                       *outp++ = base64char[((inp[1]&  0x0f)<<  2)];
>> +               }
>> +               *outp++ = '=';
>> +       }
>> +
>> +       *outp = '\0';
>> +}
>
> If inlen is 0, a single '=' should be emitted (plus the obvious zero
> termination). It could be that the code deals with that by making sure
> that inlen never is zero, though.
>


Thanks for your review, I was careless...

I decided to use base64 and md5 stuffs OpenSSL provides.
I'll remove 1 and 2 of my patch series.

Thanks,

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 4/4] git-imap-send: Add method to convert from LF to CRLF
  2010-02-09 16:15       ` Jakub Narebski
@ 2010-02-11 14:37         ` Hitoshi Mitake
  0 siblings, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-11 14:37 UTC (permalink / raw)
  To: Jakub Narebski; +Cc: gitster, git, Jeremy White, Robert Shearman

(2010年02月10日 01:15), Jakub Narebski wrote:
> Hitoshi Mitake<mitake@dcl.info.waseda.ac.jp>  writes:
>
>> Some strict IMAP servers (e.g. Cyrus) don't
>> allow "bare newlines ('\n')" in messages.
>> So I added new boolean option "lf-to-crlf" to imap section.
>> If this option enabled, git-imap-send converts LF to CRLF("\r\n").
>>
>> If you want to use it, add line:
>> 	lf-to-crlf
>> to [imap] section of your .gitconfig .
>>
>> This patch also adds description to Documentation/git-imap-send.txt .
>
>> +imap.lf-to-crlf::
>> +	If you use strict IMAP server (e.g. Cyrus),
>> +	"bare newlines ('\n')" in messages are not allowed.
>> +	If this option enabled, git-imap-send converts LF to CRLF("\r\n").
>> +
>
> If you take a look at Documentation/config.txt at the names of other
> config variables, you would see that they have
>
>    core.fileMode::
>    core.ignoreCygwinFSTricks::
>    core.quotepath::
>    core.safecrlf::
>
> names, i.e. either camelCase or allsmallcase, and not
>
>    imap.lf-to-crlf::
>
> with '-' to separate parts.  Values can and do use this syntax, like
> e.g. `blank-at-eol` for core.whitespace.
>
> The only outlier is add.ignore-errors (not add.ignoreerrors or
> add.ignoreErrors).
>
>
> The same is true for the other config variable you propsed in 3/4
> patch.

Thanks for your review, I'll rename these values
based on custom of Git.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 4/4] git-imap-send: Add method to convert from LF to CRLF
  2010-02-09 18:20         ` Junio C Hamano
@ 2010-02-11 14:38           ` Hitoshi Mitake
  0 siblings, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-11 14:38 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Linus Torvalds, git, Jeremy White, Robert Shearman

(2010年02月10日 03:20), Junio C Hamano wrote:
> Linus Torvalds<torvalds@linux-foundation.org>  writes:
>
>> So I suspect that CRLF should be unconditional.
>
> That matches my reading of the RFC.  Thanks.

OK. I'll make converting LF to CRLF default behaviour.

>
> Hitoshi, can we have a modified version of 4/4 as a separate patch, so
> that we can apply it independently from the rest of the series?

Yeah, but as I describe below, I'll rewrite
my patch series and these only has 2 parts.

>
> As to the MD5 implementation, I am somewhat torn.
>
> Even if your md5 implementation were vastly superiour and faster than
> OpenSSL one (I don't know), the use of MD5 is not performance critical
> like SHA-1 is (for which we uniformly use Linus's block SHA-1 these days);
> the only thing it would be buying us to have our own implementation is one
> less dependency for people who do want to use imap-send with CRAM-MD5 but
> without SSL support.  How common is that combination?

Yes, using OpenSSL's md5 is better implementation.
But git-imap-send.c also asuumes non existence of OpenSSL.

And in theory, CRAM-MD5 and STARTTLS are independent things.
So I import MD5 things from libsylph.
But, as you told, CRAM-MD5 without STARTTLS
is not so common situation.
If you permit, I'd like to use MD5 functions of OpenSSL.

It seems that OpenSSL also provides base64 stuffs,
so I will be able to reduce patch series into 2 part.

I'll send these later. Please discard my previous series.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v2 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-09 12:09     ` [PATCH 0/4] Some improvements for git-imap-send Hitoshi Mitake
  2010-02-09 15:06       ` Jeff King
@ 2010-02-11 14:38       ` Hitoshi Mitake
  2010-02-11 14:55         ` Erik Faye-Lund
  2010-02-11 14:38       ` [PATCH v2 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box Hitoshi Mitake
                         ` (4 subsequent siblings)
  6 siblings, 1 reply; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-11 14:38 UTC (permalink / raw)
  To: gitster, gitster
  Cc: git, Hitoshi Mitake, Erik Faye-Lund, Jakub Narebski,
	Linus Torvalds, Jeff King

This patch makes git-imap-send CRAM-MD5 authenticate method ready.
In theory, CRAM-MD5 authenticate method doesn't depend on SSL,
but for easy of maintainance, this patch uses base64 and md5 stuffs of OpenSSL.
So if you want to use CRAM-MD5 authenticate method,
you have to build git-imap-send with OpenSSL library.

Cc: Erik Faye-Lund <kusmabite@googlemail.com>
Cc: Jakub Narebski <jnareb@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jeff King <peff@peff.org>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
---
 Documentation/git-imap-send.txt |    4 +
 imap-send.c                     |  144 ++++++++++++++++++++++++++++++++++-----
 2 files changed, 131 insertions(+), 17 deletions(-)

diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index 57db955..6cafbe2 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -71,6 +71,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
 ~~~~~~~~
 
diff --git a/imap-send.c b/imap-send.c
index ba72fa4..dcd8a2a 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -25,10 +25,16 @@
 #include "cache.h"
 #include "exec_cmd.h"
 #include "run-command.h"
+
 #ifdef NO_OPENSSL
 typedef void *SSL;
+#else
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
 #endif
 
+static int login;
+
 struct store_conf {
 	char *name;
 	const char *path; /* should this be here? its interpretation is driver-specific */
@@ -140,6 +146,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 {
@@ -214,6 +234,7 @@ enum CAPABILITY {
 	LITERALPLUS,
 	NAMESPACE,
 	STARTTLS,
+	AUTH_CRAM_MD5,
 };
 
 static const char *cap_list[] = {
@@ -222,6 +243,7 @@ static const char *cap_list[] = {
 	"LITERAL+",
 	"NAMESPACE",
 	"STARTTLS",
+	"AUTH=CRAM-MD5",
 };
 
 #define RESP_OK    0
@@ -526,8 +548,9 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
 		get_cmd_result(ctx, NULL);
 
 	bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
-			   "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
-			   cmd->tag, cmd->cmd, cmd->cb.dlen);
+			  "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
+			  cmd->tag, cmd->cmd, cmd->cb.dlen);
+
 	if (Verbose) {
 		if (imap->num_in_progress)
 			printf("(%d in progress) ", imap->num_in_progress);
@@ -949,6 +972,72 @@ static void imap_close_store(struct store *ctx)
 	free(ctx);
 }
 
+/*
+ * hexchar() and cram() functions are
+ * based on ones of isync project ... http://isync.sf.net/
+ * Thanks!
+ */
+static char hexchar(unsigned int b)
+{
+	return b < 10 ? '0' + b : 'a' + (b - 10);
+}
+
+#ifndef NO_OPENSSL
+
+static char *cram(const char *challenge_64, const char *user, const char *pass)
+{
+	int i;
+	HMAC_CTX hmac;
+	char hash[16], hex[33], challenge[256], response[256];
+	char *response_64;
+
+	memset(challenge, 0, 256);
+	EVP_DecodeBlock((unsigned char *)challenge, (unsigned char *)challenge_64, strlen(challenge_64));
+	HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
+	HMAC_Update(&hmac, (unsigned char *)challenge, strlen(challenge));
+	HMAC_Final(&hmac, (unsigned char *)hash, NULL);
+
+	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);
+	}
+
+	memset(response, 0, 256);
+	snprintf(response, 256, "%s %s", user, hex);
+
+	response_64 = calloc(256 , sizeof(char));
+	EVP_EncodeBlock((unsigned char *)response_64, (unsigned char *)response, strlen(response));
+	return response_64;
+}
+
+#else
+
+static char *cram(const char *challenge_64 __used, const char *user __used, const char *pass __used)
+{
+	fprintf(stderr, "If you want to use CRAM-MD5 authenticate method,"
+		"you have to build git-imap-send with OpenSSL library\n");
+	exit(0);
+}
+
+#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)) {
+		fprintf(stderr, "IMAP error: sending response failed\n");
+		return -1;
+	}
+	free(response);
+
+	return 0;
+}
+
 static struct store *imap_open_store(struct imap_server_conf *srvc)
 {
 	struct imap_store *ctx;
@@ -1101,6 +1190,7 @@ static struct 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;
@@ -1130,10 +1220,37 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
 		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;
+				}
+				login = 1;
+			} else {
+				fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+				goto bail;
+			}
 		}
+
+		if (!login)
+			if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+				fprintf(stderr, "IMAP error: LOGIN failed\n");
+				goto bail;
+			}
 	} /* !preauth */
 
 	ctx->prefix = "";
@@ -1258,6 +1375,7 @@ static int read_message(FILE *f, struct msg_data *msg)
 
 	msg->len  = buf.len;
 	msg->data = strbuf_detach(&buf, NULL);
+
 	return msg->len;
 }
 
@@ -1307,21 +1425,10 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
 
 	msg->data = xmemdupz(data, msg->len);
 	*ofs += msg->len;
+
 	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)
@@ -1361,6 +1468,9 @@ 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;
 }
 
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box
  2010-02-09 12:09     ` [PATCH 0/4] Some improvements for git-imap-send Hitoshi Mitake
  2010-02-09 15:06       ` Jeff King
  2010-02-11 14:38       ` [PATCH v2 1/2] git-imap-send: Add CRAM-MD5 authenticate method support Hitoshi Mitake
@ 2010-02-11 14:38       ` Hitoshi Mitake
  2010-02-11 20:48         ` Junio C Hamano
  2010-02-11 14:45       ` [PATCH v2 1/2] git-imap-send: Add CRAM-MD5 authenticate method support Hitoshi Mitake
                         ` (3 subsequent siblings)
  6 siblings, 1 reply; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-11 14:38 UTC (permalink / raw)
  To: gitster, gitster
  Cc: git, Hitoshi Mitake, Erik Faye-Lund, Jakub Narebski,
	Linus Torvalds, Jeff King

According to RFC of IMAP, all messages must not have "bare newlines ('\n')".
'\n' should be converted to "\r\n" before storing messages to IMAP's mailbox.
This patch implements the converting function to git-imap-send.

Cc: Erik Faye-Lund <kusmabite@googlemail.com>
Cc: Jakub Narebski <jnareb@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jeff King <peff@peff.org>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
---
 imap-send.c |   25 +++++++++++++++++++++++++
 1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index dcd8a2a..dbc72ca 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1279,6 +1279,30 @@ static int imap_make_flags(int flags, char *buf)
 	return d;
 }
 
+static void lf_to_crlf(struct msg_data *msg)
+{
+	char *new;
+	int i, j, lfnum = 0;
+
+	for (i = 0; i < msg->len; i++) {
+		if (msg->data[i] == '\n')
+			lfnum++;
+	}
+	new = xcalloc(msg->len + lfnum, sizeof(char));
+	for (i = 0, j = 0; i < msg->len; i++) {
+		if (msg->data[i] != '\n') {
+			new[j++] = msg->data[i];
+			continue;
+		}
+		new[j++] = '\r';
+		new[j++] = '\n';
+	}
+	msg->len += lfnum;
+	free(msg->data);
+	msg->data = new;
+	msg->crlf = 1;
+}
+
 static int imap_store_msg(struct store *gctx, struct msg_data *data)
 {
 	struct imap_store *ctx = (struct imap_store *)gctx;
@@ -1288,6 +1312,7 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data)
 	int ret, d;
 	char flagstr[128];
 
+	lf_to_crlf(data);
 	memset(&cb, 0, sizeof(cb));
 
 	cb.dlen = data->len;
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-09 12:09     ` [PATCH 0/4] Some improvements for git-imap-send Hitoshi Mitake
                         ` (2 preceding siblings ...)
  2010-02-11 14:38       ` [PATCH v2 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box Hitoshi Mitake
@ 2010-02-11 14:45       ` Hitoshi Mitake
  2010-02-11 14:45       ` [PATCH v2 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box Hitoshi Mitake
                         ` (2 subsequent siblings)
  6 siblings, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-11 14:45 UTC (permalink / raw)
  To: gitster, gitster
  Cc: git, Hitoshi Mitake, Erik Faye-Lund, Jakub Narebski,
	Linus Torvalds, Jeff King

Sorry, I wrote wrong address of Jeff King,
copying email address from Thunderbird is irritant thing for me...
This is resending.

This patch makes git-imap-send CRAM-MD5 authenticate method ready.
In theory, CRAM-MD5 authenticate method doesn't depend on SSL,
but for easy of maintainance, this patch uses base64 and md5 stuffs of OpenSSL.
So if you want to use CRAM-MD5 authenticate method,
you have to build git-imap-send with OpenSSL library.

Cc: Erik Faye-Lund <kusmabite@googlemail.com>
Cc: Jakub Narebski <jnareb@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jeff King <peff@peff.net>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
---
 Documentation/git-imap-send.txt |    4 +
 imap-send.c                     |  144 ++++++++++++++++++++++++++++++++++-----
 2 files changed, 131 insertions(+), 17 deletions(-)

diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index 57db955..6cafbe2 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -71,6 +71,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
 ~~~~~~~~
 
diff --git a/imap-send.c b/imap-send.c
index ba72fa4..dcd8a2a 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -25,10 +25,16 @@
 #include "cache.h"
 #include "exec_cmd.h"
 #include "run-command.h"
+
 #ifdef NO_OPENSSL
 typedef void *SSL;
+#else
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
 #endif
 
+static int login;
+
 struct store_conf {
 	char *name;
 	const char *path; /* should this be here? its interpretation is driver-specific */
@@ -140,6 +146,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 {
@@ -214,6 +234,7 @@ enum CAPABILITY {
 	LITERALPLUS,
 	NAMESPACE,
 	STARTTLS,
+	AUTH_CRAM_MD5,
 };
 
 static const char *cap_list[] = {
@@ -222,6 +243,7 @@ static const char *cap_list[] = {
 	"LITERAL+",
 	"NAMESPACE",
 	"STARTTLS",
+	"AUTH=CRAM-MD5",
 };
 
 #define RESP_OK    0
@@ -526,8 +548,9 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
 		get_cmd_result(ctx, NULL);
 
 	bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
-			   "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
-			   cmd->tag, cmd->cmd, cmd->cb.dlen);
+			  "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
+			  cmd->tag, cmd->cmd, cmd->cb.dlen);
+
 	if (Verbose) {
 		if (imap->num_in_progress)
 			printf("(%d in progress) ", imap->num_in_progress);
@@ -949,6 +972,72 @@ static void imap_close_store(struct store *ctx)
 	free(ctx);
 }
 
+/*
+ * hexchar() and cram() functions are
+ * based on ones of isync project ... http://isync.sf.net/
+ * Thanks!
+ */
+static char hexchar(unsigned int b)
+{
+	return b < 10 ? '0' + b : 'a' + (b - 10);
+}
+
+#ifndef NO_OPENSSL
+
+static char *cram(const char *challenge_64, const char *user, const char *pass)
+{
+	int i;
+	HMAC_CTX hmac;
+	char hash[16], hex[33], challenge[256], response[256];
+	char *response_64;
+
+	memset(challenge, 0, 256);
+	EVP_DecodeBlock((unsigned char *)challenge, (unsigned char *)challenge_64, strlen(challenge_64));
+	HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
+	HMAC_Update(&hmac, (unsigned char *)challenge, strlen(challenge));
+	HMAC_Final(&hmac, (unsigned char *)hash, NULL);
+
+	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);
+	}
+
+	memset(response, 0, 256);
+	snprintf(response, 256, "%s %s", user, hex);
+
+	response_64 = calloc(256 , sizeof(char));
+	EVP_EncodeBlock((unsigned char *)response_64, (unsigned char *)response, strlen(response));
+	return response_64;
+}
+
+#else
+
+static char *cram(const char *challenge_64 __used, const char *user __used, const char *pass __used)
+{
+	fprintf(stderr, "If you want to use CRAM-MD5 authenticate method,"
+		"you have to build git-imap-send with OpenSSL library\n");
+	exit(0);
+}
+
+#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)) {
+		fprintf(stderr, "IMAP error: sending response failed\n");
+		return -1;
+	}
+	free(response);
+
+	return 0;
+}
+
 static struct store *imap_open_store(struct imap_server_conf *srvc)
 {
 	struct imap_store *ctx;
@@ -1101,6 +1190,7 @@ static struct 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;
@@ -1130,10 +1220,37 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
 		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;
+				}
+				login = 1;
+			} else {
+				fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+				goto bail;
+			}
 		}
+
+		if (!login)
+			if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+				fprintf(stderr, "IMAP error: LOGIN failed\n");
+				goto bail;
+			}
 	} /* !preauth */
 
 	ctx->prefix = "";
@@ -1258,6 +1375,7 @@ static int read_message(FILE *f, struct msg_data *msg)
 
 	msg->len  = buf.len;
 	msg->data = strbuf_detach(&buf, NULL);
+
 	return msg->len;
 }
 
@@ -1307,21 +1425,10 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
 
 	msg->data = xmemdupz(data, msg->len);
 	*ofs += msg->len;
+
 	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)
@@ -1361,6 +1468,9 @@ 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;
 }
 
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box
  2010-02-09 12:09     ` [PATCH 0/4] Some improvements for git-imap-send Hitoshi Mitake
                         ` (3 preceding siblings ...)
  2010-02-11 14:45       ` [PATCH v2 1/2] git-imap-send: Add CRAM-MD5 authenticate method support Hitoshi Mitake
@ 2010-02-11 14:45       ` Hitoshi Mitake
  2010-02-12 11:36       ` [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support Hitoshi Mitake
  2010-02-12 11:36       ` [PATCH v4 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box Hitoshi Mitake
  6 siblings, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-11 14:45 UTC (permalink / raw)
  To: gitster, gitster
  Cc: git, Hitoshi Mitake, Erik Faye-Lund, Jakub Narebski,
	Linus Torvalds, Jeff King

Sorry, I wrote wrong address of Jeff King,
copying email address from Thunderbird is irritant thing for me...
This is resending.

According to RFC of IMAP, all messages must not have "bare newlines ('\n')".
'\n' should be converted to "\r\n" before storing messages to IMAP's mailbox.
This patch implements the converting function to git-imap-send.

Cc: Erik Faye-Lund <kusmabite@googlemail.com>
Cc: Jakub Narebski <jnareb@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jeff King <peff@peff.net>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
---
 imap-send.c |   25 +++++++++++++++++++++++++
 1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index dcd8a2a..dbc72ca 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1279,6 +1279,30 @@ static int imap_make_flags(int flags, char *buf)
 	return d;
 }
 
+static void lf_to_crlf(struct msg_data *msg)
+{
+	char *new;
+	int i, j, lfnum = 0;
+
+	for (i = 0; i < msg->len; i++) {
+		if (msg->data[i] == '\n')
+			lfnum++;
+	}
+	new = xcalloc(msg->len + lfnum, sizeof(char));
+	for (i = 0, j = 0; i < msg->len; i++) {
+		if (msg->data[i] != '\n') {
+			new[j++] = msg->data[i];
+			continue;
+		}
+		new[j++] = '\r';
+		new[j++] = '\n';
+	}
+	msg->len += lfnum;
+	free(msg->data);
+	msg->data = new;
+	msg->crlf = 1;
+}
+
 static int imap_store_msg(struct store *gctx, struct msg_data *data)
 {
 	struct imap_store *ctx = (struct imap_store *)gctx;
@@ -1288,6 +1312,7 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data)
 	int ret, d;
 	char flagstr[128];
 
+	lf_to_crlf(data);
 	memset(&cb, 0, sizeof(cb));
 
 	cb.dlen = data->len;
-- 
1.6.5.2


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH 3/4] git-imap-send: Implement CRAM-MD5 auth method
  2010-02-09 14:22       ` Erik Faye-Lund
@ 2010-02-11 14:55         ` Hitoshi Mitake
  2010-02-11 14:59           ` Erik Faye-Lund
  0 siblings, 1 reply; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-11 14:55 UTC (permalink / raw)
  To: kusmabite; +Cc: Erik Faye-Lund, gitster, git, Jeremy White, Robert Shearman

(2010年02月09日 23:22), Erik Faye-Lund wrote:
> On Tue, Feb 9, 2010 at 1:09 PM, Hitoshi Mitake
> <mitake@dcl.info.waseda.ac.jp>  wrote:
>> +static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char *prompt)
>> +{
>> +       int ret;
>> +       char digest[DIGEST_HEX_LEN];
>> +       char buf[256], base64_out[256];
>> +
>> +       memset(buf, 0, 256);
>> +       base64_decode(buf, prompt, strlen(prompt));
>> +
>> +       memset(digest, 0, DIGEST_HEX_LEN);
>> +       md5_hex_hmac(digest, (const unsigned char *)buf, strlen(buf),
>> +                    (const unsigned char *)server.pass, strlen(server.pass));
>> +
>> +       memset(buf, 0, 256);
>> +       strcpy(buf, server.user);
>> +       strcpy(buf + strlen(buf), " ");
>> +       strcpy(buf + strlen(buf), digest);
>> +       memset(base64_out, 0, 256);
>> +       base64_encode(base64_out, buf, strlen(buf));
>> +
>> +       ret = socket_write(&ctx->imap->buf.sock, base64_out, strlen(base64_out));
>
> Since this is the only location in this function that accesses
> anything inside ctx, how about just passing the imap_socket itself to
> the function? That'd make it a bit simpler if, say, I was rewriting
> send-email in C and wanted to add CRAM-MD5 AUTH support (given that
> I'd done the work to use imap_socket first)...
>

Do you mean that
  auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char 
*prompt)
should be,
  auth_cram_md5(struct imap_socket *socket, struct imap_cmd *cmd, const 
char *prompt)
  ?

If this improves portability of cram-md5 auth, of course I agree.
But struct imap_socket is defined in imap-send.c yet.

If you want to separate imap-send.c and cram-md5 auth for 
git-send-email, I'll cooperate :)

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 1/2] git-imap-send: Add CRAM-MD5 authenticate method  support
  2010-02-11 14:38       ` [PATCH v2 1/2] git-imap-send: Add CRAM-MD5 authenticate method support Hitoshi Mitake
@ 2010-02-11 14:55         ` Erik Faye-Lund
  2010-02-11 14:59           ` Hitoshi Mitake
  2010-02-11 15:06           ` [PATCH v3 " Hitoshi Mitake
  0 siblings, 2 replies; 49+ messages in thread
From: Erik Faye-Lund @ 2010-02-11 14:55 UTC (permalink / raw)
  To: Hitoshi Mitake; +Cc: gitster, git, Jakub Narebski, Linus Torvalds, Jeff King

On Thu, Feb 11, 2010 at 3:38 PM, Hitoshi Mitake
<mitake@dcl.info.waseda.ac.jp> wrote:
> @@ -1101,6 +1190,7 @@ static struct 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;
> @@ -1258,6 +1375,7 @@ static int read_message(FILE *f, struct msg_data *msg)
>
>        msg->len  = buf.len;
>        msg->data = strbuf_detach(&buf, NULL);
> +
>        return msg->len;
>  }
>
> @@ -1307,21 +1425,10 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
>
>        msg->data = xmemdupz(data, msg->len);
>        *ofs += msg->len;
> +
>        return 1;
>  }
>

There's not much point in having three hunks with a single added
newline in each...


-- 
Erik "kusma" Faye-Lund

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH 3/4] git-imap-send: Implement CRAM-MD5 auth method
  2010-02-11 14:55         ` Hitoshi Mitake
@ 2010-02-11 14:59           ` Erik Faye-Lund
  2010-02-11 15:11             ` Hitoshi Mitake
  0 siblings, 1 reply; 49+ messages in thread
From: Erik Faye-Lund @ 2010-02-11 14:59 UTC (permalink / raw)
  To: Hitoshi Mitake; +Cc: gitster, git, Jeremy White, Robert Shearman

On Thu, Feb 11, 2010 at 3:55 PM, Hitoshi Mitake
<mitake@dcl.info.waseda.ac.jp> wrote:
> (2010年02月09日 23:22), Erik Faye-Lund wrote:
>>
>> On Tue, Feb 9, 2010 at 1:09 PM, Hitoshi Mitake
>> <mitake@dcl.info.waseda.ac.jp>  wrote:
>>>
>>> +static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd,
>>> const char *prompt)
>>> +{
>>> +       int ret;
>>> +       char digest[DIGEST_HEX_LEN];
>>> +       char buf[256], base64_out[256];
>>> +
>>> +       memset(buf, 0, 256);
>>> +       base64_decode(buf, prompt, strlen(prompt));
>>> +
>>> +       memset(digest, 0, DIGEST_HEX_LEN);
>>> +       md5_hex_hmac(digest, (const unsigned char *)buf, strlen(buf),
>>> +                    (const unsigned char *)server.pass,
>>> strlen(server.pass));
>>> +
>>> +       memset(buf, 0, 256);
>>> +       strcpy(buf, server.user);
>>> +       strcpy(buf + strlen(buf), " ");
>>> +       strcpy(buf + strlen(buf), digest);
>>> +       memset(base64_out, 0, 256);
>>> +       base64_encode(base64_out, buf, strlen(buf));
>>> +
>>> +       ret = socket_write(&ctx->imap->buf.sock, base64_out,
>>> strlen(base64_out));
>>
>> Since this is the only location in this function that accesses
>> anything inside ctx, how about just passing the imap_socket itself to
>> the function? That'd make it a bit simpler if, say, I was rewriting
>> send-email in C and wanted to add CRAM-MD5 AUTH support (given that
>> I'd done the work to use imap_socket first)...
>>
>
> Do you mean that
>  auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char
> *prompt)
> should be,
>  auth_cram_md5(struct imap_socket *socket, struct imap_cmd *cmd, const char
> *prompt)
>  ?
>
> If this improves portability of cram-md5 auth, of course I agree.
> But struct imap_socket is defined in imap-send.c yet.
>

Yes, it's what I meant. It's only a minor nit-pick, as some
refactoring would have to be done anyway. But I think it'd be a good
change to only pull in the state needed, but that's my personal
opinion.

> If you want to separate imap-send.c and cram-md5 auth for git-send-email,
> I'll cooperate :)
>

Not at this point, if ever. I'm fine with you not doing anything about
my comment. I was merely thinking out loud... ;)

-- 
Erik "kusma" Faye-Lund

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-11 14:55         ` Erik Faye-Lund
@ 2010-02-11 14:59           ` Hitoshi Mitake
  2010-02-11 15:06           ` [PATCH v3 " Hitoshi Mitake
  1 sibling, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-11 14:59 UTC (permalink / raw)
  To: kusmabite
  Cc: Erik Faye-Lund, gitster, git, Jakub Narebski, Linus Torvalds, Jeff King

(2010年02月11日 23:55), Erik Faye-Lund wrote:
> On Thu, Feb 11, 2010 at 3:38 PM, Hitoshi Mitake
> <mitake@dcl.info.waseda.ac.jp>  wrote:
>> @@ -1101,6 +1190,7 @@ static struct 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;
>> @@ -1258,6 +1375,7 @@ static int read_message(FILE *f, struct msg_data *msg)
>>
>>         msg->len  = buf.len;
>>         msg->data = strbuf_detach(&buf, NULL);
>> +
>>         return msg->len;
>>   }
>>
>> @@ -1307,21 +1425,10 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
>>
>>         msg->data = xmemdupz(data, msg->len);
>>         *ofs += msg->len;
>> +
>>         return 1;
>>   }
>>
>
> There's not much point in having three hunks with a single added
> newline in each...
>
>

grr.. sorry, it is completely my mistake.
I'll send v3 later.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-11 14:55         ` Erik Faye-Lund
  2010-02-11 14:59           ` Hitoshi Mitake
@ 2010-02-11 15:06           ` Hitoshi Mitake
  2010-02-11 20:30             ` Junio C Hamano
  1 sibling, 1 reply; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-11 15:06 UTC (permalink / raw)
  To: gitster
  Cc: git, Hitoshi Mitake, Erik Faye-Lund, Jakub Narebski,
	Linus Torvalds, Jeff King

This patch makes git-imap-send CRAM-MD5 authenticate method ready.
In theory, CRAM-MD5 authenticate method doesn't depend on SSL,
but for easy of maintainance, this patch uses base64 and md5 stuffs of OpenSSL.
So if you want to use CRAM-MD5 authenticate method,
you have to build git-imap-send with OpenSSL library.

v3: Erik's noticed that there were some garbage lines in this patch.
I removed these. And 2/2 wasn't changed, I'm sending 1/2 only.

Cc: Erik Faye-Lund <kusmabite@googlemail.com>
Cc: Jakub Narebski <jnareb@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jeff King <peff@peff.net>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
---
 Documentation/git-imap-send.txt |    4 +
 imap-send.c                     |  141 ++++++++++++++++++++++++++++++++++-----
 2 files changed, 128 insertions(+), 17 deletions(-)

diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index 57db955..6cafbe2 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -71,6 +71,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
 ~~~~~~~~
 
diff --git a/imap-send.c b/imap-send.c
index ba72fa4..caa4e1b 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -25,10 +25,16 @@
 #include "cache.h"
 #include "exec_cmd.h"
 #include "run-command.h"
+
 #ifdef NO_OPENSSL
 typedef void *SSL;
+#else
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
 #endif
 
+static int login;
+
 struct store_conf {
 	char *name;
 	const char *path; /* should this be here? its interpretation is driver-specific */
@@ -140,6 +146,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 {
@@ -214,6 +234,7 @@ enum CAPABILITY {
 	LITERALPLUS,
 	NAMESPACE,
 	STARTTLS,
+	AUTH_CRAM_MD5,
 };
 
 static const char *cap_list[] = {
@@ -222,6 +243,7 @@ static const char *cap_list[] = {
 	"LITERAL+",
 	"NAMESPACE",
 	"STARTTLS",
+	"AUTH=CRAM-MD5",
 };
 
 #define RESP_OK    0
@@ -526,8 +548,9 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
 		get_cmd_result(ctx, NULL);
 
 	bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
-			   "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
-			   cmd->tag, cmd->cmd, cmd->cb.dlen);
+			  "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
+			  cmd->tag, cmd->cmd, cmd->cb.dlen);
+
 	if (Verbose) {
 		if (imap->num_in_progress)
 			printf("(%d in progress) ", imap->num_in_progress);
@@ -949,6 +972,72 @@ static void imap_close_store(struct store *ctx)
 	free(ctx);
 }
 
+/*
+ * hexchar() and cram() functions are
+ * based on ones of isync project ... http://isync.sf.net/
+ * Thanks!
+ */
+static char hexchar(unsigned int b)
+{
+	return b < 10 ? '0' + b : 'a' + (b - 10);
+}
+
+#ifndef NO_OPENSSL
+
+static char *cram(const char *challenge_64, const char *user, const char *pass)
+{
+	int i;
+	HMAC_CTX hmac;
+	char hash[16], hex[33], challenge[256], response[256];
+	char *response_64;
+
+	memset(challenge, 0, 256);
+	EVP_DecodeBlock((unsigned char *)challenge, (unsigned char *)challenge_64, strlen(challenge_64));
+	HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
+	HMAC_Update(&hmac, (unsigned char *)challenge, strlen(challenge));
+	HMAC_Final(&hmac, (unsigned char *)hash, NULL);
+
+	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);
+	}
+
+	memset(response, 0, 256);
+	snprintf(response, 256, "%s %s", user, hex);
+
+	response_64 = calloc(256 , sizeof(char));
+	EVP_EncodeBlock((unsigned char *)response_64, (unsigned char *)response, strlen(response));
+	return response_64;
+}
+
+#else
+
+static char *cram(const char *challenge_64 __used, const char *user __used, const char *pass __used)
+{
+	fprintf(stderr, "If you want to use CRAM-MD5 authenticate method,"
+		"you have to build git-imap-send with OpenSSL library\n");
+	exit(0);
+}
+
+#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)) {
+		fprintf(stderr, "IMAP error: sending response failed\n");
+		return -1;
+	}
+	free(response);
+
+	return 0;
+}
+
 static struct store *imap_open_store(struct imap_server_conf *srvc)
 {
 	struct imap_store *ctx;
@@ -1130,10 +1219,37 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
 		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;
+				}
+				login = 1;
+			} else {
+				fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+				goto bail;
+			}
 		}
+
+		if (!login)
+			if (imap_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+				fprintf(stderr, "IMAP error: LOGIN failed\n");
+				goto bail;
+			}
 	} /* !preauth */
 
 	ctx->prefix = "";
@@ -1310,18 +1426,6 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
 	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)
@@ -1361,6 +1465,9 @@ 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;
 }
 
-- 
1.6.5.2

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH 3/4] git-imap-send: Implement CRAM-MD5 auth method
  2010-02-11 14:59           ` Erik Faye-Lund
@ 2010-02-11 15:11             ` Hitoshi Mitake
  0 siblings, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-11 15:11 UTC (permalink / raw)
  To: kusmabite; +Cc: Erik Faye-Lund, gitster, git, Jeremy White, Robert Shearman

(2010年02月11日 23:59), Erik Faye-Lund wrote:
> On Thu, Feb 11, 2010 at 3:55 PM, Hitoshi Mitake
> <mitake@dcl.info.waseda.ac.jp>  wrote:
>> (2010年02月09日 23:22), Erik Faye-Lund wrote:
>>>
>>> On Tue, Feb 9, 2010 at 1:09 PM, Hitoshi Mitake
>>> <mitake@dcl.info.waseda.ac.jp>    wrote:
>>>>
>>>> +static int auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd,
>>>> const char *prompt)
>>>> +{
>>>> +       int ret;
>>>> +       char digest[DIGEST_HEX_LEN];
>>>> +       char buf[256], base64_out[256];
>>>> +
>>>> +       memset(buf, 0, 256);
>>>> +       base64_decode(buf, prompt, strlen(prompt));
>>>> +
>>>> +       memset(digest, 0, DIGEST_HEX_LEN);
>>>> +       md5_hex_hmac(digest, (const unsigned char *)buf, strlen(buf),
>>>> +                    (const unsigned char *)server.pass,
>>>> strlen(server.pass));
>>>> +
>>>> +       memset(buf, 0, 256);
>>>> +       strcpy(buf, server.user);
>>>> +       strcpy(buf + strlen(buf), " ");
>>>> +       strcpy(buf + strlen(buf), digest);
>>>> +       memset(base64_out, 0, 256);
>>>> +       base64_encode(base64_out, buf, strlen(buf));
>>>> +
>>>> +       ret = socket_write(&ctx->imap->buf.sock, base64_out,
>>>> strlen(base64_out));
>>>
>>> Since this is the only location in this function that accesses
>>> anything inside ctx, how about just passing the imap_socket itself to
>>> the function? That'd make it a bit simpler if, say, I was rewriting
>>> send-email in C and wanted to add CRAM-MD5 AUTH support (given that
>>> I'd done the work to use imap_socket first)...
>>>
>>
>> Do you mean that
>>   auth_cram_md5(struct imap_store *ctx, struct imap_cmd *cmd, const char
>> *prompt)
>> should be,
>>   auth_cram_md5(struct imap_socket *socket, struct imap_cmd *cmd, const char
>> *prompt)
>>   ?
>>
>> If this improves portability of cram-md5 auth, of course I agree.
>> But struct imap_socket is defined in imap-send.c yet.
>>
>
> Yes, it's what I meant. It's only a minor nit-pick, as some
> refactoring would have to be done anyway. But I think it'd be a good
> change to only pull in the state needed, but that's my personal
> opinion.

My latest patch doesn't change the point.
Because auth_cram_md5() is a call back, so changing type of it
needs some works...

>
>> If you want to separate imap-send.c and cram-md5 auth for git-send-email,
>> I'll cooperate :)
>>
>
> Not at this point, if ever. I'm fine with you not doing anything about
> my comment. I was merely thinking out loud... ;)
>

I'm looking forward to using your new git-send-email.
I'm also a heavy user of it :)

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-11 15:06           ` [PATCH v3 " Hitoshi Mitake
@ 2010-02-11 20:30             ` Junio C Hamano
  2010-02-12 11:23               ` Hitoshi Mitake
  0 siblings, 1 reply; 49+ messages in thread
From: Junio C Hamano @ 2010-02-11 20:30 UTC (permalink / raw)
  To: Hitoshi Mitake
  Cc: gitster, git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King

Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> writes:

> This patch makes git-imap-send CRAM-MD5 authenticate method ready.
> In theory, CRAM-MD5 authenticate method doesn't depend on SSL,
> but for easy of maintainance, this patch uses base64 and md5 stuffs of OpenSSL.
> So if you want to use CRAM-MD5 authenticate method,
> you have to build git-imap-send with OpenSSL library.

Except for some grammer and length of the third line this description
looks good.  It concisely explains the design decision.

> v3: Erik's noticed that there were some garbage lines in this patch.
> I removed these. And 2/2 wasn't changed, I'm sending 1/2 only.

Please put these two lines below three-dash lines.  People reading "git
log" output 6 months from now won't know nor care what v2 looked like.

> diff --git a/imap-send.c b/imap-send.c
> index ba72fa4..caa4e1b 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -25,10 +25,16 @@
>  #include "cache.h"
>  #include "exec_cmd.h"
>  #include "run-command.h"
> +
>  #ifdef NO_OPENSSL
>  typedef void *SSL;
> +#else
> +#include <openssl/evp.h>
> +#include <openssl/hmac.h>
>  #endif
>  
> +static int login;
> +

Does this variable have a meaning?  login what?

 - "login attempted--if we have failed, the authenticator is wrong---no
   point retrying"?

 - "login attempt succeeded and we are now authenticated"?  "logged_in"
   would be a better name if this is the case.

Something else?

> @@ -526,8 +548,9 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
>  		get_cmd_result(ctx, NULL);
>  
>  	bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
> -			   "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
> -			   cmd->tag, cmd->cmd, cmd->cb.dlen);
> +			  "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
> +			  cmd->tag, cmd->cmd, cmd->cb.dlen);
> +

What did you change here?  Indentation?

> @@ -949,6 +972,72 @@ static void imap_close_store(struct store *ctx)
>  	free(ctx);
>  }
>  
> +/*
> + * hexchar() and cram() functions are
> + * based on ones of isync project ... http://isync.sf.net/
> + * Thanks!
> + */
> +static char hexchar(unsigned int b)
> +{
> +	return b < 10 ? '0' + b : 'a' + (b - 10);
> +}
> +

Do you need the above helper function outside "#ifndef NO_OPENSSL" block?

> +#ifndef NO_OPENSSL
> +
> +static char *cram(const char *challenge_64, const char *user, const char *pass)
> +{
> +	int i;
> +	HMAC_CTX hmac;
> +	char hash[16], hex[33], challenge[256], response[256];
> +	char *response_64;
> +
> +	memset(challenge, 0, 256);
> +	EVP_DecodeBlock((unsigned char *)challenge, (unsigned char *)challenge_64, strlen(challenge_64));

In this codepath, is there anything that guarantees that the decoded
result is short enough to fit in challenge[256]?

> +	HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
> +	HMAC_Update(&hmac, (unsigned char *)challenge, strlen(challenge));
> +	HMAC_Final(&hmac, (unsigned char *)hash, NULL);

Hmph, if challenge needs to be always casted to (unsigned char*), perhaps
is it better declared as such?  (not rhetorical---doing so might need cast
in other calls but I am too lazy to check myself so instead I am asking).

> +	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);
> +	}
> +
> +	memset(response, 0, 256);
> +	snprintf(response, 256, "%s %s", user, hex);

"hex" would be of a limited and known length, but username could be overly
long, no?  Is it Ok to truncate that silently using snprintf while
creating response (not rhetorical---your caller may be barfing on overlong
user name, but I am too lazy to check, so instead I am asking)?

> +	response_64 = calloc(256 , sizeof(char));

Do you need to allocate this, or just have the caller supply a pointer
into an array on its stack as an argument to this function?

> +	EVP_EncodeBlock((unsigned char *)response_64, (unsigned char *)response, strlen(response));

Again, is there anything that guarantees response would fit after encoding
in respose_64 in this codepath?

> +	return response_64;

> +#else
> +
> +static char *cram(const char *challenge_64 __used, const char *user __used, const char *pass __used)

Does everybody understand __used annotation these days, or just gcc?

> +{
> +	fprintf(stderr, "If you want to use CRAM-MD5 authenticate method,"
> +		"you have to build git-imap-send with OpenSSL library\n");
> +	exit(0);

Should this exit with "success"?

> +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)) {
> +		fprintf(stderr, "IMAP error: sending response failed\n");
> +		return -1;

Perhaps 'return error("message fmt without LF at the end", args...);'?

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box
  2010-02-11 14:38       ` [PATCH v2 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box Hitoshi Mitake
@ 2010-02-11 20:48         ` Junio C Hamano
  2010-02-12 11:24           ` Hitoshi Mitake
  0 siblings, 1 reply; 49+ messages in thread
From: Junio C Hamano @ 2010-02-11 20:48 UTC (permalink / raw)
  To: Hitoshi Mitake
  Cc: gitster, git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King

Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> writes:

> According to RFC of IMAP, all messages must not have "bare newlines ('\n')".
> '\n' should be converted to "\r\n" before storing messages to IMAP's mailbox.
> This patch implements the converting function to git-imap-send.
>
> Cc: Erik Faye-Lund <kusmabite@googlemail.com>
> Cc: Jakub Narebski <jnareb@gmail.com>
> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> Cc: Jeff King <peff@peff.org>
> Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
> ---
>  imap-send.c |   25 +++++++++++++++++++++++++
>  1 files changed, 25 insertions(+), 0 deletions(-)
>
> diff --git a/imap-send.c b/imap-send.c
> index dcd8a2a..dbc72ca 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -1279,6 +1279,30 @@ static int imap_make_flags(int flags, char *buf)
>  	return d;
>  }
>  
> +static void lf_to_crlf(struct msg_data *msg)
> +{
> +	char *new;
> +	int i, j, lfnum = 0;
> +
> +	for (i = 0; i < msg->len; i++) {
> +		if (msg->data[i] == '\n')
> +			lfnum++;
> +	}
> +	new = xcalloc(msg->len + lfnum, sizeof(char));
> +	for (i = 0, j = 0; i < msg->len; i++) {
> +		if (msg->data[i] != '\n') {
> +			new[j++] = msg->data[i];
> +			continue;
> +		}
> +		new[j++] = '\r';
> +		new[j++] = '\n';
> +	}
> +	msg->len += lfnum;
> +	free(msg->data);
> +	msg->data = new;
> +	msg->crlf = 1;
> +}

Thanks.

Two questions:

 - "msg->crlf" -- what is it used for?  Do we need to maintain it?

 - Can the incoming payload already be CRLF terminated?  If so, do we want
   to convert it into CRCRLF?

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-11 20:30             ` Junio C Hamano
@ 2010-02-12 11:23               ` Hitoshi Mitake
  0 siblings, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-12 11:23 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King

On 2010年02月12日 05:30, Junio C Hamano wrote:
> Hitoshi Mitake<mitake@dcl.info.waseda.ac.jp>  writes:
>
>> This patch makes git-imap-send CRAM-MD5 authenticate method ready.
>> In theory, CRAM-MD5 authenticate method doesn't depend on SSL,
>> but for easy of maintainance, this patch uses base64 and md5 stuffs of OpenSSL.
>> So if you want to use CRAM-MD5 authenticate method,
>> you have to build git-imap-send with OpenSSL library.
>
> Except for some grammer and length of the third line this description
> looks good.  It concisely explains the design decision.

Thanks, I'll separate and fix the third line.

>
>> v3: Erik's noticed that there were some garbage lines in this patch.
>> I removed these. And 2/2 wasn't changed, I'm sending 1/2 only.
>
> Please put these two lines below three-dash lines.  People reading "git
> log" output 6 months from now won't know nor care what v2 looked like.

Do you mean like this?

    ---
    v3: Erik's noticed that there were some garbage lines in this patch.
    I removed these. And 2/2 wasn't changed, I'm sending 1/2 only.

Sorry, I don't know well about custom of Git.

>
>> diff --git a/imap-send.c b/imap-send.c
>> index ba72fa4..caa4e1b 100644
>> --- a/imap-send.c
>> +++ b/imap-send.c
>> @@ -25,10 +25,16 @@
>>   #include "cache.h"
>>   #include "exec_cmd.h"
>>   #include "run-command.h"
>> +
>>   #ifdef NO_OPENSSL
>>   typedef void *SSL;
>> +#else
>> +#include<openssl/evp.h>
>> +#include<openssl/hmac.h>
>>   #endif
>>
>> +static int login;
>> +
>
> Does this variable have a meaning?  login what?
>
>   - "login attempted--if we have failed, the authenticator is wrong---no
>     point retrying"?
>
>   - "login attempt succeeded and we are now authenticated"?  "logged_in"
>     would be a better name if this is the case.
>
> Something else?

This is remnant of my dirty code. I removed it.

>
>> @@ -526,8 +548,9 @@ static struct imap_cmd *v_issue_imap_cmd(struct imap_store *ctx,
>>   		get_cmd_result(ctx, NULL);
>>
>>   	bufl = nfsnprintf(buf, sizeof(buf), cmd->cb.data ? CAP(LITERALPLUS) ?
>> -			   "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
>> -			   cmd->tag, cmd->cmd, cmd->cb.dlen);
>> +			  "%d %s{%d+}\r\n" : "%d %s{%d}\r\n" : "%d %s\r\n",
>> +			  cmd->tag, cmd->cmd, cmd->cb.dlen);
>> +
>
> What did you change here?  Indentation?

It is accidentally indentation, removed.

>
>> @@ -949,6 +972,72 @@ static void imap_close_store(struct store *ctx)
>>   	free(ctx);
>>   }
>>
>> +/*
>> + * hexchar() and cram() functions are
>> + * based on ones of isync project ... http://isync.sf.net/
>> + * Thanks!
>> + */
>> +static char hexchar(unsigned int b)
>> +{
>> +	return b<  10 ? '0' + b : 'a' + (b - 10);
>> +}
>> +
>
> Do you need the above helper function outside "#ifndef NO_OPENSSL" block?

Clearly not... I moved it to inside of #ifdef ... #endif block.

>
>> +#ifndef NO_OPENSSL
>> +
>> +static char *cram(const char *challenge_64, const char *user, const char *pass)
>> +{
>> +	int i;
>> +	HMAC_CTX hmac;
>> +	char hash[16], hex[33], challenge[256], response[256];
>> +	char *response_64;
>> +
>> +	memset(challenge, 0, 256);
>> +	EVP_DecodeBlock((unsigned char *)challenge, (unsigned char *)challenge_64, strlen(challenge_64));
>
> In this codepath, is there anything that guarantees that the decoded
> result is short enough to fit in challenge[256]?

I was too optimistic, my next patch
caliculate exact size of these buffer.

>
>> +	HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
>> +	HMAC_Update(&hmac, (unsigned char *)challenge, strlen(challenge));
>> +	HMAC_Final(&hmac, (unsigned char *)hash, NULL);
>
> Hmph, if challenge needs to be always casted to (unsigned char*), perhaps
> is it better declared as such?  (not rhetorical---doing so might need cast
> in other calls but I am too lazy to check myself so instead I am asking).

If I declare them as unsigned char *,
then another casting for strlen() required. And these are more than
current casting(current casts:6, calling strlen:7).

>
>> +	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);
>> +	}
>> +
>> +	memset(response, 0, 256);
>> +	snprintf(response, 256, "%s %s", user, hex);
>
> "hex" would be of a limited and known length, but username could be overly
> long, no?  Is it Ok to truncate that silently using snprintf while
> creating response (not rhetorical---your caller may be barfing on overlong
> user name, but I am too lazy to check, so instead I am asking)?
>

Exact calculation of required length of buffer is possible,
I implemented.

>> +	response_64 = calloc(256 , sizeof(char));
>
> Do you need to allocate this, or just have the caller supply a pointer
> into an array on its stack as an argument to this function?

Calculating size of response_64 before calling cram() is possible.
But doing ENCODED_SIZE(strlen(user) + 1 + strlen(hex) + 1) is not
read for readability, I think.
# Please think that ENCODED_SIZE(n) is maximum required buffer size
# encoding n bytes by base64.

>
>> +	EVP_EncodeBlock((unsigned char *)response_64, (unsigned char *)response, strlen(response));
>
> Again, is there anything that guarantees response would fit after encoding
> in respose_64 in this codepath?
>
>> +	return response_64;
>
>> +#else
>> +
>> +static char *cram(const char *challenge_64 __used, const char *user __used, const char *pass __used)
>
> Does everybody understand __used annotation these days, or just gcc?

This was custom of perf of linux kernel, and improper for Git, I removed 
these.

>
>> +{
>> +	fprintf(stderr, "If you want to use CRAM-MD5 authenticate method,"
>> +		"you have to build git-imap-send with OpenSSL library\n");
>> +	exit(0);
>
> Should this exit with "success"?

Cleary not...

>
>> +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)) {
>> +		fprintf(stderr, "IMAP error: sending response failed\n");
>> +		return -1;
>
> Perhaps 'return error("message fmt without LF at the end", args...);'?

I didn't know error(), I'll use it.

Thanks for your detailed review!
I'll send v4 later.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box
  2010-02-11 20:48         ` Junio C Hamano
@ 2010-02-12 11:24           ` Hitoshi Mitake
  0 siblings, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-12 11:24 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King

On 2010年02月12日 05:48, Junio C Hamano wrote:
> Hitoshi Mitake<mitake@dcl.info.waseda.ac.jp>  writes:
>
>> According to RFC of IMAP, all messages must not have "bare newlines ('\n')".
>> '\n' should be converted to "\r\n" before storing messages to IMAP's mailbox.
>> This patch implements the converting function to git-imap-send.
>>
>> Cc: Erik Faye-Lund<kusmabite@googlemail.com>
>> Cc: Jakub Narebski<jnareb@gmail.com>
>> Cc: Linus Torvalds<torvalds@linux-foundation.org>
>> Cc: Jeff King<peff@peff.org>
>> Signed-off-by: Hitoshi Mitake<mitake@dcl.info.waseda.ac.jp>
>> ---
>>   imap-send.c |   25 +++++++++++++++++++++++++
>>   1 files changed, 25 insertions(+), 0 deletions(-)
>>
>> diff --git a/imap-send.c b/imap-send.c
>> index dcd8a2a..dbc72ca 100644
>> --- a/imap-send.c
>> +++ b/imap-send.c
>> @@ -1279,6 +1279,30 @@ static int imap_make_flags(int flags, char *buf)
>>   	return d;
>>   }
>>
>> +static void lf_to_crlf(struct msg_data *msg)
>> +{
>> +	char *new;
>> +	int i, j, lfnum = 0;
>> +
>> +	for (i = 0; i<  msg->len; i++) {
>> +		if (msg->data[i] == '\n')
>> +			lfnum++;
>> +	}
>> +	new = xcalloc(msg->len + lfnum, sizeof(char));
>> +	for (i = 0, j = 0; i<  msg->len; i++) {
>> +		if (msg->data[i] != '\n') {
>> +			new[j++] = msg->data[i];
>> +			continue;
>> +		}
>> +		new[j++] = '\r';
>> +		new[j++] = '\n';
>> +	}
>> +	msg->len += lfnum;
>> +	free(msg->data);
>> +	msg->data = new;
>> +	msg->crlf = 1;
>> +}
>
> Thanks.
>
> Two questions:
>
>   - "msg->crlf" -- what is it used for?  Do we need to maintain it?

This is old legacy from isync, and has no meaning now.
I removed it, thanks.

>
>   - Can the incoming payload already be CRLF terminated?  If so, do we want
>     to convert it into CRCRLF?
>

I didn't thought about the case.
I rewrote lf_to_crlf() for such case.

Thanks for your review.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-09 12:09     ` [PATCH 0/4] Some improvements for git-imap-send Hitoshi Mitake
                         ` (4 preceding siblings ...)
  2010-02-11 14:45       ` [PATCH v2 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box Hitoshi Mitake
@ 2010-02-12 11:36       ` Hitoshi Mitake
  2010-02-12 12:41         ` Erik Faye-Lund
  2010-02-12 21:44         ` Junio C Hamano
  2010-02-12 11:36       ` [PATCH v4 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box Hitoshi Mitake
  6 siblings, 2 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-12 11:36 UTC (permalink / raw)
  To: gitster
  Cc: git, Hitoshi Mitake, Erik Faye-Lund, Jakub Narebski,
	Linus Torvalds, Jeff King

This patch makes git-imap-send CRAM-MD5 authenticate method ready.
In theory, CRAM-MD5 authenticate method doesn't depend on SSL.
But for easiness of maintainance, this patch uses base64 and md5 stuffs of OpenSSL.
So if you want to use CRAM-MD5 authenticate method,
you have to build git-imap-send with OpenSSL library.

---
v3: Erik noticed that there were some garbage lines in this patch.
I removed these. And 2/2 wasn't changed, I'm sending 1/2 only.

v4: Based on Junio's indication, I cleaned up some points of imap-send.c .

Cc: Erik Faye-Lund <kusmabite@googlemail.com>
Cc: Jakub Narebski <jnareb@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jeff King <peff@peff.net>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
---
 Documentation/git-imap-send.txt |    4 +
 imap-send.c                     |  139 ++++++++++++++++++++++++++++++++++----
 2 files changed, 128 insertions(+), 15 deletions(-)

diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index 57db955..6cafbe2 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -71,6 +71,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
 ~~~~~~~~
 
diff --git a/imap-send.c b/imap-send.c
index ba72fa4..c5958a3 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -25,8 +25,12 @@
 #include "cache.h"
 #include "exec_cmd.h"
 #include "run-command.h"
+
 #ifdef NO_OPENSSL
 typedef void *SSL;
+#else
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
 #endif
 
 struct store_conf {
@@ -140,6 +144,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 {
@@ -214,6 +232,7 @@ enum CAPABILITY {
 	LITERALPLUS,
 	NAMESPACE,
 	STARTTLS,
+	AUTH_CRAM_MD5,
 };
 
 static const char *cap_list[] = {
@@ -222,6 +241,7 @@ static const char *cap_list[] = {
 	"LITERAL+",
 	"NAMESPACE",
 	"STARTTLS",
+	"AUTH=CRAM-MD5",
 };
 
 #define RESP_OK    0
@@ -949,6 +969,79 @@ static void imap_close_store(struct store *ctx)
 	free(ctx);
 }
 
+#ifndef NO_OPENSSL
+
+/*
+ * hexchar() and cram() functions are
+ * based on ones of isync project ... http://isync.sf.net/
+ * Thanks!
+ */
+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;
+	HMAC_CTX hmac;
+	unsigned char hash[16];
+	char hex[33];
+	char *challenge, *response;
+	char *response_64;
+
+	/* challenge must be shorter than challenge_64
+	 * because we are decoding base64*/
+	challenge = xcalloc(strlen(challenge_64) + 1, sizeof(char));
+	EVP_DecodeBlock((unsigned char *)challenge, (unsigned char *)challenge_64, strlen(challenge_64));
+	HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
+	HMAC_Update(&hmac, (unsigned char *)challenge, strlen(challenge));
+	HMAC_Final(&hmac, hash, NULL);
+
+	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);
+	}
+
+	/* length: "<user> <digest in hex>" */
+	resp_len = strlen(user) + 1 + strlen(hex) + 1;
+	response = xcalloc(resp_len, sizeof(char));
+	snprintf(response, resp_len, "%s %s", user, hex);
+
+	response_64 = xcalloc(ENCODED_SIZE(resp_len), sizeof(char));
+	EVP_EncodeBlock((unsigned char *)response_64, (unsigned char *)response, strlen(response));
+	return response_64;
+}
+
+#else
+
+static char *cram(const char *challenge_64, const char *user, const char *pass)
+{
+	fprintf(stderr, "If you want to use CRAM-MD5 authenticate method,"
+		"you have to build git-imap-send with OpenSSL library\n");
+	exit(1);
+}
+
+#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;
@@ -1130,9 +1223,34 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
 		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_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+				fprintf(stderr, "IMAP error: LOGIN failed\n");
+				goto bail;
+			}
 		}
 	} /* !preauth */
 
@@ -1310,18 +1428,6 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
 	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)
@@ -1361,6 +1467,9 @@ 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;
 }
 
-- 
1.7.0.rc1.52.gf7cc2.dirty

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v4 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box
  2010-02-09 12:09     ` [PATCH 0/4] Some improvements for git-imap-send Hitoshi Mitake
                         ` (5 preceding siblings ...)
  2010-02-12 11:36       ` [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support Hitoshi Mitake
@ 2010-02-12 11:36       ` Hitoshi Mitake
  6 siblings, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-12 11:36 UTC (permalink / raw)
  To: gitster
  Cc: git, Hitoshi Mitake, Erik Faye-Lund, Jakub Narebski,
	Linus Torvalds, Jeff King

According to RFC of IMAP, all messages must not have "bare newlines ('\n')".
'\n' should be converted to "\r\n" before storing messages to IMAP's mailbox.
This patch implements the converting function to git-imap-send.

---
v4: Based on Junio's indication, now lf_to_crlf() consider
about message with CRLF. And struct msg_data doesn't have
the bitfield member crlf now.

Cc: Erik Faye-Lund <kusmabite@googlemail.com>
Cc: Jakub Narebski <jnareb@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jeff King <peff@peff.net>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
---
 imap-send.c |   40 +++++++++++++++++++++++++++++++++++++++-
 1 files changed, 39 insertions(+), 1 deletions(-)

diff --git a/imap-send.c b/imap-send.c
index c5958a3..624ede3 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -95,7 +95,6 @@ struct msg_data {
 	char *data;
 	int len;
 	unsigned char flags;
-	unsigned int crlf:1;
 };
 
 static const char imap_send_usage[] = "git imap-send < <mbox>";
@@ -1280,6 +1279,44 @@ static int imap_make_flags(int flags, char *buf)
 	return d;
 }
 
+static void lf_to_crlf(struct msg_data *msg)
+{
+	char *new;
+	int i, j, lfnum = 0;
+
+	if (msg->data[0] == '\n')
+		lfnum++;
+	for (i = 1; i < msg->len; i++) {
+		if (msg->data[i - 1] != '\r' && msg->data[i] == '\n')
+			lfnum++;
+	}
+
+	new = xcalloc(msg->len + lfnum, sizeof(char));
+	if (msg->data[0] == '\n') {
+		new[0] = '\r';
+		new[1] = '\n';
+		i = 1;
+		j = 2;
+	} else {
+		new[0] = msg->data[0];
+		i = 1;
+		j = 1;
+	}
+	for ( ; i < msg->len; i++) {
+		if (msg->data[i] != '\n') {
+			new[j++] = msg->data[i];
+			continue;
+		}
+		if (!(msg->data[i - 1] == '\r'))
+			new[j++] = '\r';
+		/* else: This '\n' is already not bare newline*/
+		new[j++] = '\n';
+	}
+	msg->len += lfnum;
+	free(msg->data);
+	msg->data = new;
+}
+
 static int imap_store_msg(struct store *gctx, struct msg_data *data)
 {
 	struct imap_store *ctx = (struct imap_store *)gctx;
@@ -1289,6 +1326,7 @@ static int imap_store_msg(struct store *gctx, struct msg_data *data)
 	int ret, d;
 	char flagstr[128];
 
+	lf_to_crlf(data);
 	memset(&cb, 0, sizeof(cb));
 
 	cb.dlen = data->len;
-- 
1.7.0.rc1.52.gf7cc2.dirty

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method  support
  2010-02-12 11:36       ` [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support Hitoshi Mitake
@ 2010-02-12 12:41         ` Erik Faye-Lund
  2010-02-13  4:21           ` Hitoshi Mitake
  2010-02-12 21:44         ` Junio C Hamano
  1 sibling, 1 reply; 49+ messages in thread
From: Erik Faye-Lund @ 2010-02-12 12:41 UTC (permalink / raw)
  To: Hitoshi Mitake; +Cc: gitster, git, Jakub Narebski, Linus Torvalds, Jeff King

On Fri, Feb 12, 2010 at 12:36 PM, Hitoshi Mitake
<mitake@dcl.info.waseda.ac.jp> wrote:
> +
> +static char *cram(const char *challenge_64, const char *user, const char *pass)
> +{
> +       fprintf(stderr, "If you want to use CRAM-MD5 authenticate method,"
> +               "you have to build git-imap-send with OpenSSL library\n");
> +       exit(1);
> +}
> +

Why not use die() here?

-- 
Erik "kusma" Faye-Lund

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-12 11:36       ` [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support Hitoshi Mitake
  2010-02-12 12:41         ` Erik Faye-Lund
@ 2010-02-12 21:44         ` Junio C Hamano
  2010-02-13  6:49           ` Hitoshi Mitake
  1 sibling, 1 reply; 49+ messages in thread
From: Junio C Hamano @ 2010-02-12 21:44 UTC (permalink / raw)
  To: Hitoshi Mitake
  Cc: gitster, git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King

Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> writes:

> This patch makes git-imap-send CRAM-MD5 authenticate method ready.
> In theory, CRAM-MD5 authenticate method doesn't depend on SSL.
> But for easiness of maintainance, this patch uses base64 and md5 stuffs of OpenSSL.
> So if you want to use CRAM-MD5 authenticate method,
> you have to build git-imap-send with OpenSSL library.
>
> ---
> v3: Erik noticed that there were some garbage lines in this patch.
> I removed these. And 2/2 wasn't changed, I'm sending 1/2 only.
>
> v4: Based on Junio's indication, I cleaned up some points of imap-send.c .
>
> Cc: Erik Faye-Lund <kusmabite@googlemail.com>
> Cc: Jakub Narebski <jnareb@gmail.com>
> Cc: Linus Torvalds <torvalds@linux-foundation.org>
> Cc: Jeff King <peff@peff.net>
> Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
> ---

I actually meant that the comment regarding the history of patch
iterations should go after the three-dash.  Most notably, we want your
S-o-b in the commit log.  That is:

----------------------------------------------------------------
	Subject: well thought out summary of what the patch is about

	Problem description, and rationale to justify the particular
        solution you chose.

        Signed-off-by: Your Name <your@address.example.com>
	---

	 Comments that may help the reviewers while the patch is
         going through the review cycle, but would not be useful
         after this particular version is applied and the commit
         is shown in "git log" output

	 diffstat

         diff --git ... patch text ...
----------------------------------------------------------------

> +
> +	/* challenge must be shorter than challenge_64
> +	 * because we are decoding base64*/

Just a style thing but

	/*
         * We prefer to write our multi-line
         * comments like this.
         */

> +	challenge = xcalloc(strlen(challenge_64) + 1, sizeof(char));

Why not xmalloc()?  Does EVP_DecodeBlock() want a zero-filled buffer?

> +	EVP_DecodeBlock((unsigned char *)challenge, (unsigned char *)challenge_64, strlen(challenge_64));

Does EVP_DecodeBlock diagnose an error in the input and return an error
code?  How are you supposed to protect yourself from the server giving you
a corrupt challenge that does not decode properly?

By the way, this is probably easier to the eye if you split it into two
lines:

	EVP_DecodeBlock((unsigned char *)challenge,
			(unsigned char *)challenge_64, strlen(challenge_64));

> +	HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
> +	HMAC_Update(&hmac, (unsigned char *)challenge, strlen(challenge));
> +	HMAC_Final(&hmac, hash, NULL);

Is there any clean-up necessary after you are done with hmac?  EVP_md5()
returns a pointer to EVP_MD but how and when is that resource released?

By the way, HMAC_Init() seems to be deprecated and kept only for 0.9.6b
compatibility.

    http://www.openssl.org/docs/crypto/hmac.html

> +	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);
> +	}
> +
> +	/* length: "<user> <digest in hex>" */
> +	resp_len = strlen(user) + 1 + strlen(hex) + 1;
> +	response = xcalloc(resp_len, sizeof(char));
> +	snprintf(response, resp_len, "%s %s", user, hex);
> +
> +	response_64 = xcalloc(ENCODED_SIZE(resp_len), sizeof(char));

Why not xmalloc()?  Does EVP_EncodeBlock() want a zero-filled buffer?

> +	EVP_EncodeBlock((unsigned char *)response_64, (unsigned char *)response, strlen(response));

I wouldn't worry too much about error response from this function as I
would for EVP_DecodeBlock() I mentioned earlier.

By the way, I made a couple of small fix-ups to your [2/2] (I think they
were just style and unnecessary use of xcalloc()) and queued.

Thanks.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-12 12:41         ` Erik Faye-Lund
@ 2010-02-13  4:21           ` Hitoshi Mitake
  0 siblings, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-13  4:21 UTC (permalink / raw)
  To: kusmabite
  Cc: Erik Faye-Lund, gitster, git, Jakub Narebski, Linus Torvalds, Jeff King

On 2010年02月12日 21:41, Erik Faye-Lund wrote:
> On Fri, Feb 12, 2010 at 12:36 PM, Hitoshi Mitake
> <mitake@dcl.info.waseda.ac.jp>  wrote:
>> +
>> +static char *cram(const char *challenge_64, const char *user, const char *pass)
>> +{
>> +       fprintf(stderr, "If you want to use CRAM-MD5 authenticate method,"
>> +               "you have to build git-imap-send with OpenSSL library\n");
>> +       exit(1);
>> +}
>> +
>
> Why not use die() here?
>

Ah, die() is the most adequate function for this case, thanks!

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-12 21:44         ` Junio C Hamano
@ 2010-02-13  6:49           ` Hitoshi Mitake
  2010-02-13  6:56             ` [PATCH v5 " Hitoshi Mitake
  2010-02-13  7:42             ` [PATCH v4 " Junio C Hamano
  0 siblings, 2 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-13  6:49 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King

On 2010年02月13日 06:44, Junio C Hamano wrote:
> Hitoshi Mitake<mitake@dcl.info.waseda.ac.jp>  writes:
>
>> This patch makes git-imap-send CRAM-MD5 authenticate method ready.
>> In theory, CRAM-MD5 authenticate method doesn't depend on SSL.
>> But for easiness of maintainance, this patch uses base64 and md5 stuffs of OpenSSL.
>> So if you want to use CRAM-MD5 authenticate method,
>> you have to build git-imap-send with OpenSSL library.
>>
>> ---
>> v3: Erik noticed that there were some garbage lines in this patch.
>> I removed these. And 2/2 wasn't changed, I'm sending 1/2 only.
>>
>> v4: Based on Junio's indication, I cleaned up some points of imap-send.c .
>>
>> Cc: Erik Faye-Lund<kusmabite@googlemail.com>
>> Cc: Jakub Narebski<jnareb@gmail.com>
>> Cc: Linus Torvalds<torvalds@linux-foundation.org>
>> Cc: Jeff King<peff@peff.net>
>> Signed-off-by: Hitoshi Mitake<mitake@dcl.info.waseda.ac.jp>
>> ---
>
> I actually meant that the comment regarding the history of patch
> iterations should go after the three-dash.  Most notably, we want your
> S-o-b in the commit log.  That is:
>
> ----------------------------------------------------------------
> 	Subject: well thought out summary of what the patch is about
>
> 	Problem description, and rationale to justify the particular
>          solution you chose.
>
>          Signed-off-by: Your Name<your@address.example.com>
> 	---
>
> 	 Comments that may help the reviewers while the patch is
>           going through the review cycle, but would not be useful
>           after this particular version is applied and the commit
>           is shown in "git log" output
>
> 	 diffstat
>
>           diff --git ... patch text ...
> ----------------------------------------------------------------
>

I didn't know that Git has such a convenient feature.
I'll move version specific comments to below of three-dash line.

>> +
>> +	/* challenge must be shorter than challenge_64
>> +	 * because we are decoding base64*/
>
> Just a style thing but
>
> 	/*
>           * We prefer to write our multi-line
>           * comments like this.
>           */
>

OK, I'll modify this comment.

>> +	challenge = xcalloc(strlen(challenge_64) + 1, sizeof(char));
>
> Why not xmalloc()?  Does EVP_DecodeBlock() want a zero-filled buffer?
>

Because strlen(challenge_64) is the upper limit of length of challenge.
So tail part of challenge may not be filled by EVP_DecodeBlock(), 
non-zero filled buffer produces not NULL terminated string.
I've confused once by this problem before.

>> +	EVP_DecodeBlock((unsigned char *)challenge, (unsigned char *)challenge_64, strlen(challenge_64));
>
> Does EVP_DecodeBlock diagnose an error in the input and return an error
> code?  How are you supposed to protect yourself from the server giving you
> a corrupt challenge that does not decode properly?
>

I've forgot processing return value from EVP_DecodeBlock().
I'll fix it.

>> +	HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
>> +	HMAC_Update(&hmac, (unsigned char *)challenge, strlen(challenge));
>> +	HMAC_Final(&hmac, hash, NULL);
>
> Is there any clean-up necessary after you are done with hmac?

I've forgot calling HMAC_CTX_cleanup().

> EVP_md5()
> returns a pointer to EVP_MD but how and when is that resource released?

prototype of EVP_md5() is
     const EVP_MD *EVP_md5(void);
EVP_md5() only returns const value. There is no new resource allocation.
e.g. EVP_md() == EVP_md() is true.

>
> By the way, HMAC_Init() seems to be deprecated and kept only for 0.9.6b
> compatibility.
>
>      http://www.openssl.org/docs/crypto/hmac.html

The document of OpenSSL doesn't describe HMAC_init_ex() well.
I can't know that what the parameter ENGINE *impl means...
So I'd like to use HMAC_Init(), if it is for compatibility.

>
>> +	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);
>> +	}
>> +
>> +	/* length: "<user>  <digest in hex>" */
>> +	resp_len = strlen(user) + 1 + strlen(hex) + 1;
>> +	response = xcalloc(resp_len, sizeof(char));
>> +	snprintf(response, resp_len, "%s %s", user, hex);
>> +
>> +	response_64 = xcalloc(ENCODED_SIZE(resp_len), sizeof(char));
>
> Why not xmalloc()?  Does EVP_EncodeBlock() want a zero-filled buffer?
>

The reason using xcalloc() here is like one of above.
ENCODED_SIZE() calculates upper limit of encoded size.

>> +	EVP_EncodeBlock((unsigned char *)response_64, (unsigned char *)response, strlen(response));
>
> I wouldn't worry too much about error response from this function as I
> would for EVP_DecodeBlock() I mentioned earlier.

I'll modify it, too.

>
> By the way, I made a couple of small fix-ups to your [2/2] (I think they
> were just style and unnecessary use of xcalloc()) and queued.

Thanks. Where can I find them?
# But as I mentioned above, use of xcalloc() is necessary.

And again, thanks for your very detailed review!

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v5 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-13  6:49           ` Hitoshi Mitake
@ 2010-02-13  6:56             ` Hitoshi Mitake
  2010-02-13  7:42             ` [PATCH v4 " Junio C Hamano
  1 sibling, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-13  6:56 UTC (permalink / raw)
  To: gitster, gitster
  Cc: Hitoshi Mitake, git, Erik Faye-Lund, Jakub Narebski,
	Linus Torvalds, Jeff King

This patch makes git-imap-send CRAM-MD5 authenticate method ready.
In theory, CRAM-MD5 authenticate method doesn't depend on SSL.
But for easiness of maintainance, this patch uses base64 and md5 stuffs of OpenSSL.
So if you want to use CRAM-MD5 authenticate method,
you have to build git-imap-send with OpenSSL library.

Cc: Erik Faye-Lund <kusmabite@googlemail.com>
Cc: Jakub Narebski <jnareb@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jeff King <peff@peff.net>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
---

v3: Erik noticed that there were some garbage lines in this patch.
I removed these. And 2/2 wasn't changed, I'm sending 1/2 only.

v4: Based on Junio's indication, I cleaned up some points of imap-send.c .

v5: Based on Erik's advice, cram() now uses die() if git-imap-send
is built with NO_OPENSSL.
And based on Junio's indication, some error checking code are added.

 Documentation/git-imap-send.txt |    4 +
 imap-send.c                     |  154 +++++++++++++++++++++++++++++++++++----
 2 files changed, 143 insertions(+), 15 deletions(-)

diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index 57db955..6cafbe2 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -71,6 +71,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
 ~~~~~~~~
 
diff --git a/imap-send.c b/imap-send.c
index ba72fa4..20f18b2 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -25,8 +25,12 @@
 #include "cache.h"
 #include "exec_cmd.h"
 #include "run-command.h"
+
 #ifdef NO_OPENSSL
 typedef void *SSL;
+#else
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
 #endif
 
 struct store_conf {
@@ -140,6 +144,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 {
@@ -214,6 +232,7 @@ enum CAPABILITY {
 	LITERALPLUS,
 	NAMESPACE,
 	STARTTLS,
+	AUTH_CRAM_MD5,
 };
 
 static const char *cap_list[] = {
@@ -222,6 +241,7 @@ static const char *cap_list[] = {
 	"LITERAL+",
 	"NAMESPACE",
 	"STARTTLS",
+	"AUTH=CRAM-MD5",
 };
 
 #define RESP_OK    0
@@ -949,6 +969,94 @@ static void imap_close_store(struct store *ctx)
 	free(ctx);
 }
 
+#ifndef NO_OPENSSL
+
+/*
+ * hexchar() and cram() functions are
+ * based on ones of isync project ... http://isync.sf.net/
+ * Thanks!
+ */
+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, ret, resp_len;
+	HMAC_CTX hmac;
+	unsigned char hash[16];
+	char hex[33];
+	char *challenge, *response;
+	char *response_64;
+
+	/*
+	 * challenge must be shorter than challenge_64
+	 * because we are decoding base64
+	 */
+	challenge = xcalloc(strlen(challenge_64) + 1, sizeof(char));
+	ret = EVP_DecodeBlock((unsigned char *)challenge,
+			      (unsigned char *)challenge_64, strlen(challenge_64));
+	if (ret != strlen(challenge) + 1) {
+		free(challenge);
+		return NULL;
+	}
+	HMAC_Init(&hmac, (unsigned char *)pass, strlen(pass), EVP_md5());
+	HMAC_Update(&hmac, (unsigned char *)challenge, strlen(challenge));
+	HMAC_Final(&hmac, hash, NULL);
+	HMAC_CTX_cleanup(&hmac);
+	free(challenge);
+
+	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);
+	}
+
+	/* length: "<user> <digest in hex>" */
+	resp_len = strlen(user) + 1 + strlen(hex) + 1;
+	response = xcalloc(resp_len, sizeof(char));
+	snprintf(response, resp_len, "%s %s", user, hex);
+
+	response_64 = xcalloc(ENCODED_SIZE(resp_len), sizeof(char));
+	ret = EVP_EncodeBlock((unsigned char *)response_64,
+			      (unsigned char *)response, strlen(response));
+	if (ret != strlen(response_64)) {
+		free(response_64);
+		return NULL;
+	}
+	return 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\n");
+}
+
+#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);
+	if (!response)
+		return error("IMAP error: generating response failed\n");
+
+	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;
@@ -1130,9 +1238,34 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
 		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_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+				fprintf(stderr, "IMAP error: LOGIN failed\n");
+				goto bail;
+			}
 		}
 	} /* !preauth */
 
@@ -1310,18 +1443,6 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
 	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)
@@ -1361,6 +1482,9 @@ 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;
 }
 
-- 
1.7.0.rc1.52.gf7cc2.dirty


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-13  6:49           ` Hitoshi Mitake
  2010-02-13  6:56             ` [PATCH v5 " Hitoshi Mitake
@ 2010-02-13  7:42             ` Junio C Hamano
  2010-02-16  6:34               ` Junio C Hamano
  2010-02-17  8:51               ` Hitoshi Mitake
  1 sibling, 2 replies; 49+ messages in thread
From: Junio C Hamano @ 2010-02-13  7:42 UTC (permalink / raw)
  To: Hitoshi Mitake
  Cc: git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King

Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> writes:

> Because strlen(challenge_64) is the upper limit of length of challenge.
> So tail part of challenge may not be filled by EVP_DecodeBlock(),
> non-zero filled buffer produces not NULL terminated string.
> I've confused once by this problem before.

If you know the length of the decoded thing, then you would just know
how much to hash.  Doesn't the EVP_DecodeBlock() give you that number?
Why do you need a NUL termination to begin with?

Because you pretend as if you do not have the actual length, you run
strlen() instead.  I am not that familiar with the API to EVP_* functions,
but I'd be surprised if it were designed in such a stupid way to force you
to write into a pre-zeroed buffer.

By the way, if you use strlen() on a pre-cleared and overallocated buffer,
doesn't your ENCODED_SIZE(n) have to be one byte longer than what you are
computing?

Looking for EVP_DecodeBlock in http://www.google.com/codesearch seems to
find usage examples of varying quality.  Your favorite isync-0.5 stores
the result in "len", but it entirely ignores it and does the same silly
calloc() and strlen().  Usage example in OpenSSL's own x509spki stores the
return value in spki_len and uses that as the length of the stuff to call
another function, which looks much more reasonable.  From this observation
and a bit of reading of the manual, my understanding is that the function
gives you the number of bytes written in the buffer.

So does EncodeBlock(), I would think.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-13  7:42             ` [PATCH v4 " Junio C Hamano
@ 2010-02-16  6:34               ` Junio C Hamano
  2010-02-17  9:17                 ` Hitoshi Mitake
  2010-02-17  8:51               ` Hitoshi Mitake
  1 sibling, 1 reply; 49+ messages in thread
From: Junio C Hamano @ 2010-02-16  6:34 UTC (permalink / raw)
  To: Hitoshi Mitake
  Cc: git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King

Junio C Hamano <gitster@pobox.com> writes:

> If you know the length of the decoded thing, then you would just know
> how much to hash.  Doesn't the EVP_DecodeBlock() give you that number?
> Why do you need a NUL termination to begin with?
>
> Because you pretend as if you do not have the actual length, you run
> strlen() instead.  I am not that familiar with the API to EVP_* functions,
> but I'd be surprised if it were designed in such a stupid way to force you
> to write into a pre-zeroed buffer.
>
> By the way, if you use strlen() on a pre-cleared and overallocated buffer,
> doesn't your ENCODED_SIZE(n) have to be one byte longer than what you are
> computing?
>
> Looking for EVP_DecodeBlock in http://www.google.com/codesearch seems to
> find usage examples of varying quality.  Your favorite isync-0.5 stores
> the result in "len", but it entirely ignores it and does the same silly
> calloc() and strlen().  Usage example in OpenSSL's own x509spki stores the
> return value in spki_len and uses that as the length of the stuff to call
> another function, which looks much more reasonable.  From this observation
> and a bit of reading of the manual, my understanding is that the function
> gives you the number of bytes written in the buffer.
>
> So does EncodeBlock(), I would think.

So here is a replacement version to see if I remember all the points
raised in the thread.

 * Use of HMAC_Init() is kept, so people with ancient OpenSSL can use it;

 * Lose "overallocate with xcalloc() and use strlen() to see how long the
   result is" pattern; EVP_DecodeBlock() and EVP_EncodeBlock() should be
   returning the length anyway, so use that directly;

 * Comment style fixes;

 * Lose "fprintf() then exit()"; die() instead; and

 * Call HMAC_CTX_cleanup() when done;

I didn't check the validity of the last one at all.  Please see if there
is any funnies.

-- >8 --
Subject: imap-send: support CRAM-MD5 authentication

CRAM-MD5 authentication ought to be independent from SSL, but NO_OPENSSL
build will not support this because the base64 and md5 code are used from
the OpenSSL library in this implementation.

Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

---
 Documentation/git-imap-send.txt |    4 +
 imap-send.c                     |  146 +++++++++++++++++++++++++++++++++++----
 2 files changed, 135 insertions(+), 15 deletions(-)

diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index 57db955..6cafbe2 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -71,6 +71,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
 ~~~~~~~~
 
diff --git a/imap-send.c b/imap-send.c
index ba72fa4..bf1cd73 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -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 {
@@ -140,6 +143,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 {
@@ -214,6 +231,7 @@ enum CAPABILITY {
 	LITERALPLUS,
 	NAMESPACE,
 	STARTTLS,
+	AUTH_CRAM_MD5,
 };
 
 static const char *cap_list[] = {
@@ -222,6 +240,7 @@ static const char *cap_list[] = {
 	"LITERAL+",
 	"NAMESPACE",
 	"STARTTLS",
+	"AUTH=CRAM-MD5",
 };
 
 #define RESP_OK    0
@@ -949,6 +968,87 @@ static void imap_close_store(struct store *ctx)
 	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));
+	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;
@@ -1130,9 +1230,34 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
 		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_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+				fprintf(stderr, "IMAP error: LOGIN failed\n");
+				goto bail;
+			}
 		}
 	} /* !preauth */
 
@@ -1310,18 +1435,6 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
 	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)
@@ -1361,6 +1474,9 @@ 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;
 }
 

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-13  7:42             ` [PATCH v4 " Junio C Hamano
  2010-02-16  6:34               ` Junio C Hamano
@ 2010-02-17  8:51               ` Hitoshi Mitake
  1 sibling, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-17  8:51 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King


Sorry for my slow response...

On 2010年02月13日 16:42, Junio C Hamano wrote:
> Hitoshi Mitake<mitake@dcl.info.waseda.ac.jp>  writes:
>
>> Because strlen(challenge_64) is the upper limit of length of challenge.
>> So tail part of challenge may not be filled by EVP_DecodeBlock(),
>> non-zero filled buffer produces not NULL terminated string.
>> I've confused once by this problem before.
>
> If you know the length of the decoded thing, then you would just know
> how much to hash.  Doesn't the EVP_DecodeBlock() give you that number?
> Why do you need a NUL termination to begin with?
>
> Because you pretend as if you do not have the actual length, you run
> strlen() instead.  I am not that familiar with the API to EVP_* functions,
> but I'd be surprised if it were designed in such a stupid way to force you
> to write into a pre-zeroed buffer.

Sorry, what you say is completely correct.
And base64 is not an ascii specific thing, so strlen() cannot provide
correct information here.
Please forgive my foolish coding...

# I tried to reproduce the problem of not NULL terminated case,
# but I could not. I wonder what that was..

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-16  6:34               ` Junio C Hamano
@ 2010-02-17  9:17                 ` Hitoshi Mitake
  2010-02-17  9:18                   ` [PATCH v6 " Hitoshi Mitake
  2010-02-17 18:31                   ` [PATCH v4 " Junio C Hamano
  0 siblings, 2 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-17  9:17 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King

On 2010年02月16日 15:34, Junio C Hamano wrote:
>
> So here is a replacement version to see if I remember all the points
> raised in the thread.
>
>   * Use of HMAC_Init() is kept, so people with ancient OpenSSL can use it;
>
>   * Lose "overallocate with xcalloc() and use strlen() to see how long the
>     result is" pattern; EVP_DecodeBlock() and EVP_EncodeBlock() should be
>     returning the length anyway, so use that directly;
>
>   * Comment style fixes;
>
>   * Lose "fprintf() then exit()"; die() instead; and
>
>   * Call HMAC_CTX_cleanup() when done;
>
> I didn't check the validity of the last one at all.  Please see if there
> is any funnies.

Thanks for your repairing.

I tested it and found one point have to be repaired,

     response_64 = xmalloc(ENCODED_SIZE(resp_len));
should be,
     response_64 = xmalloc(ENCODED_SIZE(resp_len) + 1);
for '\0'. This was the problem in my previous patch.
I noticed it by your indication, thanks.

And it seems that rest part of this patch doesn't contain problem.

I prepared the patch to repair this point.
I'll send it later. If you like it, please use.

>
> -- >8 --
> Subject: imap-send: support CRAM-MD5 authentication
>
> CRAM-MD5 authentication ought to be independent from SSL, but NO_OPENSSL
> build will not support this because the base64 and md5 code are used from
> the OpenSSL library in this implementation.
>
> Signed-off-by: Hitoshi Mitake<mitake@dcl.info.waseda.ac.jp>
> Signed-off-by: Junio C Hamano<gitster@pobox.com>
>
> ---
>   Documentation/git-imap-send.txt |    4 +
>   imap-send.c                     |  146 +++++++++++++++++++++++++++++++++++----
>   2 files changed, 135 insertions(+), 15 deletions(-)
>
> diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
> index 57db955..6cafbe2 100644
> --- a/Documentation/git-imap-send.txt
> +++ b/Documentation/git-imap-send.txt
> @@ -71,6 +71,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
>   ~~~~~~~~
>
> diff --git a/imap-send.c b/imap-send.c
> index ba72fa4..bf1cd73 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -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 {
> @@ -140,6 +143,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 {
> @@ -214,6 +231,7 @@ enum CAPABILITY {
>   	LITERALPLUS,
>   	NAMESPACE,
>   	STARTTLS,
> +	AUTH_CRAM_MD5,
>   };
>
>   static const char *cap_list[] = {
> @@ -222,6 +240,7 @@ static const char *cap_list[] = {
>   	"LITERAL+",
>   	"NAMESPACE",
>   	"STARTTLS",
> +	"AUTH=CRAM-MD5",
>   };
>
>   #define RESP_OK    0
> @@ -949,6 +968,87 @@ static void imap_close_store(struct store *ctx)
>   	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));
> +	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;
> @@ -1130,9 +1230,34 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
>   		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_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
> +				fprintf(stderr, "IMAP error: LOGIN failed\n");
> +				goto bail;
> +			}
>   		}
>   	} /* !preauth */
>
> @@ -1310,18 +1435,6 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
>   	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)
> @@ -1361,6 +1474,9 @@ 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;
>   }
>
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v6 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-17  9:17                 ` Hitoshi Mitake
@ 2010-02-17  9:18                   ` Hitoshi Mitake
  2010-02-17 18:31                   ` [PATCH v4 " Junio C Hamano
  1 sibling, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-17  9:18 UTC (permalink / raw)
  To: gitster
  Cc: git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King,
	Hitoshi Mitake

From: Junio C Hamano <gitster@pobox.com>

This patch makes git-imap-send CRAM-MD5 authenticate method ready.
CRAM-MD5 authentication ought to be independent from SSL, but NO_OPENSSL
build will not support this because the base64 and md5 code are used from
the OpenSSL library in this implementation.

Cc: Erik Faye-Lund <kusmabite@googlemail.com>
Cc: Jakub Narebski <jnareb@gmail.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Jeff King <peff@peff.net>
Signed-off-by: Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
v6:Cleaning by Junio C Hamano:
 * Use of HMAC_Init() is kept, so people with ancient OpenSSL can use it;

 * Lose "overallocate with xcalloc() and use strlen() to see how long the
   result is" pattern; EVP_DecodeBlock() and EVP_EncodeBlock() should be
   returning the length anyway, so use that directly;

 * Comment style fixes;

 * Lose "fprintf() then exit()"; die() instead; and

 * Call HMAC_CTX_cleanup() when done;

And length for response_64 should not be ENCODED_SIZE(resp_len),
should be ENCODED_SIZE(resp_len) + 1, by Hitoshi Mitake.

 Documentation/git-imap-send.txt |    4 +
 imap-send.c                     |  146 +++++++++++++++++++++++++++++++++++----
 2 files changed, 135 insertions(+), 15 deletions(-)

diff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt
index 57db955..6cafbe2 100644
--- a/Documentation/git-imap-send.txt
+++ b/Documentation/git-imap-send.txt
@@ -71,6 +71,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
 ~~~~~~~~
 
diff --git a/imap-send.c b/imap-send.c
index ba72fa4..3ee25d9 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -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 {
@@ -140,6 +143,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 {
@@ -214,6 +231,7 @@ enum CAPABILITY {
 	LITERALPLUS,
 	NAMESPACE,
 	STARTTLS,
+	AUTH_CRAM_MD5,
 };
 
 static const char *cap_list[] = {
@@ -222,6 +240,7 @@ static const char *cap_list[] = {
 	"LITERAL+",
 	"NAMESPACE",
 	"STARTTLS",
+	"AUTH=CRAM-MD5",
 };
 
 #define RESP_OK    0
@@ -949,6 +968,87 @@ static void imap_close_store(struct store *ctx)
 	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;
@@ -1130,9 +1230,34 @@ static struct store *imap_open_store(struct imap_server_conf *srvc)
 		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_exec(ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass) != RESP_OK) {
+				fprintf(stderr, "IMAP error: LOGIN failed\n");
+				goto bail;
+			}
 		}
 	} /* !preauth */
 
@@ -1310,18 +1435,6 @@ static int split_msg(struct msg_data *all_msgs, struct msg_data *msg, int *ofs)
 	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)
@@ -1361,6 +1474,9 @@ 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;
 }
 
-- 
1.7.0.rc1.52.gf7cc2.dirty

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-17  9:17                 ` Hitoshi Mitake
  2010-02-17  9:18                   ` [PATCH v6 " Hitoshi Mitake
@ 2010-02-17 18:31                   ` Junio C Hamano
  2010-02-18 15:45                     ` Hitoshi Mitake
  1 sibling, 1 reply; 49+ messages in thread
From: Junio C Hamano @ 2010-02-17 18:31 UTC (permalink / raw)
  To: Hitoshi Mitake
  Cc: git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King

Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> writes:

>     response_64 = xmalloc(ENCODED_SIZE(resp_len));
> should be,
>     response_64 = xmalloc(ENCODED_SIZE(resp_len) + 1);

Good catch.  Thanks for looking it over.

This is _your_ contribution (I only made a minor fix-up or two), so I'll
apply this your v6 under your name.

Thanks.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support
  2010-02-17 18:31                   ` [PATCH v4 " Junio C Hamano
@ 2010-02-18 15:45                     ` Hitoshi Mitake
  0 siblings, 0 replies; 49+ messages in thread
From: Hitoshi Mitake @ 2010-02-18 15:45 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Erik Faye-Lund, Jakub Narebski, Linus Torvalds, Jeff King

(2010年02月18日 03:31), Junio C Hamano wrote:
> Hitoshi Mitake<mitake@dcl.info.waseda.ac.jp>  writes:
>
>>      response_64 = xmalloc(ENCODED_SIZE(resp_len));
>> should be,
>>      response_64 = xmalloc(ENCODED_SIZE(resp_len) + 1);
>
> Good catch.  Thanks for looking it over.
>
> This is _your_ contribution (I only made a minor fix-up or two), so I'll
> apply this your v6 under your name.

OK, and thanks a lot for your time and review!

^ permalink raw reply	[flat|nested] 49+ messages in thread

end of thread, other threads:[~2010-02-18 15:45 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-02-06 19:26 imap.preformattedHTML and imap.sslverify Junio C Hamano
2010-02-08 22:31 ` Jeremy White
2010-02-08 23:05   ` Junio C Hamano
2010-02-09 12:09     ` [PATCH 0/4] Some improvements for git-imap-send Hitoshi Mitake
2010-02-09 15:06       ` Jeff King
2010-02-09 15:13         ` Erik Faye-Lund
2010-02-09 16:57           ` Jeff King
2010-02-09 18:37             ` Erik Faye-Lund
2010-02-09 18:54               ` Jeff King
2010-02-11 14:38       ` [PATCH v2 1/2] git-imap-send: Add CRAM-MD5 authenticate method support Hitoshi Mitake
2010-02-11 14:55         ` Erik Faye-Lund
2010-02-11 14:59           ` Hitoshi Mitake
2010-02-11 15:06           ` [PATCH v3 " Hitoshi Mitake
2010-02-11 20:30             ` Junio C Hamano
2010-02-12 11:23               ` Hitoshi Mitake
2010-02-11 14:38       ` [PATCH v2 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box Hitoshi Mitake
2010-02-11 20:48         ` Junio C Hamano
2010-02-12 11:24           ` Hitoshi Mitake
2010-02-11 14:45       ` [PATCH v2 1/2] git-imap-send: Add CRAM-MD5 authenticate method support Hitoshi Mitake
2010-02-11 14:45       ` [PATCH v2 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box Hitoshi Mitake
2010-02-12 11:36       ` [PATCH v4 1/2] git-imap-send: Add CRAM-MD5 authenticate method support Hitoshi Mitake
2010-02-12 12:41         ` Erik Faye-Lund
2010-02-13  4:21           ` Hitoshi Mitake
2010-02-12 21:44         ` Junio C Hamano
2010-02-13  6:49           ` Hitoshi Mitake
2010-02-13  6:56             ` [PATCH v5 " Hitoshi Mitake
2010-02-13  7:42             ` [PATCH v4 " Junio C Hamano
2010-02-16  6:34               ` Junio C Hamano
2010-02-17  9:17                 ` Hitoshi Mitake
2010-02-17  9:18                   ` [PATCH v6 " Hitoshi Mitake
2010-02-17 18:31                   ` [PATCH v4 " Junio C Hamano
2010-02-18 15:45                     ` Hitoshi Mitake
2010-02-17  8:51               ` Hitoshi Mitake
2010-02-12 11:36       ` [PATCH v4 2/2] git-imap-send: Convert LF to CRLF before storing patch to draft box Hitoshi Mitake
2010-02-09 12:09     ` [PATCH 1/4] Add base64 encoder and decoder Hitoshi Mitake
2010-02-09 14:45       ` Erik Faye-Lund
2010-02-11 14:37         ` Hitoshi Mitake
2010-02-09 12:09     ` [PATCH 2/4] Add stuffs for MD5 hash algorithm Hitoshi Mitake
2010-02-09 12:09     ` [PATCH 3/4] git-imap-send: Implement CRAM-MD5 auth method Hitoshi Mitake
2010-02-09 14:22       ` Erik Faye-Lund
2010-02-11 14:55         ` Hitoshi Mitake
2010-02-11 14:59           ` Erik Faye-Lund
2010-02-11 15:11             ` Hitoshi Mitake
2010-02-09 12:09     ` [PATCH 4/4] git-imap-send: Add method to convert from LF to CRLF Hitoshi Mitake
2010-02-09 16:15       ` Jakub Narebski
2010-02-11 14:37         ` Hitoshi Mitake
2010-02-09 17:24       ` Linus Torvalds
2010-02-09 18:20         ` Junio C Hamano
2010-02-11 14:38           ` Hitoshi Mitake

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.