* [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support
@ 2025-05-22 17:27 Aditya Garg
2025-05-22 17:27 ` [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (23 more replies)
0 siblings, 24 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 17:27 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, brian m . carlson, Julian Swagemakers, Shengyu Qu,
Zi Yao
Hi all,
This patch series does 2 things. Firstly it basically makes the imap-send
command usable again since it was broken because of not being able to correctly
parse the config file. The second patch adds support for OAuth2.0 authentication
to git imap-send.
P.S.: I am surprised this thing even exists xD.
Aditya Garg (2):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 48 +++++++++-
imap-send.c | 158 ++++++++++++++++++++++++++++++-
3 files changed, 200 insertions(+), 11 deletions(-)
--
2.49.0
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-22 17:27 ` Aditya Garg
2025-05-22 18:00 ` Eric Sunshine
2025-05-22 18:29 ` Jeff King
2025-05-22 17:27 ` [PATCH 2/2] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (22 subsequent siblings)
23 siblings, 2 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 17:27 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, brian m . carlson, Julian Swagemakers, Shengyu Qu,
Zi Yao
Upon setting up imap-send config file, I encountered the very first bug.
An error showing "no imap store specified" was being displayed on the
terminal. Upon investigating further, in static int git_imap_config,
cfg->folder was being incorrectly set to NULL in case imap.user, imap.pass,
imap.tunnel and imap.authmethod were defined, and the values that these configs
intended to set were not being set at all. Because of this, git imap-send was
basically not usable at all. The bug seems to be there for quite a while, and
has not yet been detected, likely due to better options like git send-email
being available.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH 2/2] imap-send: add support for OAuth2.0 authentication
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-22 17:27 ` [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-05-22 17:27 ` Aditya Garg
2025-05-22 19:45 ` brian m. carlson
2025-05-22 19:49 ` [PATCH v2 0/3] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (21 subsequent siblings)
23 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 17:27 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, brian m . carlson, Julian Swagemakers, Shengyu Qu,
Zi Yao
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 48 +++++++++-
imap-send.c | 150 ++++++++++++++++++++++++++++++-
3 files changed, 196 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..dcb0db5563 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
+ 'XOAUTH2'. If this is not set then 'git imap-send' uses the basic IMAP
+ plaintext LOGIN command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..582a46672b 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,16 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+If you have multi-factor authentication set up on your Gmail account, you can generate
+an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create it.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,14 +120,50 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
+or `XOAUTH2` mechanism in your config. In such a case you will have to use an
+OAuth2.0 access token in place of your password.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
+[NOTE]
+Due to some bugs in libcurl, OAuth2.0 authentication may fail if curl is used
+for IMAP. In case you face the same issue, use `git imap-send` with `--no-curl`
+option.
+
Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..4390001cf8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,66 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +959,20 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+static char *oauthbearer_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("If you want to use OAUTHBEARER authenticate method, "
+ "you have to build git-imap-send with OpenSSL library.");
+}
+
+static char *xoauth2_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("If you want to use XOAUTH2 authenticate method, "
+ "you have to build git-imap-send with OpenSSL library.");
+}
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -913,6 +991,46 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1104,6 +1222,36 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 17:27 ` [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-05-22 18:00 ` Eric Sunshine
2025-05-22 18:04 ` Aditya Garg
` (2 more replies)
2025-05-22 18:29 ` Jeff King
1 sibling, 3 replies; 248+ messages in thread
From: Eric Sunshine @ 2025-05-22 18:00 UTC (permalink / raw)
To: Aditya Garg
Cc: Junio C Hamano, git, brian m . carlson, Julian Swagemakers,
Shengyu Qu, Zi Yao
On Thu, May 22, 2025 at 1:29 PM Aditya Garg <gargaditya08@live.com> wrote:
> Upon setting up imap-send config file, I encountered the very first bug.
> An error showing "no imap store specified" was being displayed on the
> terminal. Upon investigating further, in static int git_imap_config,
> cfg->folder was being incorrectly set to NULL in case imap.user, imap.pass,
> imap.tunnel and imap.authmethod were defined, and the values that these configs
> intended to set were not being set at all. Because of this, git imap-send was
> basically not usable at all. The bug seems to be there for quite a while, and
> has not yet been detected, likely due to better options like git send-email
> being available.
>
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
> ---
> diff --git a/imap-send.c b/imap-send.c
> @@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
> FREE_AND_NULL(cfg->folder);
> return git_config_string(&cfg->folder, var, val);
> } else if (!strcmp("imap.user", var)) {
> - FREE_AND_NULL(cfg->folder);
> + FREE_AND_NULL(cfg->user);
> return git_config_string(&cfg->user, var, val);
> } else if (!strcmp("imap.pass", var)) {
> - FREE_AND_NULL(cfg->folder);
> + FREE_AND_NULL(cfg->pass);
> return git_config_string(&cfg->pass, var, val);
> } else if (!strcmp("imap.tunnel", var)) {
> - FREE_AND_NULL(cfg->folder);
> + FREE_AND_NULL(cfg->tunnel);
> return git_config_string(&cfg->tunnel, var, val);
> } else if (!strcmp("imap.authmethod", var)) {
> - FREE_AND_NULL(cfg->folder);
> + FREE_AND_NULL(cfg->auth_method);
> return git_config_string(&cfg->auth_method, var, val);
Okay, makes sense. It might be worth mentioning in the commit message
that these copy/paste bugs were introduced by 6d1f198f34 (imap-send:
fix leaking memory in `imap_server_conf`, 2024-06-07).
Squinting at the code a bit more, am I correct in thinking that
6d1f198f34 missed a case and that the function is still leaking
`cfg->host` in the "imap.host" conditional? I haven't traced the code
or all the callers, but I wonder if server_fill_credential() in the
same file may also be leaky. In any event, the `cfg->host` and the
possible server_fill_credential() leaks are outside the scope of this
bug-fix patch.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 18:00 ` Eric Sunshine
@ 2025-05-22 18:04 ` Aditya Garg
2025-05-22 18:21 ` Aditya Garg
2025-05-22 19:02 ` Junio C Hamano
2 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 18:04 UTC (permalink / raw)
To: Eric Sunshine
Cc: Junio C Hamano, git, brian m . carlson, Julian Swagemakers,
Shengyu Qu, Zi Yao
On 22-05-2025 11:30 pm, Eric Sunshine wrote:
> On Thu, May 22, 2025 at 1:29 PM Aditya Garg <gargaditya08@live.com> wrote:
>> Upon setting up imap-send config file, I encountered the very first bug.
>> An error showing "no imap store specified" was being displayed on the
>> terminal. Upon investigating further, in static int git_imap_config,
>> cfg->folder was being incorrectly set to NULL in case imap.user, imap.pass,
>> imap.tunnel and imap.authmethod were defined, and the values that these configs
>> intended to set were not being set at all. Because of this, git imap-send was
>> basically not usable at all. The bug seems to be there for quite a while, and
>> has not yet been detected, likely due to better options like git send-email
>> being available.
>>
>> Signed-off-by: Aditya Garg <gargaditya08@live.com>
>> ---
>> diff --git a/imap-send.c b/imap-send.c
>> @@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
>> FREE_AND_NULL(cfg->folder);
>> return git_config_string(&cfg->folder, var, val);
>> } else if (!strcmp("imap.user", var)) {
>> - FREE_AND_NULL(cfg->folder);
>> + FREE_AND_NULL(cfg->user);
>> return git_config_string(&cfg->user, var, val);
>> } else if (!strcmp("imap.pass", var)) {
>> - FREE_AND_NULL(cfg->folder);
>> + FREE_AND_NULL(cfg->pass);
>> return git_config_string(&cfg->pass, var, val);
>> } else if (!strcmp("imap.tunnel", var)) {
>> - FREE_AND_NULL(cfg->folder);
>> + FREE_AND_NULL(cfg->tunnel);
>> return git_config_string(&cfg->tunnel, var, val);
>> } else if (!strcmp("imap.authmethod", var)) {
>> - FREE_AND_NULL(cfg->folder);
>> + FREE_AND_NULL(cfg->auth_method);
>> return git_config_string(&cfg->auth_method, var, val);
>
> Okay, makes sense. It might be worth mentioning in the commit message
> that these copy/paste bugs were introduced by 6d1f198f34 (imap-send:
> fix leaking memory in `imap_server_conf`, 2024-06-07).
Thanks, I was wondering what commit brought this bug in the first place.
> Squinting at the code a bit more, am I correct in thinking that
> 6d1f198f34 missed a case and that the function is still leaking
> `cfg->host` in the "imap.host" conditional? I haven't traced the code
> or all the callers, but I wonder if server_fill_credential() in the
> same file may also be leaky. In any event, the `cfg->host` and the
> possible server_fill_credential() leaks are outside the scope of this
> bug-fix patch.
While they are outside the scope, in case I am able to get them fixed,
I can sent another patch for the same.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 18:00 ` Eric Sunshine
2025-05-22 18:04 ` Aditya Garg
@ 2025-05-22 18:21 ` Aditya Garg
2025-05-22 18:25 ` Eric Sunshine
2025-05-22 19:30 ` Aditya Garg
2025-05-22 19:02 ` Junio C Hamano
2 siblings, 2 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 18:21 UTC (permalink / raw)
To: Eric Sunshine
Cc: Junio C Hamano, git, brian m . carlson, Julian Swagemakers,
Shengyu Qu, Zi Yao
On 22-05-2025 11:30 pm, Eric Sunshine wrote:
> On Thu, May 22, 2025 at 1:29 PM Aditya Garg <gargaditya08@live.com> wrote:
>> Upon setting up imap-send config file, I encountered the very first bug.
>> An error showing "no imap store specified" was being displayed on the
>> terminal. Upon investigating further, in static int git_imap_config,
>> cfg->folder was being incorrectly set to NULL in case imap.user, imap.pass,
>> imap.tunnel and imap.authmethod were defined, and the values that these configs
>> intended to set were not being set at all. Because of this, git imap-send was
>> basically not usable at all. The bug seems to be there for quite a while, and
>> has not yet been detected, likely due to better options like git send-email
>> being available.
>>
>> Signed-off-by: Aditya Garg <gargaditya08@live.com>
>> ---
>> diff --git a/imap-send.c b/imap-send.c
>> @@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
>> FREE_AND_NULL(cfg->folder);
>> return git_config_string(&cfg->folder, var, val);
>> } else if (!strcmp("imap.user", var)) {
>> - FREE_AND_NULL(cfg->folder);
>> + FREE_AND_NULL(cfg->user);
>> return git_config_string(&cfg->user, var, val);
>> } else if (!strcmp("imap.pass", var)) {
>> - FREE_AND_NULL(cfg->folder);
>> + FREE_AND_NULL(cfg->pass);
>> return git_config_string(&cfg->pass, var, val);
>> } else if (!strcmp("imap.tunnel", var)) {
>> - FREE_AND_NULL(cfg->folder);
>> + FREE_AND_NULL(cfg->tunnel);
>> return git_config_string(&cfg->tunnel, var, val);
>> } else if (!strcmp("imap.authmethod", var)) {
>> - FREE_AND_NULL(cfg->folder);
>> + FREE_AND_NULL(cfg->auth_method);
>> return git_config_string(&cfg->auth_method, var, val);
>
> Okay, makes sense. It might be worth mentioning in the commit message
> that these copy/paste bugs were introduced by 6d1f198f34 (imap-send:
> fix leaking memory in `imap_server_conf`, 2024-06-07).
>
> Squinting at the code a bit more, am I correct in thinking that
> 6d1f198f34 missed a case and that the function is still leaking
> `cfg->host` in the "imap.host" conditional? I haven't traced the code
> or all the callers, but I wonder if server_fill_credential() in the
> same file may also be leaky. In any event, the `cfg->host` and the
> possible server_fill_credential() leaks are outside the scope of this
> bug-fix patch.
Not sure about server_fill_credential(), but I think this is also
a potential memory leak
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
char *response;
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response))
+ free(response); // fix for the leak
return error("IMAP error: sending response failed");
free(response);
return 0;
}
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 18:21 ` Aditya Garg
@ 2025-05-22 18:25 ` Eric Sunshine
2025-05-22 18:28 ` Aditya Garg
` (2 more replies)
2025-05-22 19:30 ` Aditya Garg
1 sibling, 3 replies; 248+ messages in thread
From: Eric Sunshine @ 2025-05-22 18:25 UTC (permalink / raw)
To: Aditya Garg
Cc: Junio C Hamano, git, brian m . carlson, Julian Swagemakers,
Shengyu Qu, Zi Yao
On Thu, May 22, 2025 at 2:21 PM Aditya Garg <gargaditya08@live.com> wrote:
> On 22-05-2025 11:30 pm, Eric Sunshine wrote:
> > Squinting at the code a bit more, am I correct in thinking that
> > 6d1f198f34 missed a case and that the function is still leaking
> > `cfg->host` in the "imap.host" conditional? I haven't traced the code
> > or all the callers, but I wonder if server_fill_credential() in the
> > same file may also be leaky. In any event, the `cfg->host` and the
> > possible server_fill_credential() leaks are outside the scope of this
> > bug-fix patch.
>
> Not sure about server_fill_credential(), but I think this is also
> a potential memory leak
Agreed.
> static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
> {
> int ret;
> char *response;
>
> response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
>
> ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
> if (ret != strlen(response))
> + free(response); // fix for the leak
> return error("IMAP error: sending response failed");
>
> free(response);
>
> return 0;
> }
It's subjective, but I would probably fix this a little bit
differently and (to my mind) more simply:
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
free(response);
if (ret != strlen(response))
return error("IMAP error: sending response failed");
return 0;
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 18:25 ` Eric Sunshine
@ 2025-05-22 18:28 ` Aditya Garg
2025-05-22 18:31 ` Jeff King
2025-05-24 16:28 ` Ben Knoble
2 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 18:28 UTC (permalink / raw)
To: Eric Sunshine
Cc: Junio C Hamano, git, brian m . carlson, Julian Swagemakers,
Shengyu Qu, Zi Yao
On 22-05-2025 11:55 pm, Eric Sunshine wrote:
> On Thu, May 22, 2025 at 2:21 PM Aditya Garg <gargaditya08@live.com> wrote:
>> On 22-05-2025 11:30 pm, Eric Sunshine wrote:
>>> Squinting at the code a bit more, am I correct in thinking that
>>> 6d1f198f34 missed a case and that the function is still leaking
>>> `cfg->host` in the "imap.host" conditional? I haven't traced the code
>>> or all the callers, but I wonder if server_fill_credential() in the
>>> same file may also be leaky. In any event, the `cfg->host` and the
>>> possible server_fill_credential() leaks are outside the scope of this
>>> bug-fix patch.
>>
>> Not sure about server_fill_credential(), but I think this is also
>> a potential memory leak
>
> Agreed.
>
>> static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
>> {
>> int ret;
>> char *response;
>>
>> response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
>>
>> ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
>> if (ret != strlen(response))
>> + free(response); // fix for the leak
>> return error("IMAP error: sending response failed");
>>
>> free(response);
>>
>> return 0;
>> }
>
> It's subjective, but I would probably fix this a little bit
> differently and (to my mind) more simply:
>
> response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
>
> ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
> free(response);
> if (ret != strlen(response))
> return error("IMAP error: sending response failed");
> return 0;
Looks equally good. Such minor fixes can be included in this series.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 17:27 ` [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-22 18:00 ` Eric Sunshine
@ 2025-05-22 18:29 ` Jeff King
2025-05-22 18:31 ` Aditya Garg
1 sibling, 1 reply; 248+ messages in thread
From: Jeff King @ 2025-05-22 18:29 UTC (permalink / raw)
To: Aditya Garg
Cc: Junio C Hamano, git, Eric Sunshine, brian m . carlson,
Julian Swagemakers, Shengyu Qu, Zi Yao
On Thu, May 22, 2025 at 05:27:15PM +0000, Aditya Garg wrote:
> Upon setting up imap-send config file, I encountered the very first bug.
> An error showing "no imap store specified" was being displayed on the
> terminal. Upon investigating further, in static int git_imap_config,
> cfg->folder was being incorrectly set to NULL in case imap.user, imap.pass,
> imap.tunnel and imap.authmethod were defined, and the values that these configs
> intended to set were not being set at all.
I read "these configs[...]were not being set at all" as imap.user, etc.
But I think the only thing affected was imap.folder, which was
incorrectly being reset when we saw the other fields (and of course the
leak-fix for those fields was not kicking in correctly).
So:
[imap]
host = example.com
user = foo
folder = INBOX
was fine, but:
[imap]
host = example.com
folder = INBOX
user = foo
was not (we end up with a NULL folder variable).
> Because of this, git imap-send was basically not usable at all. The
> bug seems to be there for quite a while, and has not yet been
> detected, likely due to better options like git send-email being
> available.
I think that probably explains why it was not detected (by users or the
tests). It was dependent on the usage and ordering of particular config
options.
(The patch is still doing the right thing, of course; I'm just trying to
add more context to the commit message).
-Peff
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 18:29 ` Jeff King
@ 2025-05-22 18:31 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 18:31 UTC (permalink / raw)
To: Jeff King
Cc: Junio C Hamano, git, Eric Sunshine, brian m . carlson,
Julian Swagemakers, Shengyu Qu, Zi Yao
On 22-05-2025 11:59 pm, Jeff King wrote:
> On Thu, May 22, 2025 at 05:27:15PM +0000, Aditya Garg wrote:
>
>> Upon setting up imap-send config file, I encountered the very first bug.
>> An error showing "no imap store specified" was being displayed on the
>> terminal. Upon investigating further, in static int git_imap_config,
>> cfg->folder was being incorrectly set to NULL in case imap.user, imap.pass,
>> imap.tunnel and imap.authmethod were defined, and the values that these configs
>> intended to set were not being set at all.
>
> I read "these configs[...]were not being set at all" as imap.user, etc.
> But I think the only thing affected was imap.folder, which was
> incorrectly being reset when we saw the other fields (and of course the
> leak-fix for those fields was not kicking in correctly).
>
> So:
>
> [imap]
> host = example.com
> user = foo
> folder = INBOX
>
> was fine, but:
>
> [imap]
> host = example.com
> folder = INBOX
> user = foo
>
> was not (we end up with a NULL folder variable).
>
>> Because of this, git imap-send was basically not usable at all. The
>> bug seems to be there for quite a while, and has not yet been
>> detected, likely due to better options like git send-email being
>> available.
>
> I think that probably explains why it was not detected (by users or the
> tests). It was dependent on the usage and ordering of particular config
> options.
>
> (The patch is still doing the right thing, of course; I'm just trying to
> add more context to the commit message).
mhm, looks like I need to read the code again.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 18:25 ` Eric Sunshine
2025-05-22 18:28 ` Aditya Garg
@ 2025-05-22 18:31 ` Jeff King
2025-05-22 18:33 ` Eric Sunshine
2025-05-24 16:28 ` Ben Knoble
2 siblings, 1 reply; 248+ messages in thread
From: Jeff King @ 2025-05-22 18:31 UTC (permalink / raw)
To: Eric Sunshine
Cc: Aditya Garg, Junio C Hamano, git, brian m . carlson,
Julian Swagemakers, Shengyu Qu, Zi Yao
On Thu, May 22, 2025 at 02:25:47PM -0400, Eric Sunshine wrote:
> It's subjective, but I would probably fix this a little bit
> differently and (to my mind) more simply:
>
> response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
>
> ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
> free(response);
> if (ret != strlen(response))
> return error("IMAP error: sending response failed");
Doesn't that introduce a use-after-free for response? You'd have to
store the strlen() result in a local variable.
-Peff
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 18:31 ` Jeff King
@ 2025-05-22 18:33 ` Eric Sunshine
0 siblings, 0 replies; 248+ messages in thread
From: Eric Sunshine @ 2025-05-22 18:33 UTC (permalink / raw)
To: Jeff King
Cc: Aditya Garg, Junio C Hamano, git, brian m . carlson,
Julian Swagemakers, Shengyu Qu, Zi Yao
On Thu, May 22, 2025 at 2:31 PM Jeff King <peff@peff.net> wrote:
> On Thu, May 22, 2025 at 02:25:47PM -0400, Eric Sunshine wrote:
> > It's subjective, but I would probably fix this a little bit
> > differently and (to my mind) more simply:
> >
> > response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
> >
> > ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
> > free(response);
> > if (ret != strlen(response))
> > return error("IMAP error: sending response failed");
>
> Doesn't that introduce a use-after-free for response? You'd have to
> store the strlen() result in a local variable.
You're correct, of course. Aditya, ignore my suggestion.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 18:00 ` Eric Sunshine
2025-05-22 18:04 ` Aditya Garg
2025-05-22 18:21 ` Aditya Garg
@ 2025-05-22 19:02 ` Junio C Hamano
2025-05-22 19:04 ` Aditya Garg
2 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-05-22 19:02 UTC (permalink / raw)
To: Eric Sunshine
Cc: Aditya Garg, git, brian m . carlson, Julian Swagemakers,
Shengyu Qu, Zi Yao
Eric Sunshine <sunshine@sunshineco.com> writes:
> Okay, makes sense. It might be worth mentioning in the commit message
> that these copy/paste bugs were introduced by 6d1f198f34 (imap-send:
> fix leaking memory in `imap_server_conf`, 2024-06-07).
Definitely a good thing to note in the message, together with the
subtlety that the bug is dependenty on the order in which these
configuration variables appear in the file.
In any case, this may indicate that the population of those who
tried to use imap-send since mid last year, whether they used to use
it happily before last year or they tried to use it anew, must be
very small, or we would have heard about this obvious gotcha by now.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 19:02 ` Junio C Hamano
@ 2025-05-22 19:04 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 19:04 UTC (permalink / raw)
To: Junio C Hamano, Eric Sunshine
Cc: git, brian m . carlson, Julian Swagemakers, Shengyu Qu, Zi Yao
On 23-05-2025 12:32 am, Junio C Hamano wrote:
> Eric Sunshine <sunshine@sunshineco.com> writes:
>
>> Okay, makes sense. It might be worth mentioning in the commit message
>> that these copy/paste bugs were introduced by 6d1f198f34 (imap-send:
>> fix leaking memory in `imap_server_conf`, 2024-06-07).
>
> Definitely a good thing to note in the message, together with the
> subtlety that the bug is dependenty on the order in which these
> configuration variables appear in the file.
>
> In any case, this may indicate that the population of those who
> tried to use imap-send since mid last year, whether they used to use
> it happily before last year or they tried to use it anew, must be
> very small, or we would have heard about this obvious gotcha by now.
>
I also just managed to use OAuth2.0 with curl, apparently curl has
dedicated API for this.
Will send a v2 with that fix as well.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 18:21 ` Aditya Garg
2025-05-22 18:25 ` Eric Sunshine
@ 2025-05-22 19:30 ` Aditya Garg
2025-05-22 19:32 ` Eric Sunshine
1 sibling, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 19:30 UTC (permalink / raw)
To: Eric Sunshine
Cc: Junio C Hamano, git, brian m . carlson, Julian Swagemakers,
Shengyu Qu, Zi Yao
> Not sure about server_fill_credential(), but I think this is also
> a potential memory leak
>
> static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
> {
> int ret;
> char *response;
>
> response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
>
> ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
> if (ret != strlen(response))
> + free(response); // fix for the leak
> return error("IMAP error: sending response failed");
>
> free(response);
>
> return 0;
> }
So this change leads to:
imap-send.c:990:2: error: code will never be executed [-Werror,-Wunreachable-code]
free(response);
^~~~
I think it's better to leave the other leaks for a different series. You might want to
treat it as a bug report as well.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 19:30 ` Aditya Garg
@ 2025-05-22 19:32 ` Eric Sunshine
2025-05-22 19:40 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Eric Sunshine @ 2025-05-22 19:32 UTC (permalink / raw)
To: Aditya Garg
Cc: Junio C Hamano, git, brian m . carlson, Julian Swagemakers,
Shengyu Qu, Zi Yao
On Thu, May 22, 2025 at 3:30 PM Aditya Garg <gargaditya08@live.com> wrote:
> > Not sure about server_fill_credential(), but I think this is also
> > a potential memory leak
> >
> > static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
> > {
> > int ret;
> > char *response;
> >
> > response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
> >
> > ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
> > if (ret != strlen(response))
> > + free(response); // fix for the leak
> > return error("IMAP error: sending response failed");
> >
> > free(response);
> >
> > return 0;
> > }
>
> So this change leads to:
>
> imap-send.c:990:2: error: code will never be executed [-Werror,-Wunreachable-code]
> free(response);
> ^~~~
Is that because you forgot the curly braces around the `if` body?
if (ret != strlen(response)) {
free(response);
return error("IMAP error: sending response failed");
}
free(response);
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 19:32 ` Eric Sunshine
@ 2025-05-22 19:40 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 19:40 UTC (permalink / raw)
To: Eric Sunshine
Cc: Junio C Hamano, git, brian m . carlson, Julian Swagemakers,
Shengyu Qu, Zi Yao
On 23-05-2025 01:02 am, Eric Sunshine wrote:
> On Thu, May 22, 2025 at 3:30 PM Aditya Garg <gargaditya08@live.com> wrote:
>>> Not sure about server_fill_credential(), but I think this is also
>>> a potential memory leak
>>>
>>> static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
>>> {
>>> int ret;
>>> char *response;
>>>
>>> response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
>>>
>>> ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
>>> if (ret != strlen(response))
>>> + free(response); // fix for the leak
>>> return error("IMAP error: sending response failed");
>>>
>>> free(response);
>>>
>>> return 0;
>>> }
>>
>> So this change leads to:
>>
>> imap-send.c:990:2: error: code will never be executed [-Werror,-Wunreachable-code]
>> free(response);
>> ^~~~
>
> Is that because you forgot the curly braces around the `if` body?
Yes I did forget. Side effects of writing python for a few days ;)
Not working on the port leaks rn though, the logic seems different from the strings.
And again, feel free to treat rest as a bug report.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 2/2] imap-send: add support for OAuth2.0 authentication
2025-05-22 17:27 ` [PATCH 2/2] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-05-22 19:45 ` brian m. carlson
2025-05-22 19:49 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: brian m. carlson @ 2025-05-22 19:45 UTC (permalink / raw)
To: Aditya Garg
Cc: Junio C Hamano, git, Eric Sunshine, Julian Swagemakers,
Shengyu Qu, Zi Yao
[-- Attachment #1: Type: text/plain, Size: 2149 bytes --]
On 2025-05-22 at 17:27:16, Aditya Garg wrote:
> +static char *xoauth2_base64(const char *user, const char *access_token)
> +{
> + int raw_len, b64_len;
> + char *raw, *b64;
> +
> + /* Compose the XOAUTH2 string
> + * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
> + * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
> + */
> + raw_len = strlen(user) + strlen(access_token) + 20;
> + raw = xmallocz(raw_len + 1);
> + snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
> +
> + /* Base64 encode */
> + b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
> + b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
> + free(raw);
> +
> + if (b64_len < 0) {
> + free(b64);
> + return NULL;
> + }
> + return b64;
> +}
> +
> #else
>
> static char *cram(const char *challenge_64 UNUSED,
> @@ -895,6 +959,20 @@ static char *cram(const char *challenge_64 UNUSED,
> "you have to build git-imap-send with OpenSSL library.");
> }
>
> +static char *oauthbearer_base64(const char *user UNUSED,
> + const char *access_token UNUSED)
> +{
> + die("If you want to use OAUTHBEARER authenticate method, "
> + "you have to build git-imap-send with OpenSSL library.");
> +}
I don't think this is a good idea. Linux distros and other parties who
distribute OpenSSL cannot legally distribute Git linked against it and
we should not require people to use OpenSSL for this. It looks like all
you need here is a base64 encoder and it should be pretty easy to write
such an encoder. There's a minimal decoder as part of decode_b_segent
in mailinfo.c and I'm pretty sure we could just add a suitable encoder
that writes to a strbuf like we have for percent-encoding.
Alternatively, you could just push this into the credential helper with
the new credential helper extensions by adding support for that to git
imap-send and let the helper implement the base64 encoding. That's kind
of the reason I implemented it in the first place.
--
brian m. carlson (they/them)
Toronto, Ontario, CA
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 325 bytes --]
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 2/2] imap-send: add support for OAuth2.0 authentication
2025-05-22 19:45 ` brian m. carlson
@ 2025-05-22 19:49 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 19:49 UTC (permalink / raw)
To: brian m. carlson, Junio C Hamano, git, Eric Sunshine,
Julian Swagemakers, Shengyu Qu, Zi Yao
On 23-05-2025 01:15 am, brian m. carlson wrote:
> On 2025-05-22 at 17:27:16, Aditya Garg wrote:
>> +static char *xoauth2_base64(const char *user, const char *access_token)
>> +{
>> + int raw_len, b64_len;
>> + char *raw, *b64;
>> +
>> + /* Compose the XOAUTH2 string
>> + * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
>> + * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
>> + */
>> + raw_len = strlen(user) + strlen(access_token) + 20;
>> + raw = xmallocz(raw_len + 1);
>> + snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
>> +
>> + /* Base64 encode */
>> + b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
>> + b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
>> + free(raw);
>> +
>> + if (b64_len < 0) {
>> + free(b64);
>> + return NULL;
>> + }
>> + return b64;
>> +}
>> +
>> #else
>>
>> static char *cram(const char *challenge_64 UNUSED,
>> @@ -895,6 +959,20 @@ static char *cram(const char *challenge_64 UNUSED,
>> "you have to build git-imap-send with OpenSSL library.");
>> }
>>
>> +static char *oauthbearer_base64(const char *user UNUSED,
>> + const char *access_token UNUSED)
>> +{
>> + die("If you want to use OAUTHBEARER authenticate method, "
>> + "you have to build git-imap-send with OpenSSL library.");
>> +}
>
> I don't think this is a good idea. Linux distros and other parties who
> distribute OpenSSL cannot legally distribute Git linked against it and
> we should not require people to use OpenSSL for this. It looks like all
> you need here is a base64 encoder and it should be pretty easy to write
> such an encoder. There's a minimal decoder as part of decode_b_segent
> in mailinfo.c and I'm pretty sure we could just add a suitable encoder
> that writes to a strbuf like we have for percent-encoding.
>
> Alternatively, you could just push this into the credential helper with
> the new credential helper extensions by adding support for that to git
> imap-send and let the helper implement the base64 encoding. That's kind
> of the reason I implemented it in the first place.
We can still use curl to use these. Curl has a dedicated API for OAuth2.0.
That is what I am doing with v2.
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v2 0/3] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-22 17:27 ` [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-22 17:27 ` [PATCH 2/2] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-05-22 19:49 ` Aditya Garg
2025-05-22 19:49 ` [PATCH v2 1/3] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (2 more replies)
2025-05-23 3:58 ` [PATCH v3 0/3] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (20 subsequent siblings)
23 siblings, 3 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 19:49 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, brian m . carlson, Julian Swagemakers, Zi Yao,
Jeff King
This patch series does 2 things. Firstly it basically makes the imap-send
command usable again since it was broken because of not being able to correctly
parse the config file. The second patch adds support for OAuth2.0 authentication
to git imap-send.
P.S.: I am surprised this thing even exists xD.
v2: Added support for OAuth2.0 with curl.
Fixed the memory leak in case auth_cram_md5 fails.
Aditya Garg (3):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: fix memory leak in case auth_cram_md5 fails
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 43 ++++++-
imap-send.c | 188 +++++++++++++++++++++++++++++--
3 files changed, 218 insertions(+), 18 deletions(-)
--
2.49.0
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v2 1/3] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 19:49 ` [PATCH v2 0/3] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-22 19:49 ` Aditya Garg
2025-05-22 23:27 ` Junio C Hamano
2025-05-22 19:49 ` [PATCH v2 2/3] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-05-22 19:49 ` [PATCH v2 3/3] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 19:49 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, brian m . carlson, Julian Swagemakers, Zi Yao,
Jeff King
Upon setting up imap-send config file, I encountered the very first bug.
An error showing "no imap store specified" was being displayed on the
terminal. Upon investigating further, in static int git_imap_config,
cfg->folder was being incorrectly set to NULL in case imap.user, imap.pass,
imap.tunnel and imap.authmethod were defined. Because of this, git imap-send
was basically not usable at all. The bug seems to be there for quite a while,
and has not yet been detected, likely due to better options like git send-email
being available.
Fixes: 6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v2 2/3] imap-send: add support for OAuth2.0 authentication
2025-05-22 19:49 ` [PATCH v2 0/3] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-22 19:49 ` [PATCH v2 1/3] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-05-22 19:49 ` Aditya Garg
2025-05-22 19:49 ` [PATCH v2 3/3] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 19:49 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, brian m . carlson, Julian Swagemakers, Zi Yao,
Jeff King
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 43 +++++++-
imap-send.c | 176 +++++++++++++++++++++++++++++--
3 files changed, 211 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..fef6487293 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
+ 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext LOGIN command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..8b73599d5e 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,16 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+If you have multi-factor authentication set up on your Gmail account, you can generate
+an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create it.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +120,33 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
+or `XOAUTH2` mechanism in your config. In such a case you will have to use an
+OAuth2.0 access token in place of your password.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +155,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..7616496cba 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,66 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +959,20 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+static char *oauthbearer_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("If you want to use OAUTHBEARER authenticate method, "
+ "you have to build git-imap-send with OpenSSL library.");
+}
+
+static char *xoauth2_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("If you want to use XOAUTH2 authenticate method, "
+ "you have to build git-imap-send with OpenSSL library.");
+}
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -913,6 +991,46 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1104,6 +1222,36 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1553,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
+ strcmp(srvc->auth_method, "XOAUTH2") ||
+ strcmp(srvc->auth_method, "OAUTHBEARER"))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1575,21 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /* While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v2 3/3] imap-send: fix memory leak in case auth_cram_md5 fails
2025-05-22 19:49 ` [PATCH v2 0/3] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-22 19:49 ` [PATCH v2 1/3] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-22 19:49 ` [PATCH v2 2/3] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-05-22 19:49 ` Aditya Garg
2 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-22 19:49 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, brian m . carlson, Julian Swagemakers, Zi Yao,
Jeff King
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 7616496cba..2afc4fb63f 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -983,8 +983,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v2 1/3] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 19:49 ` [PATCH v2 1/3] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-05-22 23:27 ` Junio C Hamano
0 siblings, 0 replies; 248+ messages in thread
From: Junio C Hamano @ 2025-05-22 23:27 UTC (permalink / raw)
To: Aditya Garg
Cc: git, Eric Sunshine, brian m . carlson, Julian Swagemakers, Zi Yao,
Jeff King
Aditya Garg <gargaditya08@live.com> writes:
> Upon setting up imap-send config file, I encountered the very first bug.
> An error showing "no imap store specified" was being displayed on the
> terminal. Upon investigating further, in static int git_imap_config,
> cfg->folder was being incorrectly set to NULL in case imap.user, imap.pass,
> imap.tunnel and imap.authmethod were defined. Because of this, git imap-send
> was basically not usable at all. The bug seems to be there for quite a while,
> and has not yet been detected, likely due to better options like git send-email
> being available.
>
> Fixes: 6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
This project frowns upon this trailer. One reason is that it may
later turn out that this does not fix ;-) And no, this is to suggest
using "attempts-to-fix" or anything of that sort. Most other
trailers are declaration of facts. "Fixes:" is not.
Also, in the long run, first-person experience of an author is not
all that interesting to "git log" readers. "I did this, I saw that"
is something we try not to use too often.
Rather, flow it in the problem description, perhaps like
6d1f198f (imap-send: fix leaking memory in `imap_server_conf`,
2024-06-07) broken imap-send with mistaken copy-and-paste and
cleared cfg->folder when it should have cleared other members in
the structure the code is about to overwrite. git-imap-send
since Git 2.46.0 is unusable due to this bug.
or something to start the description.
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
> ---
> imap-send.c | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> diff --git a/imap-send.c b/imap-send.c
> index 27dc033c7f..37f94a37e8 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
> FREE_AND_NULL(cfg->folder);
> return git_config_string(&cfg->folder, var, val);
> } else if (!strcmp("imap.user", var)) {
> - FREE_AND_NULL(cfg->folder);
> + FREE_AND_NULL(cfg->user);
> return git_config_string(&cfg->user, var, val);
> } else if (!strcmp("imap.pass", var)) {
> - FREE_AND_NULL(cfg->folder);
> + FREE_AND_NULL(cfg->pass);
> return git_config_string(&cfg->pass, var, val);
> } else if (!strcmp("imap.tunnel", var)) {
> - FREE_AND_NULL(cfg->folder);
> + FREE_AND_NULL(cfg->tunnel);
> return git_config_string(&cfg->tunnel, var, val);
> } else if (!strcmp("imap.authmethod", var)) {
> - FREE_AND_NULL(cfg->folder);
> + FREE_AND_NULL(cfg->auth_method);
> return git_config_string(&cfg->auth_method, var, val);
> } else if (!strcmp("imap.port", var)) {
> cfg->port = git_config_int(var, val, ctx->kvi);
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v3 0/3] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-05-22 19:49 ` [PATCH v2 0/3] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-23 3:58 ` Aditya Garg
2025-05-23 3:58 ` [PATCH v3 1/3] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (2 more replies)
2025-05-23 12:14 ` [PATCH v4 0/4] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (19 subsequent siblings)
23 siblings, 3 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-23 3:58 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King
This patch series does 2 things. Firstly it basically makes the imap-send
command usable again since it was broken because of not being able to correctly
parse the config file. The second patch adds support for OAuth2.0 authentication
to git imap-send.
P.S.: I am surprised this thing even exists xD.
v2: Added support for OAuth2.0 with curl.
Fixed the memory leak in case auth_cram_md5 fails.
v3: Improve wording in first patch
Change misleading message if OAuth2.0 is used without OpenSSL
Aditya Garg (3):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: fix memory leak in case auth_cram_md5 fails
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 46 +++++++-
imap-send.c | 188 +++++++++++++++++++++++++++++--
3 files changed, 221 insertions(+), 18 deletions(-)
--
2.49.0
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v3 1/3] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-23 3:58 ` [PATCH v3 0/3] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-23 3:58 ` Aditya Garg
2025-05-23 3:58 ` [PATCH v3 2/3] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-05-23 3:58 ` [PATCH v3 3/3] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-23 3:58 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
was being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel
and imap.authmethod were defined. Because of this, git imap-send was basically
not usable at all. The bug seems to be there for quite a while, and has not
yet been detected, likely due to better options like git send-email being
available.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v3 2/3] imap-send: add support for OAuth2.0 authentication
2025-05-23 3:58 ` [PATCH v3 0/3] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-23 3:58 ` [PATCH v3 1/3] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-05-23 3:58 ` Aditya Garg
2025-05-23 3:58 ` [PATCH v3 3/3] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-23 3:58 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 46 +++++++-
imap-send.c | 176 +++++++++++++++++++++++++++++--
3 files changed, 214 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..fef6487293 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
+ 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext LOGIN command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..c3a46070ac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,19 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your account password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you can generate
+an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create it.
+If you do not want to enable multi-factor authentication, you can use OAuth2.0
+authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +123,33 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
+or `XOAUTH2` mechanism in your config. In such a case you will have to use an
+OAuth2.0 access token in place of your password.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +158,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..04b507fc14 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,66 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +959,20 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+static char *oauthbearer_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use OAUTHBEARER authenticate method "
+ "with OpenSSL library, but it's support has not been compiled in.");
+}
+
+static char *xoauth2_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use XOAUTH2 authenticate method "
+ "with OpenSSL library, but it's support has not been compiled in.");
+}
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -913,6 +991,46 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1104,6 +1222,36 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1553,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
+ strcmp(srvc->auth_method, "XOAUTH2") ||
+ strcmp(srvc->auth_method, "OAUTHBEARER"))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1575,21 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /* While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v3 3/3] imap-send: fix memory leak in case auth_cram_md5 fails
2025-05-23 3:58 ` [PATCH v3 0/3] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-23 3:58 ` [PATCH v3 1/3] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-23 3:58 ` [PATCH v3 2/3] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-05-23 3:58 ` Aditya Garg
2 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-23 3:58 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 04b507fc14..e19dc69b7c 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -983,8 +983,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v4 0/4] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-05-23 3:58 ` [PATCH v3 0/3] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-23 12:14 ` Aditya Garg
2025-05-23 12:14 ` [PATCH v4 1/4] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (18 subsequent siblings)
23 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-23 12:14 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King
This patch series does 2 things. Firstly it basically makes the imap-send
command usable again since it was broken because of not being able to correctly
parse the config file. The second patch adds support for OAuth2.0 authentication
to git imap-send.
P.S.: I am surprised this thing even exists xD.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
Aditya Garg (4):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: fix memory leak in case auth_cram_md5 fails
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 46 +++++-
imap-send.c | 268 +++++++++++++++++++++++++++++--
3 files changed, 300 insertions(+), 19 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v4 1/4] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-05-23 12:14 ` [PATCH v4 0/4] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-23 12:14 ` Aditya Garg
2025-05-23 12:14 ` [PATCH v4 2/4] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (17 subsequent siblings)
23 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-23 12:14 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v4 2/4] imap-send: add support for OAuth2.0 authentication
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-05-23 12:14 ` [PATCH v4 1/4] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-05-23 12:14 ` Aditya Garg
2025-05-23 12:14 ` [PATCH v4 3/4] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (16 subsequent siblings)
23 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-23 12:14 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 46 +++++++-
imap-send.c | 176 +++++++++++++++++++++++++++++--
3 files changed, 214 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..fef6487293 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
+ 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext LOGIN command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..c3a46070ac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,19 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your account password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you can generate
+an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create it.
+If you do not want to enable multi-factor authentication, you can use OAuth2.0
+authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +123,33 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
+or `XOAUTH2` mechanism in your config. In such a case you will have to use an
+OAuth2.0 access token in place of your password.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +158,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..04b507fc14 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,66 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +959,20 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+static char *oauthbearer_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use OAUTHBEARER authenticate method "
+ "with OpenSSL library, but it's support has not been compiled in.");
+}
+
+static char *xoauth2_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use XOAUTH2 authenticate method "
+ "with OpenSSL library, but it's support has not been compiled in.");
+}
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -913,6 +991,46 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1104,6 +1222,36 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1553,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
+ strcmp(srvc->auth_method, "XOAUTH2") ||
+ strcmp(srvc->auth_method, "OAUTHBEARER"))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1575,21 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /* While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v4 3/4] imap-send: add PLAIN authentication method to OpenSSL
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-05-23 12:14 ` [PATCH v4 2/4] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-05-23 12:14 ` Aditya Garg
2025-05-23 12:14 ` [PATCH v4 4/4] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (15 subsequent siblings)
23 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-23 12:14 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +-
imap-send.c | 80 +++++++++++++++++++++++++++++++++-
2 files changed, 81 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index fef6487293..24e88228d0 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
- 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are 'PLAIN', 'CRAM-MD5', 'OAUTHBEARER'
+ and 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
plaintext LOGIN command.
diff --git a/imap-send.c b/imap-send.c
index 04b507fc14..ad54aceb28 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,40 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /* Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +987,13 @@ static char *xoauth2_base64(const char *user, const char *access_token)
#else
+static char *plain_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use PLAIN authenticate method "
+ "with OpenSSL library, but it's support has not been compiled in.");
+}
+
static char *cram(const char *challenge_64 UNUSED,
const char *user UNUSED,
const char *pass UNUSED)
@@ -975,6 +1018,26 @@ static char *xoauth2_base64(const char *user UNUSED,
#endif
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1207,7 +1270,22 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (srvc->auth_method) {
struct imap_cmd_cb cb;
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (!CAP(AUTH_PLAIN)) {
+ fprintf(stderr, "You specified "
+ "PLAIN as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* PLAIN */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_plain;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
"CRAM-MD5 as authentication method, "
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v4 4/4] imap-send: fix memory leak in case auth_cram_md5 fails
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-05-23 12:14 ` [PATCH v4 3/4] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-05-23 12:14 ` Aditya Garg
2025-05-25 18:54 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (14 subsequent siblings)
23 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-23 12:14 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index ad54aceb28..87abfd15f3 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1046,8 +1046,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-22 18:25 ` Eric Sunshine
2025-05-22 18:28 ` Aditya Garg
2025-05-22 18:31 ` Jeff King
@ 2025-05-24 16:28 ` Ben Knoble
2025-05-24 16:30 ` Aditya Garg
2025-05-24 16:32 ` Ben Knoble
2 siblings, 2 replies; 248+ messages in thread
From: Ben Knoble @ 2025-05-24 16:28 UTC (permalink / raw)
To: Eric Sunshine
Cc: Aditya Garg, Junio C Hamano, git, brian m . carlson,
Julian Swagemakers, Shengyu Qu, Zi Yao
> Le 22 mai 2025 à 14:26, Eric Sunshine <sunshine@sunshineco.com> a écrit :
>
> ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
> free(response);
> if (ret != strlen(response))
> return error("IMAP error: sending response failed");
> return 0;
Apologies if I missed something , but : strlen _after_ free?
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-24 16:28 ` Ben Knoble
@ 2025-05-24 16:30 ` Aditya Garg
2025-05-24 16:32 ` Ben Knoble
1 sibling, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-24 16:30 UTC (permalink / raw)
To: Ben Knoble, Eric Sunshine
Cc: Junio C Hamano, git, brian m . carlson, Julian Swagemakers,
Shengyu Qu, Zi Yao
On 24-05-2025 09:58 pm, Ben Knoble wrote:
>
>> Le 22 mai 2025 à 14:26, Eric Sunshine <sunshine@sunshineco.com> a écrit :
>>
>> ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
>> free(response);
>> if (ret != strlen(response))
>> return error("IMAP error: sending response failed");
>> return 0;
>
> Apologies if I missed something , but : strlen _after_ free?
It was a mistake, and had been acknowledged already :)
The latest version of this patch series does not have this.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-24 16:28 ` Ben Knoble
2025-05-24 16:30 ` Aditya Garg
@ 2025-05-24 16:32 ` Ben Knoble
1 sibling, 0 replies; 248+ messages in thread
From: Ben Knoble @ 2025-05-24 16:32 UTC (permalink / raw)
To: Eric Sunshine
Cc: Aditya Garg, Junio C Hamano, git, brian m . carlson,
Julian Swagemakers, Shengyu Qu, Zi Yao
> Le 24 mai 2025 à 12:28, Ben Knoble <ben.knoble@gmail.com> a écrit :
>
>
>> Le 22 mai 2025 à 14:26, Eric Sunshine <sunshine@sunshineco.com> a écrit :
>>
>> ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
>> free(response);
>> if (ret != strlen(response))
>> return error("IMAP error: sending response failed");
>> return 0;
>
> Apologies if I missed something , but : strlen _after_ free?
Ah, others caught this already (and I hadn’t finished reading yet). I really need to find a way to queue up my messages and then go back after finishing a thread and edit before sending ;)
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (8 preceding siblings ...)
2025-05-23 12:14 ` [PATCH v4 4/4] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-05-25 18:54 ` Aditya Garg
2025-05-25 18:54 ` [PATCH v5 1/6] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (6 more replies)
2025-05-28 7:38 ` [PATCH v6 " Aditya Garg
` (13 subsequent siblings)
23 siblings, 7 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-25 18:54 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, sandals@crustytoothpaste.net, Julian Swagemakers,
Zi Yao, Jeff King, Ben Knoble
This patch series does the following things:
Firstly it basically makes the imap-send command usable again since it
was broken because of not being able to correctly parse the config file.
Further it adds support for OAuth2.0 and PLAIN authentication to git
imap-send.
Last, it does some minor improvements including adding the ability to
specify the folder using the command line and set a default between
curl and openssl using the config.
P.S.: I am surprised this thing even exists xD.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
Aditya Garg (6):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: enable specifying the folder using the command line
imap-send: enable user to choose between libcurl and openssl using the
config
Documentation/config/imap.adoc | 17 +-
Documentation/git-imap-send.adoc | 55 +++++-
imap-send.c | 277 +++++++++++++++++++++++++++++--
3 files changed, 326 insertions(+), 23 deletions(-)
--
2.43.0
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v5 1/6] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-25 18:54 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-25 18:54 ` Aditya Garg
2025-05-25 18:54 ` [PATCH v5 2/6] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (5 subsequent siblings)
6 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-25 18:54 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, sandals@crustytoothpaste.net, Julian Swagemakers,
Zi Yao, Jeff King, Ben Knoble
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v5 2/6] imap-send: add support for OAuth2.0 authentication
2025-05-25 18:54 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-25 18:54 ` [PATCH v5 1/6] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-05-25 18:54 ` Aditya Garg
2025-05-25 18:54 ` [PATCH v5 3/6] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (4 subsequent siblings)
6 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-25 18:54 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, sandals@crustytoothpaste.net, Julian Swagemakers,
Zi Yao, Jeff King, Ben Knoble
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 46 +++++++-
imap-send.c | 176 +++++++++++++++++++++++++++++--
3 files changed, 214 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..fef6487293 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
+ 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext LOGIN command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..c3a46070ac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,19 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your account password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you can generate
+an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create it.
+If you do not want to enable multi-factor authentication, you can use OAuth2.0
+authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +123,33 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
+or `XOAUTH2` mechanism in your config. In such a case you will have to use an
+OAuth2.0 access token in place of your password.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +158,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..04b507fc14 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,66 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +959,20 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+static char *oauthbearer_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use OAUTHBEARER authenticate method "
+ "with OpenSSL library, but it's support has not been compiled in.");
+}
+
+static char *xoauth2_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use XOAUTH2 authenticate method "
+ "with OpenSSL library, but it's support has not been compiled in.");
+}
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -913,6 +991,46 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1104,6 +1222,36 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1553,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
+ strcmp(srvc->auth_method, "XOAUTH2") ||
+ strcmp(srvc->auth_method, "OAUTHBEARER"))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1575,21 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /* While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v5 3/6] imap-send: add PLAIN authentication method to OpenSSL
2025-05-25 18:54 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-25 18:54 ` [PATCH v5 1/6] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-25 18:54 ` [PATCH v5 2/6] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-05-25 18:54 ` Aditya Garg
2025-05-25 18:55 ` [PATCH v5 4/6] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (3 subsequent siblings)
6 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-25 18:54 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, sandals@crustytoothpaste.net, Julian Swagemakers,
Zi Yao, Jeff King, Ben Knoble
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +-
imap-send.c | 80 +++++++++++++++++++++++++++++++++-
2 files changed, 81 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index fef6487293..24e88228d0 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
- 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are 'PLAIN', 'CRAM-MD5', 'OAUTHBEARER'
+ and 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
plaintext LOGIN command.
diff --git a/imap-send.c b/imap-send.c
index 04b507fc14..ad54aceb28 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,40 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /* Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +987,13 @@ static char *xoauth2_base64(const char *user, const char *access_token)
#else
+static char *plain_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use PLAIN authenticate method "
+ "with OpenSSL library, but it's support has not been compiled in.");
+}
+
static char *cram(const char *challenge_64 UNUSED,
const char *user UNUSED,
const char *pass UNUSED)
@@ -975,6 +1018,26 @@ static char *xoauth2_base64(const char *user UNUSED,
#endif
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1207,7 +1270,22 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (srvc->auth_method) {
struct imap_cmd_cb cb;
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (!CAP(AUTH_PLAIN)) {
+ fprintf(stderr, "You specified "
+ "PLAIN as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* PLAIN */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_plain;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
"CRAM-MD5 as authentication method, "
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v5 4/6] imap-send: fix memory leak in case auth_cram_md5 fails
2025-05-25 18:54 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-05-25 18:54 ` [PATCH v5 3/6] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-05-25 18:55 ` Aditya Garg
2025-05-25 18:55 ` [PATCH v5 5/6] imap-send: enable specifying the folder using the command line Aditya Garg
` (2 subsequent siblings)
6 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-25 18:55 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, sandals@crustytoothpaste.net, Julian Swagemakers,
Zi Yao, Jeff King, Ben Knoble
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index ad54aceb28..87abfd15f3 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1046,8 +1046,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v5 5/6] imap-send: enable specifying the folder using the command line
2025-05-25 18:54 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-05-25 18:55 ` [PATCH v5 4/6] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-05-25 18:55 ` Aditya Garg
2025-05-25 18:55 ` [PATCH v5 6/6] imap-send: enable user to choose between libcurl and openssl using the config Aditya Garg
2025-05-25 20:34 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Eric Sunshine
6 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-25 18:55 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, sandals@crustytoothpaste.net, Julian Swagemakers,
Zi Yao, Jeff King, Ben Knoble
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +++--
Documentation/git-imap-send.adoc | 5 +++++
imap-send.c | 7 +++++++
3 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 24e88228d0..829d9e0bac 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,8 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: 'INBOX.Drafts', 'INBOX/Drafts' or
+ '[Gmail]/Drafts'. Required if `--folder` argument is not used. If
+ set and `--folder` is also used, `--folder` will be preferred.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index c3a46070ac..de3613928f 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -37,6 +37,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder <folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder [Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index 87abfd15f3..e062758198 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1762,6 +1764,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v5 6/6] imap-send: enable user to choose between libcurl and openssl using the config
2025-05-25 18:54 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-05-25 18:55 ` [PATCH v5 5/6] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-05-25 18:55 ` Aditya Garg
2025-05-25 20:34 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Eric Sunshine
6 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-25 18:55 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, sandals@crustytoothpaste.net, Julian Swagemakers,
Zi Yao, Jeff King, Ben Knoble
Currently, imap-send allows the user to choose between libcurl and
openssl in case Git is compiled with both libraries only using the
command line, and no option to set a default using the config is
available. Add support for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 7 +++++++
Documentation/git-imap-send.adoc | 4 ++--
imap-send.c | 2 ++
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 829d9e0bac..608c0be7ab 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -25,6 +25,13 @@ imap.port::
Defaults to 143 for imap:// hosts and 993 for imaps:// hosts.
Ignored when imap.tunnel is set.
+imap.usecurl::
+ A boolean to choose whether to use libcurl or not to communicate
+ with the IMAP server.
+ Ignored if Git was built without `USE_CURL_FOR_IMAP_SEND` option
+ or with `NO_OPENSSL` option set.
+ `--[no]-curl` argument will override this option.
+
imap.sslverify::
A boolean to enable/disable verification of the server certificate
used by the SSL/TLS connection. Default is `true`. Ignored when
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index de3613928f..efaa2b774e 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -44,12 +44,12 @@ OPTIONS
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
- into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
+ into it. Ignored if Git was built without the `USE_CURL_FOR_IMAP_SEND`
option set.
--no-curl::
Talk to the IMAP server using git's own IMAP routines instead of
- using libcurl. Ignored if Git was built with the NO_OPENSSL option
+ using libcurl. Ignored if Git was built with the `NO_OPENSSL` option
set.
diff --git a/imap-send.c b/imap-send.c
index e062758198..90819eb856 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1559,6 +1559,8 @@ static int git_imap_config(const char *var, const char *val,
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
+ } else if (!strcmp("imap.usecurl", var)) {
+ use_curl = git_config_bool(var, val);
} else if (!strcmp("imap.host", var)) {
if (!val) {
return config_error_nonbool(var);
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support
2025-05-25 18:54 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-05-25 18:55 ` [PATCH v5 6/6] imap-send: enable user to choose between libcurl and openssl using the config Aditya Garg
@ 2025-05-25 20:34 ` Eric Sunshine
2025-05-26 9:06 ` Aditya Garg
6 siblings, 1 reply; 248+ messages in thread
From: Eric Sunshine @ 2025-05-25 20:34 UTC (permalink / raw)
To: Aditya Garg
Cc: Junio C Hamano, git@vger.kernel.org, sandals@crustytoothpaste.net,
Julian Swagemakers, Zi Yao, Jeff King, Ben Knoble
On Sun, May 25, 2025 at 2:55 PM Aditya Garg <gargaditya08@live.com> wrote:
> v2: - Added support for OAuth2.0 with curl.
> - Fixed the memory leak in case auth_cram_md5 fails.
> v3: - Improve wording in first patch
> - Change misleading message if OAuth2.0 is used without OpenSSL
> v4: - Add PLAIN authentication mechanism for OpenSSL
> - Improved wording in the first patch a bit more
> v5: - Add ability to specify destination folder using the command line
> - Add ability to set a default between curl and openssl using the config
Thanks for describing the changes between versions. Reviewers
appreciate the thoughtfulness.
In addition to describing the changes in prose, you can further assist
reviewers by including a range-diff (see the --range-diff option of
git-format-patch).
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support
2025-05-25 20:34 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Eric Sunshine
@ 2025-05-26 9:06 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-26 9:06 UTC (permalink / raw)
To: Eric Sunshine
Cc: Junio C Hamano, git@vger.kernel.org, sandals@crustytoothpaste.net,
Julian Swagemakers, Zi Yao, Jeff King, Ben Knoble
On 26/05/25 2:04 am, Eric Sunshine wrote:
> On Sun, May 25, 2025 at 2:55 PM Aditya Garg <gargaditya08@live.com> wrote:
>> v2: - Added support for OAuth2.0 with curl.
>> - Fixed the memory leak in case auth_cram_md5 fails.
>> v3: - Improve wording in first patch
>> - Change misleading message if OAuth2.0 is used without OpenSSL
>> v4: - Add PLAIN authentication mechanism for OpenSSL
>> - Improved wording in the first patch a bit more
>> v5: - Add ability to specify destination folder using the command line
>> - Add ability to set a default between curl and openssl using the config
>
> Thanks for describing the changes between versions. Reviewers
> appreciate the thoughtfulness.
>
> In addition to describing the changes in prose, you can further assist
> reviewers by including a range-diff (see the --range-diff option of
> git-format-patch).
Will do in future. Thanks.
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v6 0/6] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (9 preceding siblings ...)
2025-05-25 18:54 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-28 7:38 ` Aditya Garg
2025-05-28 7:38 ` [PATCH v6 1/6] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (5 more replies)
2025-05-28 17:17 ` [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (12 subsequent siblings)
23 siblings, 6 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 7:38 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King,
Ben Knoble
This patch series does the following things:
Firstly it basically makes the imap-send command usable again since it
was broken because of not being able to correctly parse the config file.
Further it adds support for OAuth2.0 and PLAIN authentication to git
imap-send.
Last, it does some minor improvements including adding the ability to
specify the folder using the command line and set a default between
curl and openssl using the config.
P.S.: I am surprised this thing even exists xD.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
Aditya Garg (6):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: enable specifying the folder using the command line
imap-send: enable user to choose between libcurl and openssl using the
config
Documentation/config/imap.adoc | 17 +-
Documentation/git-imap-send.adoc | 65 +++++--
imap-send.c | 279 +++++++++++++++++++++++++++++--
3 files changed, 333 insertions(+), 28 deletions(-)
Range-diff:
-: ---------- > 1: 4757d0305d imap-send: fix bug causing cfg->folder being set to NULL
-: ---------- > 2: f5ad01abc5 imap-send: add support for OAuth2.0 authentication
-: ---------- > 3: e3dc19dc49 imap-send: add PLAIN authentication method to OpenSSL
-: ---------- > 4: 11f7ac1325 imap-send: fix memory leak in case auth_cram_md5 fails
1: 62edbcfc6e ! 5: f6e7a5498e imap-send: enable specifying the folder using the command line
@@ Documentation/config/imap.adoc
Command used to set up a tunnel to the IMAP server through which
## Documentation/git-imap-send.adoc ##
+@@ Documentation/git-imap-send.adoc: git-imap-send - Send a collection of patches from stdin to an IMAP folder
+ SYNOPSIS
+ --------
+ [verse]
+-'git imap-send' [-v] [-q] [--[no-]curl]
++'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+
+
+ DESCRIPTION
+ -----------
+-This command uploads a mailbox generated with 'git format-patch'
++This command uploads a mailbox generated with `git format-patch`
+ into an IMAP drafts folder. This allows patches to be sent as
+ other email is when using mail clients that cannot read mailbox
+ files directly. The command also works with any general mailbox
+-in which emails have the fields "From", "Date", and "Subject" in
++in which emails have the fields 'From', 'Date', and 'Subject' in
+ that order.
+
+ Typical usage is something like:
+
+-git format-patch --signoff --stdout --attach origin | git imap-send
++------
++$ git format-patch --signoff --stdout --attach origin | git imap-send
++------
+
+
+ OPTIONS
@@ Documentation/git-imap-send.adoc: OPTIONS
--quiet::
Be quiet.
+-f <folder>::
-+--folder <folder>::
++--folder=<folder>::
+ Specify the folder in which the emails have to saved.
-+ For example: `--folder [Gmail]/Drafts` or `-f INBOX/Drafts`.
++ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
@@ imap-send.c
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
- static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
++static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
2: 245cc89cca = 6: 4769924781 imap-send: enable user to choose between libcurl and openssl using the config
3: 4b91d3bf89 < -: ---------- fix
--
2.43.0
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v6 1/6] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-28 7:38 ` [PATCH v6 " Aditya Garg
@ 2025-05-28 7:38 ` Aditya Garg
2025-05-28 7:38 ` [PATCH v6 2/6] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (4 subsequent siblings)
5 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 7:38 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King,
Ben Knoble
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v6 2/6] imap-send: add support for OAuth2.0 authentication
2025-05-28 7:38 ` [PATCH v6 " Aditya Garg
2025-05-28 7:38 ` [PATCH v6 1/6] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-05-28 7:38 ` Aditya Garg
2025-05-28 7:38 ` [PATCH v6 3/6] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (3 subsequent siblings)
5 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 7:38 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King,
Ben Knoble
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 46 +++++++-
imap-send.c | 176 +++++++++++++++++++++++++++++--
3 files changed, 214 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..fef6487293 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
+ 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext LOGIN command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..c3a46070ac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,19 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your account password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you can generate
+an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create it.
+If you do not want to enable multi-factor authentication, you can use OAuth2.0
+authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +123,33 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
+or `XOAUTH2` mechanism in your config. In such a case you will have to use an
+OAuth2.0 access token in place of your password.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +158,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..04b507fc14 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,66 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +959,20 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+static char *oauthbearer_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use OAUTHBEARER authenticate method "
+ "with OpenSSL library, but it's support has not been compiled in.");
+}
+
+static char *xoauth2_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use XOAUTH2 authenticate method "
+ "with OpenSSL library, but it's support has not been compiled in.");
+}
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -913,6 +991,46 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1104,6 +1222,36 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1553,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
+ strcmp(srvc->auth_method, "XOAUTH2") ||
+ strcmp(srvc->auth_method, "OAUTHBEARER"))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1575,21 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /* While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v6 3/6] imap-send: add PLAIN authentication method to OpenSSL
2025-05-28 7:38 ` [PATCH v6 " Aditya Garg
2025-05-28 7:38 ` [PATCH v6 1/6] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-28 7:38 ` [PATCH v6 2/6] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-05-28 7:38 ` Aditya Garg
2025-05-28 7:38 ` [PATCH v6 4/6] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (2 subsequent siblings)
5 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 7:38 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King,
Ben Knoble
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +-
imap-send.c | 80 +++++++++++++++++++++++++++++++++-
2 files changed, 81 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index fef6487293..24e88228d0 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
- 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are 'PLAIN', 'CRAM-MD5', 'OAUTHBEARER'
+ and 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
plaintext LOGIN command.
diff --git a/imap-send.c b/imap-send.c
index 04b507fc14..ad54aceb28 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,40 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /* Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +987,13 @@ static char *xoauth2_base64(const char *user, const char *access_token)
#else
+static char *plain_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use PLAIN authenticate method "
+ "with OpenSSL library, but it's support has not been compiled in.");
+}
+
static char *cram(const char *challenge_64 UNUSED,
const char *user UNUSED,
const char *pass UNUSED)
@@ -975,6 +1018,26 @@ static char *xoauth2_base64(const char *user UNUSED,
#endif
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1207,7 +1270,22 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (srvc->auth_method) {
struct imap_cmd_cb cb;
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (!CAP(AUTH_PLAIN)) {
+ fprintf(stderr, "You specified "
+ "PLAIN as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* PLAIN */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_plain;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
"CRAM-MD5 as authentication method, "
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v6 4/6] imap-send: fix memory leak in case auth_cram_md5 fails
2025-05-28 7:38 ` [PATCH v6 " Aditya Garg
` (2 preceding siblings ...)
2025-05-28 7:38 ` [PATCH v6 3/6] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-05-28 7:38 ` Aditya Garg
2025-05-28 7:38 ` [PATCH v6 5/6] imap-send: enable specifying the folder using the command line Aditya Garg
2025-05-28 7:38 ` [PATCH v6 6/6] imap-send: enable user to choose between libcurl and openssl using the config Aditya Garg
5 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 7:38 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King,
Ben Knoble
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index ad54aceb28..87abfd15f3 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1046,8 +1046,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v6 5/6] imap-send: enable specifying the folder using the command line
2025-05-28 7:38 ` [PATCH v6 " Aditya Garg
` (3 preceding siblings ...)
2025-05-28 7:38 ` [PATCH v6 4/6] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-05-28 7:38 ` Aditya Garg
2025-05-28 7:38 ` [PATCH v6 6/6] imap-send: enable user to choose between libcurl and openssl using the config Aditya Garg
5 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 7:38 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King,
Ben Knoble
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 24e88228d0..829d9e0bac 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,8 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: 'INBOX.Drafts', 'INBOX/Drafts' or
+ '[Gmail]/Drafts'. Required if `--folder` argument is not used. If
+ set and `--folder` is also used, `--folder` will be preferred.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index c3a46070ac..a35f278baf 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields 'From', 'Date', and 'Subject' in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index 87abfd15f3..51372e1811 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1762,6 +1764,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v6 6/6] imap-send: enable user to choose between libcurl and openssl using the config
2025-05-28 7:38 ` [PATCH v6 " Aditya Garg
` (4 preceding siblings ...)
2025-05-28 7:38 ` [PATCH v6 5/6] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-05-28 7:38 ` Aditya Garg
5 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 7:38 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, sandals, Julian Swagemakers, Zi Yao, Jeff King,
Ben Knoble
Currently, imap-send allows the user to choose between libcurl and
openssl in case Git is compiled with both libraries only using the
command line, and no option to set a default using the config is
available. Add support for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 7 +++++++
Documentation/git-imap-send.adoc | 4 ++--
imap-send.c | 2 ++
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 829d9e0bac..608c0be7ab 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -25,6 +25,13 @@ imap.port::
Defaults to 143 for imap:// hosts and 993 for imaps:// hosts.
Ignored when imap.tunnel is set.
+imap.usecurl::
+ A boolean to choose whether to use libcurl or not to communicate
+ with the IMAP server.
+ Ignored if Git was built without `USE_CURL_FOR_IMAP_SEND` option
+ or with `NO_OPENSSL` option set.
+ `--[no]-curl` argument will override this option.
+
imap.sslverify::
A boolean to enable/disable verification of the server certificate
used by the SSL/TLS connection. Default is `true`. Ignored when
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index a35f278baf..cbbe534ec2 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -46,12 +46,12 @@ OPTIONS
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
- into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
+ into it. Ignored if Git was built without the `USE_CURL_FOR_IMAP_SEND`
option set.
--no-curl::
Talk to the IMAP server using git's own IMAP routines instead of
- using libcurl. Ignored if Git was built with the NO_OPENSSL option
+ using libcurl. Ignored if Git was built with the `NO_OPENSSL` option
set.
diff --git a/imap-send.c b/imap-send.c
index 51372e1811..18aba005cf 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1559,6 +1559,8 @@ static int git_imap_config(const char *var, const char *val,
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
+ } else if (!strcmp("imap.usecurl", var)) {
+ use_curl = git_config_bool(var, val);
} else if (!strcmp("imap.host", var)) {
if (!val) {
return config_error_nonbool(var);
--
2.43.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (10 preceding siblings ...)
2025-05-28 7:38 ` [PATCH v6 " Aditya Garg
@ 2025-05-28 17:17 ` Aditya Garg
2025-05-28 17:17 ` [PATCH v7 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (8 more replies)
2025-05-29 16:21 ` [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (11 subsequent siblings)
23 siblings, 9 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 17:17 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
This patch series does the following things:
Firstly it basically makes the imap-send command usable again since it
was broken because of not being able to correctly parse the config file.
Further it adds support for OAuth2.0 and PLAIN authentication to git
imap-send.
Last, it does some minor improvements including adding the ability to
specify the folder using the command line and set a default between
curl and openssl using the config.
P.S.: I am surprised this thing even exists xD.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
Aditya Garg (9):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: enable specifying the folder using the command line
imap-send: enable user to choose between libcurl and openssl using the
config
imap-send: fix numerous spelling and grammar mistakes in logs
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
Documentation/config/imap.adoc | 17 +-
Documentation/git-imap-send.adoc | 65 +++++-
imap-send.c | 327 +++++++++++++++++++++++++++----
3 files changed, 358 insertions(+), 51 deletions(-)
Range-diff:
-: ---------- > 1: 4757d0305d imap-send: fix bug causing cfg->folder being set to NULL
1: f5ad01abc5 ! 2: c4e2a5659b imap-send: add support for OAuth2.0 authentication
@@ imap-send.c: static char *cram(const char *challenge_64 UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use OAUTHBEARER authenticate method "
-+ "with OpenSSL library, but it's support has not been compiled in.");
++ "with OpenSSL library, but its support has not been compiled in.");
+}
+
+static char *xoauth2_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use XOAUTH2 authenticate method "
-+ "with OpenSSL library, but it's support has not been compiled in.");
++ "with OpenSSL library, but its support has not been compiled in.");
+}
+
#endif
-: ---------- > 3: af9aa85cab imap-send: add PLAIN authentication method to OpenSSL
3: 11f7ac1325 = 4: 2ca10774db imap-send: fix memory leak in case auth_cram_md5 fails
4: f6e7a5498e = 5: 190bed0bff imap-send: enable specifying the folder using the command line
5: 4769924781 = 6: 469c05321b imap-send: enable user to choose between libcurl and openssl using the config
2: e3dc19dc49 ! 7: 6a839e5f4d imap-send: add PLAIN authentication method to OpenSSL
@@ Metadata
Author: Aditya Garg <gargaditya08@live.com>
## Commit message ##
- imap-send: add PLAIN authentication method to OpenSSL
+ imap-send: fix numerous spelling and grammar mistakes in logs
- The current implementation for PLAIN in imap-send works just fine
- if using curl, but if attempted to use for OpenSSL, it is treated
- as an invalid mechanism. The default implementation for OpenSSL is
- IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
- still used today by many email providers in form of app passwords,
- lets add an implementation that can use AUTH PLAIN if specified.
+ A lot of spelling and grammar mistakes were found in the logs shown to
+ the user while using imap-send. Most of them are lack of a full stop at
+ the end of a sentence and first word of a sentence not being capitalized.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
- ## Documentation/config/imap.adoc ##
-@@ Documentation/config/imap.adoc: imap.authMethod::
- Specify the authentication method for authenticating with the IMAP server.
- If Git was built with the NO_CURL option, or if your curl version is older
- than 7.34.0, or if you're running git-imap-send with the `--no-curl`
-- option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
-- 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
-+ option, the only supported methods are 'PLAIN', 'CRAM-MD5', 'OAUTHBEARER'
-+ and 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
- plaintext LOGIN command.
-
## imap-send.c ##
-@@ imap-send.c: enum CAPABILITY {
- LITERALPLUS,
- NAMESPACE,
- STARTTLS,
-+ AUTH_PLAIN,
- AUTH_CRAM_MD5,
- AUTH_OAUTHBEARER,
- AUTH_XOAUTH2
-@@ imap-send.c: static const char *cap_list[] = {
- "LITERAL+",
- "NAMESPACE",
- "STARTTLS",
-+ "AUTH=PLAIN",
- "AUTH=CRAM-MD5",
- "AUTH=OAUTHBEARER",
- "AUTH=XOAUTH2",
-@@ imap-send.c: static char hexchar(unsigned int b)
+@@ imap-send.c: static int ssl_socket_connect(struct imap_socket *sock UNUSED,
+ const struct imap_server_conf *cfg UNUSED,
+ int use_tls_only UNUSED)
+ {
+- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
++ fprintf(stderr, "SSL requested, but SSL support is not compiled in.\n");
+ return -1;
}
- #define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
-+static char *plain_base64(const char *user, const char *pass)
-+{
-+ int user_len = strlen(user);
-+ int pass_len = strlen(pass);
-+ int raw_len = 1 + user_len + 1 + pass_len;
-+ int b64_len;
-+ char *raw, *b64;
-+
-+ /* Compose the PLAIN string
-+ *
-+ * The username and password are combined to one string and base64 encoded.
-+ * "\0user\0pass"
-+ *
-+ * The method has been described in RFC4616.
-+ *
-+ * https://datatracker.ietf.org/doc/html/rfc4616
-+ */
-+ raw = xmallocz(raw_len);
-+ raw[0] = '\0';
-+ memcpy(raw + 1, user, user_len);
-+ raw[1 + user_len] = '\0';
-+ memcpy(raw + 2 + user_len, pass, pass_len);
-+
-+ b64 = xmallocz(ENCODED_SIZE(raw_len));
-+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
-+ free(raw);
-+
-+ if (b64_len < 0) {
-+ free(b64);
-+ return NULL;
-+ }
-+ return b64;
-+}
-+
- static char *cram(const char *challenge_64, const char *user, const char *pass)
- {
- int i, resp_len, encoded_len, decoded_len;
-@@ imap-send.c: static char *xoauth2_base64(const char *user, const char *access_token)
-
- #else
-
-+static char *plain_base64(const char *user UNUSED,
-+ const char *access_token UNUSED)
-+{
-+ die("You are trying to use PLAIN authenticate method "
-+ "with OpenSSL library, but it's support has not been compiled in.");
-+}
-+
- static char *cram(const char *challenge_64 UNUSED,
- const char *user UNUSED,
- const char *pass UNUSED)
-@@ imap-send.c: static char *xoauth2_base64(const char *user UNUSED,
+@@ imap-send.c: static int verify_hostname(X509 *cert, const char *hostname)
+
+ /* try the common name */
+ if (!(subj = X509_get_subject_name(cert)))
+- return error("cannot get certificate subject");
++ return error("Cannot get certificate subject");
+ if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
+- return error("cannot get certificate common name");
++ return error("Cannot get certificate common name");
+ if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
+ return 0;
+ return error("certificate owner '%s' does not match hostname '%s'",
+@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const char *pass)
+ decoded_len = EVP_DecodeBlock((unsigned char *)challenge,
+ (unsigned char *)challenge_64, encoded_len);
+ if (decoded_len < 0)
+- die("invalid challenge %s", challenge_64);
++ die("Invalid challenge %s", challenge_64);
+ if (!HMAC(EVP_md5(), pass, strlen(pass), (unsigned char *)challenge, decoded_len, hash, NULL))
+ die("HMAC error");
+
+@@ imap-send.c: static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+ ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
+ if (ret != strlen(response)) {
+ free(response);
+- return error("IMAP error: sending response failed");
++ return error("IMAP error: sending CRAM-MD5 response failed");
+ }
+
+ free(response);
+@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
+ tunnel.in = -1;
+ tunnel.out = -1;
+ if (start_command(&tunnel))
+- die("cannot start proxy %s", srvc->tunnel);
++ die("Cannot start proxy %s", srvc->tunnel);
+ imap->buf.sock.fd[0] = tunnel.out;
+ imap->buf.sock.fd[1] = tunnel.in;
+
+- imap_info("ok\n");
++ imap_info("OK\n");
+ } else {
+ #ifndef NO_IPV6
+ struct addrinfo hints, *ai0, *ai;
+@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
+ fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
+ goto bail;
+ }
+- imap_info("ok\n");
++ imap_info("OK\n");
+
+ for (ai0 = ai; ai; ai = ai->ai_next) {
+ char addr[NI_MAXHOST];
+@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
+ perror("gethostbyname");
+ goto bail;
+ }
+- imap_info("ok\n");
++ imap_info("OK\n");
+
+ addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
+
+@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
+ }
#endif
+ if (s < 0) {
+- fputs("Error: unable to connect to server.\n", stderr);
++ fputs("Error: unable to connect to server\n", stderr);
+ goto bail;
+ }
-+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
-+{
-+ int ret;
-+ char *b64;
-+
-+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
-+ if (!b64)
-+ return error("PLAIN: base64 encoding failed");
-+
-+ /* Send the base64-encoded response */
-+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
-+ if (ret != (int)strlen(b64)) {
-+ free(b64);
-+ return error("IMAP error: sending PLAIN response failed");
-+ }
-+
-+ free(b64);
-+ return 0;
-+}
-+
- static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
- {
- int ret;
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
- if (srvc->auth_method) {
- struct imap_cmd_cb cb;
-
-- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
-+ if (!strcmp(srvc->auth_method, "PLAIN")) {
-+ if (!CAP(AUTH_PLAIN)) {
-+ fprintf(stderr, "You specified "
-+ "PLAIN as authentication method, "
-+ "but %s doesn't support it.\n", srvc->host);
-+ goto bail;
-+ }
-+ /* PLAIN */
-+
-+ memset(&cb, 0, sizeof(cb));
-+ cb.cont = auth_plain;
-+ if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
-+ fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
-+ goto bail;
-+ }
-+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
- if (!CAP(AUTH_CRAM_MD5)) {
- fprintf(stderr, "You specified "
- "CRAM-MD5 as authentication method, "
+ close(s);
+ goto bail;
+ }
+- imap_info("ok\n");
++ imap_info("OK\n");
+ }
+
+ /* read the greeting string */
+@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
+ }
+ } else {
+ if (CAP(NOLOGIN)) {
+- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
++ fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN.\n",
+ srvc->user, srvc->host);
+ goto bail;
+ }
+ if (!imap->buf.sock.ssl)
+ imap_warn("*** IMAP Warning *** Password is being "
+- "sent in the clear\n");
++ "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;
+@@ imap-send.c: static int append_msgs_to_imap(struct imap_server_conf *server,
+
+ ctx = imap_open_store(server, server->folder);
+ if (!ctx) {
+- fprintf(stderr, "failed to open store\n");
++ fprintf(stderr, "Failed to open store.\n");
+ return 1;
+ }
+ ctx->name = server->folder;
+
+- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
++ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ while (1) {
+ unsigned percent = n * 100 / total;
+
+@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
+
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+- die("failed to encode server folder");
++ die("Failed to encode server folder.");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+
+@@ imap-send.c: static int curl_append_msgs_to_imap(struct imap_server_conf *server,
+ curl = setup_curl(server, &cred);
+ curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
+
+- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
++ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ while (1) {
+ unsigned percent = n * 100 / total;
+ int prev_len;
+@@ imap-send.c: int cmd_main(int argc, const char **argv)
+ server.port = server.use_ssl ? 993 : 143;
+
+ if (!server.folder) {
+- fprintf(stderr, "no imap store specified\n");
++ fprintf(stderr, "No IMAP store specified.\n");
+ ret = 1;
+ goto out;
+ }
+ if (!server.host) {
+ if (!server.tunnel) {
+- fprintf(stderr, "no imap host specified\n");
++ fprintf(stderr, "No IMAP host specified.\n");
+ ret = 1;
+ goto out;
+ }
+@@ imap-send.c: int cmd_main(int argc, const char **argv)
+
+ /* read the messages */
+ if (strbuf_read(&all_msgs, 0, 0) < 0) {
+- error_errno(_("could not read from stdin"));
++ error_errno(_("Could not read from stdin."));
+ ret = 1;
+ goto out;
+ }
+
+ if (all_msgs.len == 0) {
+- fprintf(stderr, "nothing to send\n");
++ fprintf(stderr, "Nothing to send.\n");
+ ret = 1;
+ goto out;
+ }
+
+ total = count_messages(&all_msgs);
+ if (!total) {
+- fprintf(stderr, "no messages to send\n");
++ fprintf(stderr, "No messages found to send.\n");
+ ret = 1;
+ goto out;
+ }
-: ---------- > 8: a60d8f458f imap-send: display port alongwith host when git credential is invoked
-: ---------- > 9: 5db5b64a3b imap-send: display the destination mailbox when sending a message
--
2.49.0.638.g5db5b64a3b.dirty
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v7 1/9] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-28 17:17 ` [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-28 17:17 ` Aditya Garg
2025-05-28 17:17 ` [PATCH v7 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (7 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 17:17 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0.638.g5db5b64a3b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v7 2/9] imap-send: add support for OAuth2.0 authentication
2025-05-28 17:17 ` [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-28 17:17 ` [PATCH v7 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-05-28 17:17 ` Aditya Garg
2025-05-28 17:17 ` [PATCH v7 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (6 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 17:17 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 46 +++++++-
imap-send.c | 176 +++++++++++++++++++++++++++++--
3 files changed, 214 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..fef6487293 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
+ 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext LOGIN command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..c3a46070ac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,19 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your account password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you can generate
+an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create it.
+If you do not want to enable multi-factor authentication, you can use OAuth2.0
+authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +123,33 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
+or `XOAUTH2` mechanism in your config. In such a case you will have to use an
+OAuth2.0 access token in place of your password.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +158,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..0c7844aff2 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,66 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +959,20 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+static char *oauthbearer_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use OAUTHBEARER authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
+static char *xoauth2_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use XOAUTH2 authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -913,6 +991,46 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1104,6 +1222,36 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1553,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
+ strcmp(srvc->auth_method, "XOAUTH2") ||
+ strcmp(srvc->auth_method, "OAUTHBEARER"))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1575,21 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /* While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0.638.g5db5b64a3b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v7 3/9] imap-send: add PLAIN authentication method to OpenSSL
2025-05-28 17:17 ` [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-28 17:17 ` [PATCH v7 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-28 17:17 ` [PATCH v7 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-05-28 17:17 ` Aditya Garg
2025-05-28 17:17 ` [PATCH v7 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (5 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 17:17 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +-
imap-send.c | 80 +++++++++++++++++++++++++++++++++-
2 files changed, 81 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index fef6487293..24e88228d0 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
- 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are 'PLAIN', 'CRAM-MD5', 'OAUTHBEARER'
+ and 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
plaintext LOGIN command.
diff --git a/imap-send.c b/imap-send.c
index 0c7844aff2..c07ff98c3a 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,40 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /* Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +987,13 @@ static char *xoauth2_base64(const char *user, const char *access_token)
#else
+static char *plain_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use PLAIN authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
static char *cram(const char *challenge_64 UNUSED,
const char *user UNUSED,
const char *pass UNUSED)
@@ -975,6 +1018,26 @@ static char *xoauth2_base64(const char *user UNUSED,
#endif
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1207,7 +1270,22 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (srvc->auth_method) {
struct imap_cmd_cb cb;
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (!CAP(AUTH_PLAIN)) {
+ fprintf(stderr, "You specified "
+ "PLAIN as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* PLAIN */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_plain;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
"CRAM-MD5 as authentication method, "
--
2.49.0.638.g5db5b64a3b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v7 4/9] imap-send: fix memory leak in case auth_cram_md5 fails
2025-05-28 17:17 ` [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-05-28 17:17 ` [PATCH v7 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-05-28 17:17 ` Aditya Garg
2025-05-28 17:17 ` [PATCH v7 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
` (4 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 17:17 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index c07ff98c3a..d0c7bac030 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1046,8 +1046,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0.638.g5db5b64a3b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v7 5/9] imap-send: enable specifying the folder using the command line
2025-05-28 17:17 ` [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-05-28 17:17 ` [PATCH v7 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-05-28 17:17 ` Aditya Garg
2025-05-28 17:17 ` [PATCH v7 6/9] imap-send: enable user to choose between libcurl and openssl using the config Aditya Garg
` (3 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 17:17 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 24e88228d0..829d9e0bac 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,8 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: 'INBOX.Drafts', 'INBOX/Drafts' or
+ '[Gmail]/Drafts'. Required if `--folder` argument is not used. If
+ set and `--folder` is also used, `--folder` will be preferred.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index c3a46070ac..a35f278baf 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields 'From', 'Date', and 'Subject' in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index d0c7bac030..337f1049ca 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1762,6 +1764,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0.638.g5db5b64a3b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v7 6/9] imap-send: enable user to choose between libcurl and openssl using the config
2025-05-28 17:17 ` [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-05-28 17:17 ` [PATCH v7 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-05-28 17:17 ` Aditya Garg
2025-05-29 13:58 ` Phillip Wood
2025-05-28 17:17 ` [PATCH v7 7/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
` (2 subsequent siblings)
8 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 17:17 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
Currently, imap-send allows the user to choose between libcurl and
openssl in case Git is compiled with both libraries only using the
command line, and no option to set a default using the config is
available. Add support for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 7 +++++++
Documentation/git-imap-send.adoc | 4 ++--
imap-send.c | 2 ++
3 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 829d9e0bac..608c0be7ab 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -25,6 +25,13 @@ imap.port::
Defaults to 143 for imap:// hosts and 993 for imaps:// hosts.
Ignored when imap.tunnel is set.
+imap.usecurl::
+ A boolean to choose whether to use libcurl or not to communicate
+ with the IMAP server.
+ Ignored if Git was built without `USE_CURL_FOR_IMAP_SEND` option
+ or with `NO_OPENSSL` option set.
+ `--[no]-curl` argument will override this option.
+
imap.sslverify::
A boolean to enable/disable verification of the server certificate
used by the SSL/TLS connection. Default is `true`. Ignored when
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index a35f278baf..cbbe534ec2 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -46,12 +46,12 @@ OPTIONS
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
- into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
+ into it. Ignored if Git was built without the `USE_CURL_FOR_IMAP_SEND`
option set.
--no-curl::
Talk to the IMAP server using git's own IMAP routines instead of
- using libcurl. Ignored if Git was built with the NO_OPENSSL option
+ using libcurl. Ignored if Git was built with the `NO_OPENSSL` option
set.
diff --git a/imap-send.c b/imap-send.c
index 337f1049ca..b08ec0e1d5 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1559,6 +1559,8 @@ static int git_imap_config(const char *var, const char *val,
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
+ } else if (!strcmp("imap.usecurl", var)) {
+ use_curl = git_config_bool(var, val);
} else if (!strcmp("imap.host", var)) {
if (!val) {
return config_error_nonbool(var);
--
2.49.0.638.g5db5b64a3b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v7 7/9] imap-send: fix numerous spelling and grammar mistakes in logs
2025-05-28 17:17 ` [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-05-28 17:17 ` [PATCH v7 6/9] imap-send: enable user to choose between libcurl and openssl using the config Aditya Garg
@ 2025-05-28 17:17 ` Aditya Garg
2025-05-28 17:17 ` [PATCH v7 8/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-05-28 17:17 ` [PATCH v7 9/9] imap-send: display the destination mailbox when sending a message Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 17:17 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
A lot of spelling and grammar mistakes were found in the logs shown to
the user while using imap-send. Most of them are lack of a full stop at
the end of a sentence and first word of a sentence not being capitalized.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 44 ++++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 22 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index b08ec0e1d5..204d2a14b2 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -205,7 +205,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in.\n");
return -1;
}
@@ -249,9 +249,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
/* try the common name */
if (!(subj = X509_get_subject_name(cert)))
- return error("cannot get certificate subject");
+ return error("Cannot get certificate subject");
if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
- return error("cannot get certificate common name");
+ return error("Cannot get certificate common name");
if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
return 0;
return error("certificate owner '%s' does not match hostname '%s'",
@@ -905,7 +905,7 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
decoded_len = EVP_DecodeBlock((unsigned char *)challenge,
(unsigned char *)challenge_64, encoded_len);
if (decoded_len < 0)
- die("invalid challenge %s", challenge_64);
+ die("Invalid challenge %s", challenge_64);
if (!HMAC(EVP_md5(), pass, strlen(pass), (unsigned char *)challenge, decoded_len, hash, NULL))
die("HMAC error");
@@ -1050,7 +1050,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1144,12 +1144,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
tunnel.in = -1;
tunnel.out = -1;
if (start_command(&tunnel))
- die("cannot start proxy %s", srvc->tunnel);
+ die("Cannot start proxy %s", srvc->tunnel);
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1168,7 +1168,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1206,7 +1206,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1220,7 +1220,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("Error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1232,7 +1232,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1340,13 +1340,13 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN.\n",
srvc->user, srvc->host);
goto bail;
}
if (!imap->buf.sock.ssl)
imap_warn("*** IMAP Warning *** Password is being "
- "sent in the clear\n");
+ "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;
@@ -1593,12 +1593,12 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
ctx = imap_open_store(server, server->folder);
if (!ctx) {
- fprintf(stderr, "failed to open store\n");
+ fprintf(stderr, "Failed to open store.\n");
return 1;
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
@@ -1650,7 +1650,7 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
if (!uri_encoded_folder)
- die("failed to encode server folder");
+ die("Failed to encode server folder.");
strbuf_addstr(&path, uri_encoded_folder);
curl_free(uri_encoded_folder);
@@ -1706,7 +1706,7 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
@@ -1790,13 +1790,13 @@ int cmd_main(int argc, const char **argv)
server.port = server.use_ssl ? 993 : 143;
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "No IMAP store specified.\n");
ret = 1;
goto out;
}
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "No IMAP host specified.\n");
ret = 1;
goto out;
}
@@ -1805,20 +1805,20 @@ int cmd_main(int argc, const char **argv)
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
- error_errno(_("could not read from stdin"));
+ error_errno(_("Could not read from stdin."));
ret = 1;
goto out;
}
if (all_msgs.len == 0) {
- fprintf(stderr, "nothing to send\n");
+ fprintf(stderr, "Nothing to send.\n");
ret = 1;
goto out;
}
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "No messages found to send.\n");
ret = 1;
goto out;
}
--
2.49.0.638.g5db5b64a3b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v7 8/9] imap-send: display port alongwith host when git credential is invoked
2025-05-28 17:17 ` [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-05-28 17:17 ` [PATCH v7 7/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
@ 2025-05-28 17:17 ` Aditya Garg
2025-05-28 17:17 ` [PATCH v7 9/9] imap-send: display the destination mailbox when sending a message Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 17:17 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
FWIW, if no port is specified by the user, the default port, 993 for
IMAPS and 143 for IMAP is used by the code. So, the case of no port
defined for the helper is not possible, and therefore is not added.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 204d2a14b2..3172cd5191 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1104,7 +1104,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0.638.g5db5b64a3b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v7 9/9] imap-send: display the destination mailbox when sending a message
2025-05-28 17:17 ` [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-05-28 17:17 ` [PATCH v7 8/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-05-28 17:17 ` Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-28 17:17 UTC (permalink / raw)
To: Junio C Hamano, git; +Cc: Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
Sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 3172cd5191..fd589f8aa1 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1598,7 +1598,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1706,7 +1707,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0.638.g5db5b64a3b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v7 6/9] imap-send: enable user to choose between libcurl and openssl using the config
2025-05-28 17:17 ` [PATCH v7 6/9] imap-send: enable user to choose between libcurl and openssl using the config Aditya Garg
@ 2025-05-29 13:58 ` Phillip Wood
2025-05-29 14:09 ` Aditya Garg
2025-05-29 16:25 ` Junio C Hamano
0 siblings, 2 replies; 248+ messages in thread
From: Phillip Wood @ 2025-05-29 13:58 UTC (permalink / raw)
To: Aditya Garg, Junio C Hamano, git
Cc: Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
Hi Aditya
On 28/05/2025 18:17, Aditya Garg wrote:
> Currently, imap-send allows the user to choose between libcurl and
> openssl in case Git is compiled with both libraries only using the
> command line, and no option to set a default using the config is
> available. Add support for the same.
I'm wondering why anyone would want to switch the backend at run-time?
There has been talk in the past about removing the openssl code [1] and
just relying on the curl backend. I think that is a worthwhile goal as
it simplifies the code and means we would avoid having to worry about
whether we're using openssl correctly [2]. That would be harder to do if
we add this config setting. If we don't already do so, perhaps we could
start using libcurl even when openssl is also available though that does
not need to be part of this patch series.
Best Wishes
Phillip
[1] https://lore.kernel.org/git/Y+LNitGAude1vogv@coredump.intra.peff.net/
[2]
https://lore.kernel.org/git/pull.1886.git.1742819282360.gitgitgadget@gmail.com/
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
> ---
> Documentation/config/imap.adoc | 7 +++++++
> Documentation/git-imap-send.adoc | 4 ++--
> imap-send.c | 2 ++
> 3 files changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
> index 829d9e0bac..608c0be7ab 100644
> --- a/Documentation/config/imap.adoc
> +++ b/Documentation/config/imap.adoc
> @@ -25,6 +25,13 @@ imap.port::
> Defaults to 143 for imap:// hosts and 993 for imaps:// hosts.
> Ignored when imap.tunnel is set.
>
> +imap.usecurl::
> + A boolean to choose whether to use libcurl or not to communicate
> + with the IMAP server.
> + Ignored if Git was built without `USE_CURL_FOR_IMAP_SEND` option
> + or with `NO_OPENSSL` option set.
> + `--[no]-curl` argument will override this option.
> +
> imap.sslverify::
> A boolean to enable/disable verification of the server certificate
> used by the SSL/TLS connection. Default is `true`. Ignored when
> diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
> index a35f278baf..cbbe534ec2 100644
> --- a/Documentation/git-imap-send.adoc
> +++ b/Documentation/git-imap-send.adoc
> @@ -46,12 +46,12 @@ OPTIONS
>
> --curl::
> Use libcurl to communicate with the IMAP server, unless tunneling
> - into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
> + into it. Ignored if Git was built without the `USE_CURL_FOR_IMAP_SEND`
> option set.
>
> --no-curl::
> Talk to the IMAP server using git's own IMAP routines instead of
> - using libcurl. Ignored if Git was built with the NO_OPENSSL option
> + using libcurl. Ignored if Git was built with the `NO_OPENSSL` option
> set.
>
>
> diff --git a/imap-send.c b/imap-send.c
> index 337f1049ca..b08ec0e1d5 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -1559,6 +1559,8 @@ static int git_imap_config(const char *var, const char *val,
> return git_config_string(&cfg->auth_method, var, val);
> } else if (!strcmp("imap.port", var)) {
> cfg->port = git_config_int(var, val, ctx->kvi);
> + } else if (!strcmp("imap.usecurl", var)) {
> + use_curl = git_config_bool(var, val);
> } else if (!strcmp("imap.host", var)) {
> if (!val) {
> return config_error_nonbool(var);
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v7 6/9] imap-send: enable user to choose between libcurl and openssl using the config
2025-05-29 13:58 ` Phillip Wood
@ 2025-05-29 14:09 ` Aditya Garg
2025-05-29 16:25 ` Junio C Hamano
1 sibling, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-29 14:09 UTC (permalink / raw)
To: phillip.wood, Junio C Hamano, git
Cc: Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
On 29/05/25 7:28 pm, Phillip Wood wrote:
> Hi Aditya
>
> On 28/05/2025 18:17, Aditya Garg wrote:
>> Currently, imap-send allows the user to choose between libcurl and
>> openssl in case Git is compiled with both libraries only using the
>> command line, and no option to set a default using the config is
>> available. Add support for the same.
>
> I'm wondering why anyone would want to switch the backend at run-time? There has been talk in the past about removing the openssl code [1] and just relying on the curl backend. I think that is a worthwhile goal as it simplifies the code and means we would avoid having to worry about whether we're using openssl correctly [2]. That would be harder to do if we add this config setting. If we don't already do so, perhaps we could start using libcurl even when openssl is also available though that does not need to be part of this patch series.
OpenSSL is still needed for CRAM-MD5, something which curl does not support at all.
I agree CRAM-MD5 is not popular today, but I have seen servers that still use it
even today (For example rediffmail).
Also, implementing more features is simply more easy and feasible with OpenSSL,
curl has limitations.
Lastly, in my tests, OpenSSL did perform much better in terms of sending mails
than curl, and thus having that implementation does not harm if someone is ready
to compile git themselves.
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (11 preceding siblings ...)
2025-05-28 17:17 ` [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-29 16:21 ` Aditya Garg
2025-05-29 16:21 ` [PATCH v8 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (8 more replies)
2025-05-30 17:32 ` [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (10 subsequent siblings)
23 siblings, 9 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-29 16:21 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, sandals@crustytoothpaste.net, Jeff King,
Ben Knoble, Phillip Wood
This patch series does the following things:
Firstly it basically makes the imap-send command usable again since it
was broken because of not being able to correctly parse the config file.
Further it adds support for OAuth2.0 and PLAIN authentication to git
imap-send.
Last, it does some minor improvements including adding the ability to
specify the folder using the command line and ability to list the
available folders by adding a `--list` option.
P.S.: I am surprised this thing even exists xD.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
v8: - Drop the patch that enabled user to choose between libcurl and openssl using the config
- Add ability to list the available folders by adding a `--list` option
Aditya Garg (9):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: enable specifying the folder using the command line
imap-send: fix numerous spelling and grammar mistakes in logs
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
imap-send: add ability to list the available folders
Documentation/config/imap.adoc | 10 +-
Documentation/git-imap-send.adoc | 67 ++++-
imap-send.c | 417 +++++++++++++++++++++++++++----
3 files changed, 431 insertions(+), 63 deletions(-)
Range-diff against v7:
1: 4757d0305d = 1: 3e3ddf7077 imap-send: fix bug causing cfg->folder being set to NULL
2: c4e2a5659b = 2: f0743d46e1 imap-send: add support for OAuth2.0 authentication
3: af9aa85cab = 3: b1602644b7 imap-send: add PLAIN authentication method to OpenSSL
4: 2ca10774db = 4: 49790e60cc imap-send: fix memory leak in case auth_cram_md5 fails
5: 190bed0bff = 5: 2efe897379 imap-send: enable specifying the folder using the command line
7: 6a839e5f4d = 6: 8f6676a046 imap-send: fix numerous spelling and grammar mistakes in logs
8: a60d8f458f = 7: 69fdae55cd imap-send: display port alongwith host when git credential is invoked
9: 5db5b64a3b = 8: 187dbccd03 imap-send: display the destination mailbox when sending a message
6: 469c05321b ! 9: 03d7d6a772 imap-send: enable user to choose between libcurl and openssl using the config
@@ Metadata
Author: Aditya Garg <gargaditya08@live.com>
## Commit message ##
- imap-send: enable user to choose between libcurl and openssl using the config
+ imap-send: add ability to list the available folders
- Currently, imap-send allows the user to choose between libcurl and
- openssl in case Git is compiled with both libraries only using the
- command line, and no option to set a default using the config is
- available. Add support for the same.
+ Various IMAP servers have different ways to name common folders.
+ For example, the folder where all deleted messages are stored is often
+ named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
+ Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
+ on Gmail it is named "[Gmail]/Drafts".
- Signed-off-by: Aditya Garg <gargaditya08@live.com>
+ This commit adds a `--list` command to the `imap-send` tool that lists
+ the available folders on the IMAP server, allowing users to see
+ which folders are available and how they are named. A sample output
+ looks like this when run against a Gmail server:
- ## Documentation/config/imap.adoc ##
-@@ Documentation/config/imap.adoc: imap.port::
- Defaults to 143 for imap:// hosts and 993 for imaps:// hosts.
- Ignored when imap.tunnel is set.
-
-+imap.usecurl::
-+ A boolean to choose whether to use libcurl or not to communicate
-+ with the IMAP server.
-+ Ignored if Git was built without `USE_CURL_FOR_IMAP_SEND` option
-+ or with `NO_OPENSSL` option set.
-+ `--[no]-curl` argument will override this option.
-+
- imap.sslverify::
- A boolean to enable/disable verification of the server certificate
- used by the SSL/TLS connection. Default is `true`. Ignored when
+ Fetching the list of available folders...
+ * LIST (\HasNoChildren) "/" "INBOX"
+ * LIST (\HasChildren \Noselect) "/" "[Gmail]"
+ * LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
+ * LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
+ * LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
+ * LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
+ * LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
+ * LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
+ * LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
+
+ For OpenSSL, this is achived by running the 'IMAP LIST' command and
+ parsing the response. This command is specified in RFC6154:
+ https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
+
+ For libcurl, the example code published in the libcurl documentation
+ is used to implement this functionality:
+ https://curl.se/libcurl/c/imap-list.html
+
+ Signed-off-by: Aditya Garg <gargaditya08@live.com>
## Documentation/git-imap-send.adoc ##
-@@ Documentation/git-imap-send.adoc: OPTIONS
+@@ Documentation/git-imap-send.adoc: SYNOPSIS
+ --------
+ [verse]
+ 'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
++'git imap-send' --list
- --curl::
- Use libcurl to communicate with the IMAP server, unless tunneling
-- into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
-+ into it. Ignored if Git was built without the `USE_CURL_FOR_IMAP_SEND`
- option set.
- --no-curl::
- Talk to the IMAP server using git's own IMAP routines instead of
-- using libcurl. Ignored if Git was built with the NO_OPENSSL option
-+ using libcurl. Ignored if Git was built with the `NO_OPENSSL` option
+ DESCRIPTION
+@@ Documentation/git-imap-send.adoc: OPTIONS
+ using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
++--list::
++ Run the IMAP LIST command to output a list of all the folders present.
+
+ CONFIGURATION
+ -------------
+@@ Documentation/git-imap-send.adoc: authentication as described below.
+ [NOTE]
+ You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
+-that the "Folder doesn't exist".
++that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
++list of available folders.
+
+ [NOTE]
+ If your Gmail account is set to another language than English, the name of the "Drafts"
## imap-send.c ##
-@@ imap-send.c: static int git_imap_config(const char *var, const char *val,
- return git_config_string(&cfg->auth_method, var, val);
- } else if (!strcmp("imap.port", var)) {
- cfg->port = git_config_int(var, val, ctx->kvi);
-+ } else if (!strcmp("imap.usecurl", var)) {
-+ use_curl = git_config_bool(var, val);
- } else if (!strcmp("imap.host", var)) {
- if (!val) {
- return config_error_nonbool(var);
+@@
+ #endif
+
+ static int verbosity;
++static int list_folders = 0;
+ static int use_curl = USE_CURL_DEFAULT;
+ static char *opt_folder = NULL;
+
+-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
++static char const * const imap_send_usage[] = {
++ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
++ "git imap-send --list",
++ NULL
++};
+
+ static struct option imap_send_options[] = {
+ OPT__VERBOSITY(&verbosity),
+ OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
++ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
+ OPT_END()
+ };
+
+@@ imap-send.c: static int buffer_gets(struct imap_buffer *b, char **s)
+ if (b->buf[b->offset + 1] == '\n') {
+ b->buf[b->offset] = 0; /* terminate the string */
+ b->offset += 2; /* next line */
+- if (0 < verbosity)
++ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
+ puts(*s);
+ return 0;
+ }
+@@ imap-send.c: static int append_msgs_to_imap(struct imap_server_conf *server,
+ return 0;
+ }
+
++static int list_imap_folders(struct imap_server_conf *server)
++{
++ struct imap_store *ctx = imap_open_store(server, "INBOX");
++ if (!ctx) {
++ fprintf(stderr, "Failed to connect to IMAP server.\n");
++ return 1;
++ }
++
++ fprintf(stderr, "Fetching the list of available folders...\n");
++ /* Issue the LIST command and print the results */
++ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
++ fprintf(stderr, "Failed to list folders.\n");
++ imap_close_store(ctx);
++ return 1;
++ }
++
++ imap_close_store(ctx);
++ return 0;
++}
++
+ #ifdef USE_CURL_FOR_IMAP_SEND
+ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
+ {
+@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
+ if (!path.len || path.buf[path.len - 1] != '/')
+ strbuf_addch(&path, '/');
+
+- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+- if (!uri_encoded_folder)
+- die("Failed to encode server folder.");
+- strbuf_addstr(&path, uri_encoded_folder);
+- curl_free(uri_encoded_folder);
++ if (!list_folders) {
++ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
++ if (!uri_encoded_folder)
++ die("Failed to encode server folder.");
++ strbuf_addstr(&path, uri_encoded_folder);
++ curl_free(uri_encoded_folder);
++ }
+
+ curl_easy_setopt(curl, CURLOPT_URL, path.buf);
+ strbuf_release(&path);
+@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
+
+- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+-
+- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+-
+ if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
+ http_trace_curl_no_data();
+ setup_curl_trace(curl);
+@@ imap-send.c: static int curl_append_msgs_to_imap(struct imap_server_conf *server,
+ struct credential cred = CREDENTIAL_INIT;
+
+ curl = setup_curl(server, &cred);
++
++ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
++ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
++
+ curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
+
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+@@ imap-send.c: static int curl_append_msgs_to_imap(struct imap_server_conf *server,
+
+ return res != CURLE_OK;
+ }
++
++static int curl_list_imap_folders(struct imap_server_conf *server)
++{
++ CURL *curl;
++ CURLcode res = CURLE_OK;
++ struct credential cred = CREDENTIAL_INIT;
++
++ fprintf(stderr, "Fetching the list of available folders...\n");
++ curl = setup_curl(server, &cred);
++ res = curl_easy_perform(curl);
++
++ curl_easy_cleanup(curl);
++ curl_global_cleanup();
++
++ if (cred.username) {
++ if (res == CURLE_OK)
++ credential_approve(the_repository, &cred);
++ else if (res == CURLE_LOGIN_DENIED)
++ credential_reject(the_repository, &cred);
++ }
++
++ credential_clear(&cred);
++
++ return res != CURLE_OK;
++}
+ #endif
+
+ int cmd_main(int argc, const char **argv)
+@@ imap-send.c: int cmd_main(int argc, const char **argv)
+ if (!server.port)
+ server.port = server.use_ssl ? 993 : 143;
+
+- if (!server.folder) {
+- fprintf(stderr, "No IMAP store specified.\n");
+- ret = 1;
+- goto out;
+- }
+ if (!server.host) {
+ if (!server.tunnel) {
+ fprintf(stderr, "No IMAP host specified.\n");
+@@ imap-send.c: int cmd_main(int argc, const char **argv)
+ server.host = xstrdup("tunnel");
+ }
+
++ if (list_folders) {
++ if (server.tunnel)
++ ret = list_imap_folders(&server);
++#ifdef USE_CURL_FOR_IMAP_SEND
++ else if (use_curl)
++ ret = curl_list_imap_folders(&server);
++#endif
++ else
++ ret = list_imap_folders(&server);
++ goto out;
++ }
++
++ if (!server.folder) {
++ fprintf(stderr, "No IMAP store specified.\n");
++ ret = 1;
++ goto out;
++ }
++
+ /* read the messages */
+ if (strbuf_read(&all_msgs, 0, 0) < 0) {
+ error_errno(_("Could not read from stdin."));
--
2.49.0.638.g602e07a80b.dirty
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v8 1/9] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-29 16:21 ` [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-29 16:21 ` Aditya Garg
2025-05-29 16:21 ` [PATCH v8 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (7 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-29 16:21 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, sandals@crustytoothpaste.net, Jeff King,
Ben Knoble, Phillip Wood
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0.638.g602e07a80b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v8 2/9] imap-send: add support for OAuth2.0 authentication
2025-05-29 16:21 ` [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-29 16:21 ` [PATCH v8 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-05-29 16:21 ` Aditya Garg
2025-05-29 16:21 ` [PATCH v8 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (6 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-29 16:21 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, sandals@crustytoothpaste.net, Jeff King,
Ben Knoble, Phillip Wood
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 46 +++++++-
imap-send.c | 176 +++++++++++++++++++++++++++++--
3 files changed, 214 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..fef6487293 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
+ 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext LOGIN command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..c3a46070ac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,19 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your account password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you can generate
+an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create it.
+If you do not want to enable multi-factor authentication, you can use OAuth2.0
+authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +123,33 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
+or `XOAUTH2` mechanism in your config. In such a case you will have to use an
+OAuth2.0 access token in place of your password.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +158,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..0c7844aff2 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,66 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +959,20 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+static char *oauthbearer_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use OAUTHBEARER authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
+static char *xoauth2_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use XOAUTH2 authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -913,6 +991,46 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1104,6 +1222,36 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1553,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
+ strcmp(srvc->auth_method, "XOAUTH2") ||
+ strcmp(srvc->auth_method, "OAUTHBEARER"))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1575,21 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /* While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0.638.g602e07a80b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v8 3/9] imap-send: add PLAIN authentication method to OpenSSL
2025-05-29 16:21 ` [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-29 16:21 ` [PATCH v8 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-29 16:21 ` [PATCH v8 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-05-29 16:21 ` Aditya Garg
2025-05-29 16:21 ` [PATCH v8 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (5 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-29 16:21 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, sandals@crustytoothpaste.net, Jeff King,
Ben Knoble, Phillip Wood
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +-
imap-send.c | 80 +++++++++++++++++++++++++++++++++-
2 files changed, 81 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index fef6487293..24e88228d0 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
- 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are 'PLAIN', 'CRAM-MD5', 'OAUTHBEARER'
+ and 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
plaintext LOGIN command.
diff --git a/imap-send.c b/imap-send.c
index 0c7844aff2..c07ff98c3a 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,40 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /* Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +987,13 @@ static char *xoauth2_base64(const char *user, const char *access_token)
#else
+static char *plain_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use PLAIN authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
static char *cram(const char *challenge_64 UNUSED,
const char *user UNUSED,
const char *pass UNUSED)
@@ -975,6 +1018,26 @@ static char *xoauth2_base64(const char *user UNUSED,
#endif
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1207,7 +1270,22 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (srvc->auth_method) {
struct imap_cmd_cb cb;
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (!CAP(AUTH_PLAIN)) {
+ fprintf(stderr, "You specified "
+ "PLAIN as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* PLAIN */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_plain;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
"CRAM-MD5 as authentication method, "
--
2.49.0.638.g602e07a80b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v8 4/9] imap-send: fix memory leak in case auth_cram_md5 fails
2025-05-29 16:21 ` [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-05-29 16:21 ` [PATCH v8 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-05-29 16:21 ` Aditya Garg
2025-05-29 16:21 ` [PATCH v8 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
` (4 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-29 16:21 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, sandals@crustytoothpaste.net, Jeff King,
Ben Knoble, Phillip Wood
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index c07ff98c3a..d0c7bac030 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1046,8 +1046,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0.638.g602e07a80b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v8 5/9] imap-send: enable specifying the folder using the command line
2025-05-29 16:21 ` [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-05-29 16:21 ` [PATCH v8 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-05-29 16:21 ` Aditya Garg
2025-05-29 16:21 ` [PATCH v8 6/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
` (3 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-29 16:21 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, sandals@crustytoothpaste.net, Jeff King,
Ben Knoble, Phillip Wood
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 24e88228d0..829d9e0bac 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,8 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: 'INBOX.Drafts', 'INBOX/Drafts' or
+ '[Gmail]/Drafts'. Required if `--folder` argument is not used. If
+ set and `--folder` is also used, `--folder` will be preferred.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index c3a46070ac..a35f278baf 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields 'From', 'Date', and 'Subject' in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index d0c7bac030..337f1049ca 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1762,6 +1764,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0.638.g602e07a80b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v8 6/9] imap-send: fix numerous spelling and grammar mistakes in logs
2025-05-29 16:21 ` [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-05-29 16:21 ` [PATCH v8 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-05-29 16:21 ` Aditya Garg
2025-05-29 16:21 ` [PATCH v8 7/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
` (2 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-29 16:21 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, sandals@crustytoothpaste.net, Jeff King,
Ben Knoble, Phillip Wood
A lot of spelling and grammar mistakes were found in the logs shown to
the user while using imap-send. Most of them are lack of a full stop at
the end of a sentence and first word of a sentence not being capitalized.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 44 ++++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 22 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 337f1049ca..d99eed0659 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -205,7 +205,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in.\n");
return -1;
}
@@ -249,9 +249,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
/* try the common name */
if (!(subj = X509_get_subject_name(cert)))
- return error("cannot get certificate subject");
+ return error("Cannot get certificate subject");
if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
- return error("cannot get certificate common name");
+ return error("Cannot get certificate common name");
if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
return 0;
return error("certificate owner '%s' does not match hostname '%s'",
@@ -905,7 +905,7 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
decoded_len = EVP_DecodeBlock((unsigned char *)challenge,
(unsigned char *)challenge_64, encoded_len);
if (decoded_len < 0)
- die("invalid challenge %s", challenge_64);
+ die("Invalid challenge %s", challenge_64);
if (!HMAC(EVP_md5(), pass, strlen(pass), (unsigned char *)challenge, decoded_len, hash, NULL))
die("HMAC error");
@@ -1050,7 +1050,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1144,12 +1144,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
tunnel.in = -1;
tunnel.out = -1;
if (start_command(&tunnel))
- die("cannot start proxy %s", srvc->tunnel);
+ die("Cannot start proxy %s", srvc->tunnel);
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1168,7 +1168,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1206,7 +1206,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1220,7 +1220,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("Error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1232,7 +1232,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1340,13 +1340,13 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN.\n",
srvc->user, srvc->host);
goto bail;
}
if (!imap->buf.sock.ssl)
imap_warn("*** IMAP Warning *** Password is being "
- "sent in the clear\n");
+ "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;
@@ -1591,12 +1591,12 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
ctx = imap_open_store(server, server->folder);
if (!ctx) {
- fprintf(stderr, "failed to open store\n");
+ fprintf(stderr, "Failed to open store.\n");
return 1;
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
@@ -1648,7 +1648,7 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
if (!uri_encoded_folder)
- die("failed to encode server folder");
+ die("Failed to encode server folder.");
strbuf_addstr(&path, uri_encoded_folder);
curl_free(uri_encoded_folder);
@@ -1704,7 +1704,7 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
@@ -1788,13 +1788,13 @@ int cmd_main(int argc, const char **argv)
server.port = server.use_ssl ? 993 : 143;
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "No IMAP store specified.\n");
ret = 1;
goto out;
}
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "No IMAP host specified.\n");
ret = 1;
goto out;
}
@@ -1803,20 +1803,20 @@ int cmd_main(int argc, const char **argv)
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
- error_errno(_("could not read from stdin"));
+ error_errno(_("Could not read from stdin."));
ret = 1;
goto out;
}
if (all_msgs.len == 0) {
- fprintf(stderr, "nothing to send\n");
+ fprintf(stderr, "Nothing to send.\n");
ret = 1;
goto out;
}
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "No messages found to send.\n");
ret = 1;
goto out;
}
--
2.49.0.638.g602e07a80b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v8 7/9] imap-send: display port alongwith host when git credential is invoked
2025-05-29 16:21 ` [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-05-29 16:21 ` [PATCH v8 6/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
@ 2025-05-29 16:21 ` Aditya Garg
2025-05-29 16:21 ` [PATCH v8 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-05-29 16:21 ` [PATCH v8 9/9] imap-send: add ability to list the available folders Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-29 16:21 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, sandals@crustytoothpaste.net, Jeff King,
Ben Knoble, Phillip Wood
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
FWIW, if no port is specified by the user, the default port, 993 for
IMAPS and 143 for IMAP is used by the code. So, the case of no port
defined for the helper is not possible, and therefore is not added.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index d99eed0659..c963ce62d8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1104,7 +1104,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0.638.g602e07a80b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v8 8/9] imap-send: display the destination mailbox when sending a message
2025-05-29 16:21 ` [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-05-29 16:21 ` [PATCH v8 7/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-05-29 16:21 ` Aditya Garg
2025-05-29 16:21 ` [PATCH v8 9/9] imap-send: add ability to list the available folders Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-29 16:21 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, sandals@crustytoothpaste.net, Jeff King,
Ben Knoble, Phillip Wood
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
Sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index c963ce62d8..95b78fda42 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1596,7 +1596,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1704,7 +1705,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0.638.g602e07a80b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v8 9/9] imap-send: add ability to list the available folders
2025-05-29 16:21 ` [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-05-29 16:21 ` [PATCH v8 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-05-29 16:21 ` Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-29 16:21 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, sandals@crustytoothpaste.net, Jeff King,
Ben Knoble, Phillip Wood
Various IMAP servers have different ways to name common folders.
For example, the folder where all deleted messages are stored is often
named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
on Gmail it is named "[Gmail]/Drafts".
This commit adds a `--list` command to the `imap-send` tool that lists
the available folders on the IMAP server, allowing users to see
which folders are available and how they are named. A sample output
looks like this when run against a Gmail server:
Fetching the list of available folders...
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasChildren \Noselect) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
For OpenSSL, this is achived by running the 'IMAP LIST' command and
parsing the response. This command is specified in RFC6154:
https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
For libcurl, the example code published in the libcurl documentation
is used to implement this functionality:
https://curl.se/libcurl/c/imap-list.html
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/git-imap-send.adoc | 6 +-
imap-send.c | 98 ++++++++++++++++++++++++++------
2 files changed, 87 insertions(+), 17 deletions(-)
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index a35f278baf..24e1459f5c 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+'git imap-send' --list
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
+--list::
+ Run the IMAP LIST command to output a list of all the folders present.
CONFIGURATION
-------------
@@ -124,7 +127,8 @@ authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
-that the "Folder doesn't exist".
+that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
+list of available folders.
[NOTE]
If your Gmail account is set to another language than English, the name of the "Drafts"
diff --git a/imap-send.c b/imap-send.c
index 95b78fda42..60562dc9b8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -45,15 +45,21 @@
#endif
static int verbosity;
+static int list_folders = 0;
static int use_curl = USE_CURL_DEFAULT;
static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
+ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
+ "git imap-send --list",
+ NULL
+};
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
+ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
OPT_END()
};
@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
if (b->buf[b->offset + 1] == '\n') {
b->buf[b->offset] = 0; /* terminate the string */
b->offset += 2; /* next line */
- if (0 < verbosity)
+ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
puts(*s);
return 0;
}
@@ -1619,6 +1625,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
+static int list_imap_folders(struct imap_server_conf *server)
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
+ fprintf(stderr, "Failed to connect to IMAP server.\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
+ fprintf(stderr, "Failed to list folders.\n");
+ imap_close_store(ctx);
+ return 1;
+ }
+
+ imap_close_store(ctx);
+ return 0;
+}
+
#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
@@ -1647,11 +1673,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
- die("Failed to encode server folder.");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+ die("Failed to encode server folder.");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ -1681,10 +1709,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
-
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(curl);
@@ -1703,6 +1727,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
struct credential cred = CREDENTIAL_INIT;
curl = setup_curl(server, &cred);
+
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
fprintf(stderr, "Sending %d message%s to %s folder...\n",
@@ -1749,6 +1777,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
+
+static int curl_list_imap_folders(struct imap_server_conf *server)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct credential cred = CREDENTIAL_INIT;
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ curl = setup_curl(server, &cred);
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ if (cred.username) {
+ if (res == CURLE_OK)
+ credential_approve(the_repository, &cred);
+ else if (res == CURLE_LOGIN_DENIED)
+ credential_reject(the_repository, &cred);
+ }
+
+ credential_clear(&cred);
+
+ return res != CURLE_OK;
+}
#endif
int cmd_main(int argc, const char **argv)
@@ -1789,11 +1842,6 @@ int cmd_main(int argc, const char **argv)
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
- fprintf(stderr, "No IMAP store specified.\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "No IMAP host specified.\n");
@@ -1803,6 +1851,24 @@ int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
+ if (list_folders) {
+ if (server.tunnel)
+ ret = list_imap_folders(&server);
+#ifdef USE_CURL_FOR_IMAP_SEND
+ else if (use_curl)
+ ret = curl_list_imap_folders(&server);
+#endif
+ else
+ ret = list_imap_folders(&server);
+ goto out;
+ }
+
+ if (!server.folder) {
+ fprintf(stderr, "No IMAP store specified.\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("Could not read from stdin."));
--
2.49.0.638.g602e07a80b.dirty
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v7 6/9] imap-send: enable user to choose between libcurl and openssl using the config
2025-05-29 13:58 ` Phillip Wood
2025-05-29 14:09 ` Aditya Garg
@ 2025-05-29 16:25 ` Junio C Hamano
2025-05-29 16:28 ` Aditya Garg
1 sibling, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-05-29 16:25 UTC (permalink / raw)
To: Phillip Wood
Cc: Aditya Garg, git, Eric Sunshine, sandals, Zi Yao, Jeff King,
Ben Knoble
Phillip Wood <phillip.wood123@gmail.com> writes:
> I'm wondering why anyone would want to switch the backend at run-time?
> There has been talk in the past about removing the openssl code [1]
> and just relying on the curl backend. I think that is a worthwhile
> goal as it simplifies the code and means we would avoid having to
> worry about whether we're using openssl correctly [2].
Excellent point. Is there a downside if we only do imap via cURL
library and lose the code that directly use OpenSSL?
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v7 6/9] imap-send: enable user to choose between libcurl and openssl using the config
2025-05-29 16:25 ` Junio C Hamano
@ 2025-05-29 16:28 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-29 16:28 UTC (permalink / raw)
To: Junio C Hamano, Phillip Wood
Cc: git, Eric Sunshine, sandals, Zi Yao, Jeff King, Ben Knoble
On 29/05/25 9:55 pm, Junio C Hamano wrote:
> Phillip Wood <phillip.wood123@gmail.com> writes:
>
>> I'm wondering why anyone would want to switch the backend at run-time?
>> There has been talk in the past about removing the openssl code [1]
>> and just relying on the curl backend. I think that is a worthwhile
>> goal as it simplifies the code and means we would avoid having to
>> worry about whether we're using openssl correctly [2].
>
> Excellent point. Is there a downside if we only do imap via cURL
> library and lose the code that directly use OpenSSL?
I think the only real down side is removing CRAM-MD5 support, which
won't impact much, especially considering the fact that how little
this is used.
Although, most code will still be needed is you are using the tunnel
option, so in terms of code cleanup, not much will be lost.
Anyways, in v8, I have removed this patch, and added another ability
to list the available folders.
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (12 preceding siblings ...)
2025-05-29 16:21 ` [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-30 17:32 ` Aditya Garg
2025-05-30 17:32 ` [PATCH v9 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (8 more replies)
2025-06-01 7:10 ` [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (9 subsequent siblings)
23 siblings, 9 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-30 17:32 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
This patch series does the following things:
Firstly it basically makes the imap-send command usable again since it
was broken because of not being able to correctly parse the config file.
Further it adds support for OAuth2.0 and PLAIN authentication to git
imap-send.
Last, it does some minor improvements including adding the ability to
specify the folder using the command line and ability to list the
available folders by adding a `--list` option.
P.S.: I am surprised this thing even exists xD.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
v8: - Drop the patch that enabled user to choose between libcurl and openssl using the config
- Add ability to list the available folders by adding a `--list` option
v9: - Encourage users to use OAuth2.0 for Gmail (similar change done for send-email docs).
Aditya Garg (9):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: enable specifying the folder using the command line
imap-send: fix numerous spelling and grammar mistakes in logs
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
imap-send: add ability to list the available folders
Documentation/config/imap.adoc | 10 +-
Documentation/git-imap-send.adoc | 68 ++++-
imap-send.c | 417 +++++++++++++++++++++++++++----
3 files changed, 432 insertions(+), 63 deletions(-)
Range-diff against v8:
-: ---------- > 1: 3e3ddf7077 imap-send: fix bug causing cfg->folder being set to NULL
1: f0743d46e1 ! 2: c5ee87051f imap-send: add support for OAuth2.0 authentication
@@ Documentation/git-imap-send.adoc: Using Gmail's IMAP interface:
+ port = 993
---------
-+Gmail does not allow using your account password for `git imap-send`.
++Gmail does not allow using your regular password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you can generate
+an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create it.
-+If you do not want to enable multi-factor authentication, you can use OAuth2.0
-+authentication as described below.
++Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
@@ Documentation/git-imap-send.adoc: that the "Folder doesn't exist".
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
-+or `XOAUTH2` mechanism in your config. In such a case you will have to use an
-+OAuth2.0 access token in place of your password.
++or `XOAUTH2` mechanism in your config. It is more secure than using app-specific
++passwords, and also does not enforce the need of having multi-factor authentication.
++You will have to use an OAuth2.0 access token in place of your password when using this
++authentication.
+
+---------
+[imap]
2: b1602644b7 = 3: 17e263ea27 imap-send: add PLAIN authentication method to OpenSSL
3: 49790e60cc = 4: 5c471f640b imap-send: fix memory leak in case auth_cram_md5 fails
4: 2efe897379 = 5: db8ee71785 imap-send: enable specifying the folder using the command line
5: 8f6676a046 = 6: a8fbcdf9d5 imap-send: fix numerous spelling and grammar mistakes in logs
6: 69fdae55cd = 7: a5dad0f2b2 imap-send: display port alongwith host when git credential is invoked
7: 187dbccd03 = 8: d2569a5e36 imap-send: display the destination mailbox when sending a message
8: 03d7d6a772 ! 9: cf844b2632 imap-send: add ability to list the available folders
@@ Documentation/git-imap-send.adoc: OPTIONS
CONFIGURATION
-------------
-@@ Documentation/git-imap-send.adoc: authentication as described below.
+@@ Documentation/git-imap-send.adoc: Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
--
2.49.0.639.ge2dd5d9d81
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v9 1/9] imap-send: fix bug causing cfg->folder being set to NULL
2025-05-30 17:32 ` [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-05-30 17:32 ` Aditya Garg
2025-05-30 17:32 ` [PATCH v9 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (7 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-30 17:32 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0.639.ge2dd5d9d81
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v9 2/9] imap-send: add support for OAuth2.0 authentication
2025-05-30 17:32 ` [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-30 17:32 ` [PATCH v9 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-05-30 17:32 ` Aditya Garg
2025-05-30 20:51 ` Eric Sunshine
2025-05-30 17:32 ` [PATCH v9 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (6 subsequent siblings)
8 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-05-30 17:32 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 47 ++++++++-
imap-send.c | 176 +++++++++++++++++++++++++++++--
3 files changed, 215 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..fef6487293 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
+ 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext LOGIN command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..08ecb1e829 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,18 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your regular password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you can generate
+an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create it.
+Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +122,35 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
+or `XOAUTH2` mechanism in your config. It is more secure than using app-specific
+passwords, and also does not enforce the need of having multi-factor authentication.
+You will have to use an OAuth2.0 access token in place of your password when using this
+authentication.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +159,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..0c7844aff2 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,66 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /* Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +959,20 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+static char *oauthbearer_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use OAUTHBEARER authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
+static char *xoauth2_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use XOAUTH2 authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -913,6 +991,46 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1104,6 +1222,36 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1553,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
+ strcmp(srvc->auth_method, "XOAUTH2") ||
+ strcmp(srvc->auth_method, "OAUTHBEARER"))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1575,21 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /* While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0.639.ge2dd5d9d81
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v9 3/9] imap-send: add PLAIN authentication method to OpenSSL
2025-05-30 17:32 ` [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-30 17:32 ` [PATCH v9 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-30 17:32 ` [PATCH v9 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-05-30 17:32 ` Aditya Garg
2025-05-30 17:32 ` [PATCH v9 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (5 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-30 17:32 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +-
imap-send.c | 80 +++++++++++++++++++++++++++++++++-
2 files changed, 81 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index fef6487293..24e88228d0 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
- 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are 'PLAIN', 'CRAM-MD5', 'OAUTHBEARER'
+ and 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
plaintext LOGIN command.
diff --git a/imap-send.c b/imap-send.c
index 0c7844aff2..c07ff98c3a 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,40 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /* Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +987,13 @@ static char *xoauth2_base64(const char *user, const char *access_token)
#else
+static char *plain_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use PLAIN authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
static char *cram(const char *challenge_64 UNUSED,
const char *user UNUSED,
const char *pass UNUSED)
@@ -975,6 +1018,26 @@ static char *xoauth2_base64(const char *user UNUSED,
#endif
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1207,7 +1270,22 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (srvc->auth_method) {
struct imap_cmd_cb cb;
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (!CAP(AUTH_PLAIN)) {
+ fprintf(stderr, "You specified "
+ "PLAIN as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* PLAIN */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_plain;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
"CRAM-MD5 as authentication method, "
--
2.49.0.639.ge2dd5d9d81
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v9 4/9] imap-send: fix memory leak in case auth_cram_md5 fails
2025-05-30 17:32 ` [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-05-30 17:32 ` [PATCH v9 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-05-30 17:32 ` Aditya Garg
2025-05-30 17:32 ` [PATCH v9 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
` (4 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-30 17:32 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index c07ff98c3a..d0c7bac030 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1046,8 +1046,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0.639.ge2dd5d9d81
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v9 5/9] imap-send: enable specifying the folder using the command line
2025-05-30 17:32 ` [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-05-30 17:32 ` [PATCH v9 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-05-30 17:32 ` Aditya Garg
2025-05-31 0:45 ` Junio C Hamano
2025-05-30 17:32 ` [PATCH v9 6/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
` (3 subsequent siblings)
8 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-05-30 17:32 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 24e88228d0..829d9e0bac 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,8 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: 'INBOX.Drafts', 'INBOX/Drafts' or
+ '[Gmail]/Drafts'. Required if `--folder` argument is not used. If
+ set and `--folder` is also used, `--folder` will be preferred.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 08ecb1e829..8f221240d0 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields 'From', 'Date', and 'Subject' in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index d0c7bac030..337f1049ca 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1762,6 +1764,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0.639.ge2dd5d9d81
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v9 6/9] imap-send: fix numerous spelling and grammar mistakes in logs
2025-05-30 17:32 ` [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-05-30 17:32 ` [PATCH v9 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-05-30 17:32 ` Aditya Garg
2025-05-30 17:32 ` [PATCH v9 7/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
` (2 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-30 17:32 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
A lot of spelling and grammar mistakes were found in the logs shown to
the user while using imap-send. Most of them are lack of a full stop at
the end of a sentence and first word of a sentence not being capitalized.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 44 ++++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 22 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 337f1049ca..d99eed0659 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -205,7 +205,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in.\n");
return -1;
}
@@ -249,9 +249,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
/* try the common name */
if (!(subj = X509_get_subject_name(cert)))
- return error("cannot get certificate subject");
+ return error("Cannot get certificate subject");
if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
- return error("cannot get certificate common name");
+ return error("Cannot get certificate common name");
if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
return 0;
return error("certificate owner '%s' does not match hostname '%s'",
@@ -905,7 +905,7 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
decoded_len = EVP_DecodeBlock((unsigned char *)challenge,
(unsigned char *)challenge_64, encoded_len);
if (decoded_len < 0)
- die("invalid challenge %s", challenge_64);
+ die("Invalid challenge %s", challenge_64);
if (!HMAC(EVP_md5(), pass, strlen(pass), (unsigned char *)challenge, decoded_len, hash, NULL))
die("HMAC error");
@@ -1050,7 +1050,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1144,12 +1144,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
tunnel.in = -1;
tunnel.out = -1;
if (start_command(&tunnel))
- die("cannot start proxy %s", srvc->tunnel);
+ die("Cannot start proxy %s", srvc->tunnel);
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1168,7 +1168,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1206,7 +1206,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1220,7 +1220,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("Error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1232,7 +1232,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1340,13 +1340,13 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN.\n",
srvc->user, srvc->host);
goto bail;
}
if (!imap->buf.sock.ssl)
imap_warn("*** IMAP Warning *** Password is being "
- "sent in the clear\n");
+ "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;
@@ -1591,12 +1591,12 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
ctx = imap_open_store(server, server->folder);
if (!ctx) {
- fprintf(stderr, "failed to open store\n");
+ fprintf(stderr, "Failed to open store.\n");
return 1;
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
@@ -1648,7 +1648,7 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
if (!uri_encoded_folder)
- die("failed to encode server folder");
+ die("Failed to encode server folder.");
strbuf_addstr(&path, uri_encoded_folder);
curl_free(uri_encoded_folder);
@@ -1704,7 +1704,7 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
@@ -1788,13 +1788,13 @@ int cmd_main(int argc, const char **argv)
server.port = server.use_ssl ? 993 : 143;
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "No IMAP store specified.\n");
ret = 1;
goto out;
}
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "No IMAP host specified.\n");
ret = 1;
goto out;
}
@@ -1803,20 +1803,20 @@ int cmd_main(int argc, const char **argv)
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
- error_errno(_("could not read from stdin"));
+ error_errno(_("Could not read from stdin."));
ret = 1;
goto out;
}
if (all_msgs.len == 0) {
- fprintf(stderr, "nothing to send\n");
+ fprintf(stderr, "Nothing to send.\n");
ret = 1;
goto out;
}
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "No messages found to send.\n");
ret = 1;
goto out;
}
--
2.49.0.639.ge2dd5d9d81
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v9 7/9] imap-send: display port alongwith host when git credential is invoked
2025-05-30 17:32 ` [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-05-30 17:32 ` [PATCH v9 6/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
@ 2025-05-30 17:32 ` Aditya Garg
2025-05-30 17:32 ` [PATCH v9 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-05-30 17:32 ` [PATCH v9 9/9] imap-send: add ability to list the available folders Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-30 17:32 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
FWIW, if no port is specified by the user, the default port, 993 for
IMAPS and 143 for IMAP is used by the code. So, the case of no port
defined for the helper is not possible, and therefore is not added.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index d99eed0659..c963ce62d8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1104,7 +1104,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0.639.ge2dd5d9d81
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v9 8/9] imap-send: display the destination mailbox when sending a message
2025-05-30 17:32 ` [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-05-30 17:32 ` [PATCH v9 7/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-05-30 17:32 ` Aditya Garg
2025-05-30 17:32 ` [PATCH v9 9/9] imap-send: add ability to list the available folders Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-30 17:32 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
Sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index c963ce62d8..95b78fda42 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1596,7 +1596,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1704,7 +1705,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0.639.ge2dd5d9d81
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v9 9/9] imap-send: add ability to list the available folders
2025-05-30 17:32 ` [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-05-30 17:32 ` [PATCH v9 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-05-30 17:32 ` Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-30 17:32 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
Various IMAP servers have different ways to name common folders.
For example, the folder where all deleted messages are stored is often
named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
on Gmail it is named "[Gmail]/Drafts".
This commit adds a `--list` command to the `imap-send` tool that lists
the available folders on the IMAP server, allowing users to see
which folders are available and how they are named. A sample output
looks like this when run against a Gmail server:
Fetching the list of available folders...
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasChildren \Noselect) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
For OpenSSL, this is achived by running the 'IMAP LIST' command and
parsing the response. This command is specified in RFC6154:
https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
For libcurl, the example code published in the libcurl documentation
is used to implement this functionality:
https://curl.se/libcurl/c/imap-list.html
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/git-imap-send.adoc | 6 +-
imap-send.c | 98 ++++++++++++++++++++++++++------
2 files changed, 87 insertions(+), 17 deletions(-)
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 8f221240d0..379a371c08 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+'git imap-send' --list
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
+--list::
+ Run the IMAP LIST command to output a list of all the folders present.
CONFIGURATION
-------------
@@ -123,7 +126,8 @@ Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
-that the "Folder doesn't exist".
+that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
+list of available folders.
[NOTE]
If your Gmail account is set to another language than English, the name of the "Drafts"
diff --git a/imap-send.c b/imap-send.c
index 95b78fda42..60562dc9b8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -45,15 +45,21 @@
#endif
static int verbosity;
+static int list_folders = 0;
static int use_curl = USE_CURL_DEFAULT;
static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
+ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
+ "git imap-send --list",
+ NULL
+};
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
+ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
OPT_END()
};
@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
if (b->buf[b->offset + 1] == '\n') {
b->buf[b->offset] = 0; /* terminate the string */
b->offset += 2; /* next line */
- if (0 < verbosity)
+ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
puts(*s);
return 0;
}
@@ -1619,6 +1625,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
+static int list_imap_folders(struct imap_server_conf *server)
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
+ fprintf(stderr, "Failed to connect to IMAP server.\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
+ fprintf(stderr, "Failed to list folders.\n");
+ imap_close_store(ctx);
+ return 1;
+ }
+
+ imap_close_store(ctx);
+ return 0;
+}
+
#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
@@ -1647,11 +1673,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
- die("Failed to encode server folder.");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+ die("Failed to encode server folder.");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ -1681,10 +1709,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
-
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(curl);
@@ -1703,6 +1727,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
struct credential cred = CREDENTIAL_INIT;
curl = setup_curl(server, &cred);
+
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
fprintf(stderr, "Sending %d message%s to %s folder...\n",
@@ -1749,6 +1777,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
+
+static int curl_list_imap_folders(struct imap_server_conf *server)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct credential cred = CREDENTIAL_INIT;
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ curl = setup_curl(server, &cred);
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ if (cred.username) {
+ if (res == CURLE_OK)
+ credential_approve(the_repository, &cred);
+ else if (res == CURLE_LOGIN_DENIED)
+ credential_reject(the_repository, &cred);
+ }
+
+ credential_clear(&cred);
+
+ return res != CURLE_OK;
+}
#endif
int cmd_main(int argc, const char **argv)
@@ -1789,11 +1842,6 @@ int cmd_main(int argc, const char **argv)
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
- fprintf(stderr, "No IMAP store specified.\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "No IMAP host specified.\n");
@@ -1803,6 +1851,24 @@ int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
+ if (list_folders) {
+ if (server.tunnel)
+ ret = list_imap_folders(&server);
+#ifdef USE_CURL_FOR_IMAP_SEND
+ else if (use_curl)
+ ret = curl_list_imap_folders(&server);
+#endif
+ else
+ ret = list_imap_folders(&server);
+ goto out;
+ }
+
+ if (!server.folder) {
+ fprintf(stderr, "No IMAP store specified.\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("Could not read from stdin."));
--
2.49.0.639.ge2dd5d9d81
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v9 2/9] imap-send: add support for OAuth2.0 authentication
2025-05-30 17:32 ` [PATCH v9 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-05-30 20:51 ` Eric Sunshine
2025-05-30 21:12 ` Junio C Hamano
0 siblings, 1 reply; 248+ messages in thread
From: Eric Sunshine @ 2025-05-30 20:51 UTC (permalink / raw)
To: Aditya Garg
Cc: Junio C Hamano, git, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
On Fri, May 30, 2025 at 1:32 PM Aditya Garg <gargaditya08@live.com> wrote:
> OAuth2.0 is a new way of authentication supported by various email providers
> these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
> for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
> XOAUTH2 is Google's proprietary mechanism (See [3]).
>
> [1]: https://datatracker.ietf.org/doc/html/rfc5801
> [2]: https://datatracker.ietf.org/doc/html/rfc7628
> [3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
>
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
Not a proper review, just something I spotted several versions back
but assumed that someone else -- providing a proper review -- would
mention...
> diff --git a/imap-send.c b/imap-send.c
> @@ -885,6 +889,66 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
> +static char *oauthbearer_base64(const char *user, const char *access_token)
> +{
> + /* Compose the OAUTHBEARER string
> + *
> + * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
> + *
> + * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
> + * * gs2-cb-flag `n` -> client does not support CB
> + * * gs2-authzid `a=" {User} "`
> + *
> + * The second part are key value pairs containing host, port and auth as
> + * described in RFC7628.
> + *
> + * https://datatracker.ietf.org/doc/html/rfc5801
> + * https://datatracker.ietf.org/doc/html/rfc7628
> + */
On this project, multi-line comments are formatted like this:
/*
* Line 1
* Line 2
* ...
*/
The same observation applies to other parts of this patch, as well.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v9 2/9] imap-send: add support for OAuth2.0 authentication
2025-05-30 20:51 ` Eric Sunshine
@ 2025-05-30 21:12 ` Junio C Hamano
2025-05-31 9:06 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-05-30 21:12 UTC (permalink / raw)
To: Eric Sunshine
Cc: Aditya Garg, git, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
Eric Sunshine <sunshine@sunshineco.com> writes:
> Not a proper review, just something I spotted several versions back
> but assumed that someone else -- providing a proper review -- would
> mention...
I suspect that we weren't ready to prifvide "a proper review" yet on
this series at the coding style level, while the design at a bit
higher level, like "should choice of openssl/curl be runtime?", was
discussed.
> On this project, multi-line comments are formatted like this:
>
> /*
> * Line 1
> * Line 2
> * ...
> */
>
> The same observation applies to other parts of this patch, as well.
Thanks.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v9 5/9] imap-send: enable specifying the folder using the command line
2025-05-30 17:32 ` [PATCH v9 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-05-31 0:45 ` Junio C Hamano
2025-05-31 9:16 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-05-31 0:45 UTC (permalink / raw)
To: Aditya Garg
Cc: git, Eric Sunshine, Zi Yao, sandals, Jeff King, Ben Knoble,
Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> Some users may very often want to imap-send messages to a folder
> other than the default set in the config. Add a command line
> argument for the same.
>
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
> ---
> Documentation/config/imap.adoc | 5 +++--
> Documentation/git-imap-send.adoc | 15 +++++++++++----
> imap-send.c | 9 ++++++++-
> 3 files changed, 22 insertions(+), 7 deletions(-)
Did you forget to adjust tests that expect the traditional messages?
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v9 2/9] imap-send: add support for OAuth2.0 authentication
2025-05-30 21:12 ` Junio C Hamano
@ 2025-05-31 9:06 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-05-31 9:06 UTC (permalink / raw)
To: Junio C Hamano
Cc: Eric Sunshine, git@vger.kernel.org, Zi Yao,
sandals@crustytoothpaste.net, Jeff King, Ben Knoble, Phillip Wood
> On 31 May 2025, at 2:42 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Eric Sunshine <sunshine@sunshineco.com> writes:
>
>> Not a proper review, just something I spotted several versions back
>> but assumed that someone else -- providing a proper review -- would
>> mention...
>
> I suspect that we weren't ready to prifvide "a proper review" yet on
> this series at the coding style level, while the design at a bit
> higher level, like "should choice of openssl/curl be runtime?", was
> discussed.
I think that has been made clear. Just leave it as it is right now and
drop the patch allowing user to set it using the config.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v9 5/9] imap-send: enable specifying the folder using the command line
2025-05-31 0:45 ` Junio C Hamano
@ 2025-05-31 9:16 ` Aditya Garg
2025-06-01 2:40 ` Junio C Hamano
0 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-05-31 9:16 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao,
sandals@crustytoothpaste.net, Jeff King, Ben Knoble, Phillip Wood
> On 31 May 2025, at 6:15 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>> Some users may very often want to imap-send messages to a folder
>> other than the default set in the config. Add a command line
>> argument for the same.
>>
>> Signed-off-by: Aditya Garg <gargaditya08@live.com>
>> ---
>> Documentation/config/imap.adoc | 5 +++--
>> Documentation/git-imap-send.adoc | 15 +++++++++++----
>> imap-send.c | 9 ++++++++-
>> 3 files changed, 22 insertions(+), 7 deletions(-)
>
> Did you forget to adjust tests that expect the traditional messages?
I am not sure what you mean here. Could be more specific?
In any case, whatever folder is passed using the --folder argument,
will be treated the same way as the same folder would be when set
using the config.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v9 5/9] imap-send: enable specifying the folder using the command line
2025-05-31 9:16 ` Aditya Garg
@ 2025-06-01 2:40 ` Junio C Hamano
2025-06-01 4:41 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-01 2:40 UTC (permalink / raw)
To: Aditya Garg
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao,
sandals@crustytoothpaste.net, Jeff King, Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
>> On 31 May 2025, at 6:15 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Aditya Garg <gargaditya08@live.com> writes:
>>
>>> Some users may very often want to imap-send messages to a folder
>>> other than the default set in the config. Add a command line
>>> argument for the same.
>>>
>>> Signed-off-by: Aditya Garg <gargaditya08@live.com>
>>> ---
>>> Documentation/config/imap.adoc | 5 +++--
>>> Documentation/git-imap-send.adoc | 15 +++++++++++----
>>> imap-send.c | 9 ++++++++-
>>> 3 files changed, 22 insertions(+), 7 deletions(-)
>>
>> Did you forget to adjust tests that expect the traditional messages?
>
> I am not sure what you mean here. Could be more specific?
>
> In any case, whatever folder is passed using the --folder argument,
> will be treated the same way as the same folder would be when set
> using the config.
Ah, not this step, but if you ran
$ make test
you will see what I meant. It failed during one of my integration
run.
Please make it a habit to always do so, if you haven't already,
before sending your patches.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v9 5/9] imap-send: enable specifying the folder using the command line
2025-06-01 2:40 ` Junio C Hamano
@ 2025-06-01 4:41 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 4:41 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao,
sandals@crustytoothpaste.net, Jeff King, Ben Knoble, Phillip Wood
> On 1 Jun 2025, at 8:10 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>>>> On 31 May 2025, at 6:15 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>>
>>> Aditya Garg <gargaditya08@live.com> writes:
>>>
>>>> Some users may very often want to imap-send messages to a folder
>>>> other than the default set in the config. Add a command line
>>>> argument for the same.
>>>>
>>>> Signed-off-by: Aditya Garg <gargaditya08@live.com>
>>>> ---
>>>> Documentation/config/imap.adoc | 5 +++--
>>>> Documentation/git-imap-send.adoc | 15 +++++++++++----
>>>> imap-send.c | 9 ++++++++-
>>>> 3 files changed, 22 insertions(+), 7 deletions(-)
>>>
>>> Did you forget to adjust tests that expect the traditional messages?
>>
>> I am not sure what you mean here. Could be more specific?
>>
>> In any case, whatever folder is passed using the --folder argument,
>> will be treated the same way as the same folder would be when set
>> using the config.
>
> Ah, not this step, but if you ran
>
> $ make test
>
> you will see what I meant. It failed during one of my integration
> run.
>
> Please make it a habit to always do so, if you haven't already,
> before sending your patches.
Oh ok.
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (13 preceding siblings ...)
2025-05-30 17:32 ` [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-01 7:10 ` Aditya Garg
2025-06-01 7:10 ` [PATCH v10 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (8 more replies)
2025-06-01 8:38 ` [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (8 subsequent siblings)
23 siblings, 9 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 7:10 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch series does the following things:
Firstly it basically makes the imap-send command usable again since it
was broken because of not being able to correctly parse the config file.
Further it adds support for OAuth2.0 and PLAIN authentication to git
imap-send.
Last, it does some minor improvements including adding the ability to
specify the folder using the command line and ability to list the
available folders by adding a `--list` option.
P.S.: I am surprised this thing even exists xD.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
v8: - Drop the patch that enabled user to choose between libcurl and openssl using the config
- Add ability to list the available folders by adding a `--list` option
v9: - Encourage users to use OAuth2.0 for Gmail (similar change done for send-email docs).
v10: - Fix comment styles
- Fix failing tests
Aditya Garg (9):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: enable specifying the folder using the command line
imap-send: fix numerous spelling and grammar mistakes in logs
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
imap-send: add ability to list the available folders
Documentation/config/imap.adoc | 10 +-
Documentation/git-imap-send.adoc | 68 ++++-
imap-send.c | 421 +++++++++++++++++++++++++++----
t/t1517-outside-repo.sh | 2 +-
4 files changed, 437 insertions(+), 64 deletions(-)
Range-diff against v9:
-: ---------- > 1: 3e3ddf7077 imap-send: fix bug causing cfg->folder being set to NULL
1: c5ee87051f ! 2: 02037873a1 imap-send: add support for OAuth2.0 authentication
@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const
+ int raw_len, b64_len;
+ char *raw, *b64;
+
-+ /* Compose the OAUTHBEARER string
++ /*
++ * Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const
+ int raw_len, b64_len;
+ char *raw, *b64;
+
-+ /* Compose the XOAUTH2 string
++ /*
++ * Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct crede
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
-+ /* While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
++ /*
++ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
2: 17e263ea27 ! 3: 3a0be43838 imap-send: add PLAIN authentication method to OpenSSL
@@ imap-send.c: static char hexchar(unsigned int b)
+ int b64_len;
+ char *raw, *b64;
+
-+ /* Compose the PLAIN string
++ /*
++ * Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
3: 5c471f640b = 4: 45f5b3f1ff imap-send: fix memory leak in case auth_cram_md5 fails
4: db8ee71785 = 5: 8899f686d7 imap-send: enable specifying the folder using the command line
5: a8fbcdf9d5 ! 6: 991f978c22 imap-send: fix numerous spelling and grammar mistakes in logs
@@ imap-send.c: int cmd_main(int argc, const char **argv)
ret = 1;
goto out;
}
+
+ ## t/t1517-outside-repo.sh ##
+@@ t/t1517-outside-repo.sh: test_expect_success 'imap-send outside repository' '
+ test_config_global imap.host imaps://localhost &&
+ test_config_global imap.folder Drafts &&
+
+- echo nothing to send >expect &&
++ echo Nothing to send. >expect &&
+ test_must_fail git imap-send -v </dev/null 2>actual &&
+ test_cmp expect actual &&
+
6: a5dad0f2b2 = 7: e436a12198 imap-send: display port alongwith host when git credential is invoked
7: d2569a5e36 = 8: 5183253004 imap-send: display the destination mailbox when sending a message
8: cf844b2632 = 9: c33469a5db imap-send: add ability to list the available folders
--
2.49.0.638.g67a2d115ec
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v10 1/9] imap-send: fix bug causing cfg->folder being set to NULL
2025-06-01 7:10 ` [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-01 7:10 ` Aditya Garg
2025-06-01 7:10 ` [PATCH v10 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (7 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 7:10 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0.638.g67a2d115ec
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v10 2/9] imap-send: add support for OAuth2.0 authentication
2025-06-01 7:10 ` [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-01 7:10 ` [PATCH v10 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-06-01 7:10 ` Aditya Garg
2025-06-01 7:10 ` [PATCH v10 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (6 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 7:10 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 47 +++++++-
imap-send.c | 179 +++++++++++++++++++++++++++++--
3 files changed, 218 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..fef6487293 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
+ 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext LOGIN command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..08ecb1e829 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,18 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your regular password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you can generate
+an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create it.
+Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +122,35 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
+or `XOAUTH2` mechanism in your config. It is more secure than using app-specific
+passwords, and also does not enforce the need of having multi-factor authentication.
+You will have to use an OAuth2.0 access token in place of your password when using this
+authentication.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +159,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..4f3a1fb5b1 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,68 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +961,20 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+static char *oauthbearer_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use OAUTHBEARER authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
+static char *xoauth2_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use XOAUTH2 authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -913,6 +993,46 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1104,6 +1224,36 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1555,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
+ strcmp(srvc->auth_method, "XOAUTH2") ||
+ strcmp(srvc->auth_method, "OAUTHBEARER"))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1577,22 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /*
+ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0.638.g67a2d115ec
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v10 3/9] imap-send: add PLAIN authentication method to OpenSSL
2025-06-01 7:10 ` [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-01 7:10 ` [PATCH v10 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-01 7:10 ` [PATCH v10 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-01 7:10 ` Aditya Garg
2025-06-01 7:10 ` [PATCH v10 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (5 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 7:10 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +-
imap-send.c | 81 +++++++++++++++++++++++++++++++++-
2 files changed, 82 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index fef6487293..24e88228d0 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
- 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are 'PLAIN', 'CRAM-MD5', 'OAUTHBEARER'
+ and 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
plaintext LOGIN command.
diff --git a/imap-send.c b/imap-send.c
index 4f3a1fb5b1..bc26abd150 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,41 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -953,6 +990,13 @@ static char *xoauth2_base64(const char *user, const char *access_token)
#else
+static char *plain_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use PLAIN authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
static char *cram(const char *challenge_64 UNUSED,
const char *user UNUSED,
const char *pass UNUSED)
@@ -977,6 +1021,26 @@ static char *xoauth2_base64(const char *user UNUSED,
#endif
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1209,7 +1273,22 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (srvc->auth_method) {
struct imap_cmd_cb cb;
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (!CAP(AUTH_PLAIN)) {
+ fprintf(stderr, "You specified "
+ "PLAIN as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* PLAIN */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_plain;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
"CRAM-MD5 as authentication method, "
--
2.49.0.638.g67a2d115ec
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v10 4/9] imap-send: fix memory leak in case auth_cram_md5 fails
2025-06-01 7:10 ` [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-06-01 7:10 ` [PATCH v10 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-06-01 7:10 ` Aditya Garg
2025-06-01 7:10 ` [PATCH v10 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
` (4 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 7:10 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index bc26abd150..e169c5e919 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1049,8 +1049,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0.638.g67a2d115ec
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v10 5/9] imap-send: enable specifying the folder using the command line
2025-06-01 7:10 ` [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-06-01 7:10 ` [PATCH v10 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-06-01 7:10 ` Aditya Garg
2025-06-01 7:10 ` [PATCH v10 6/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
` (3 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 7:10 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 24e88228d0..829d9e0bac 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,8 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: 'INBOX.Drafts', 'INBOX/Drafts' or
+ '[Gmail]/Drafts'. Required if `--folder` argument is not used. If
+ set and `--folder` is also used, `--folder` will be preferred.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 08ecb1e829..8f221240d0 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields 'From', 'Date', and 'Subject' in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index e169c5e919..cfa335b647 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1766,6 +1768,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0.638.g67a2d115ec
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v10 6/9] imap-send: fix numerous spelling and grammar mistakes in logs
2025-06-01 7:10 ` [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-06-01 7:10 ` [PATCH v10 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-06-01 7:10 ` Aditya Garg
2025-06-01 7:28 ` Eric Sunshine
2025-06-01 7:10 ` [PATCH v10 7/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
` (2 subsequent siblings)
8 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 7:10 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
A lot of spelling and grammar mistakes were found in the logs shown to
the user while using imap-send. Most of them are lack of a full stop at
the end of a sentence and first word of a sentence not being capitalized.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 44 ++++++++++++++++++++---------------------
t/t1517-outside-repo.sh | 2 +-
2 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index cfa335b647..d791cbff43 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -205,7 +205,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in.\n");
return -1;
}
@@ -249,9 +249,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
/* try the common name */
if (!(subj = X509_get_subject_name(cert)))
- return error("cannot get certificate subject");
+ return error("Cannot get certificate subject");
if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
- return error("cannot get certificate common name");
+ return error("Cannot get certificate common name");
if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
return 0;
return error("certificate owner '%s' does not match hostname '%s'",
@@ -906,7 +906,7 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
decoded_len = EVP_DecodeBlock((unsigned char *)challenge,
(unsigned char *)challenge_64, encoded_len);
if (decoded_len < 0)
- die("invalid challenge %s", challenge_64);
+ die("Invalid challenge %s", challenge_64);
if (!HMAC(EVP_md5(), pass, strlen(pass), (unsigned char *)challenge, decoded_len, hash, NULL))
die("HMAC error");
@@ -1053,7 +1053,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1147,12 +1147,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
tunnel.in = -1;
tunnel.out = -1;
if (start_command(&tunnel))
- die("cannot start proxy %s", srvc->tunnel);
+ die("Cannot start proxy %s", srvc->tunnel);
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1171,7 +1171,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1209,7 +1209,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1223,7 +1223,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("Error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1235,7 +1235,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1343,13 +1343,13 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN.\n",
srvc->user, srvc->host);
goto bail;
}
if (!imap->buf.sock.ssl)
imap_warn("*** IMAP Warning *** Password is being "
- "sent in the clear\n");
+ "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;
@@ -1594,12 +1594,12 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
ctx = imap_open_store(server, server->folder);
if (!ctx) {
- fprintf(stderr, "failed to open store\n");
+ fprintf(stderr, "Failed to open store.\n");
return 1;
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
@@ -1651,7 +1651,7 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
if (!uri_encoded_folder)
- die("failed to encode server folder");
+ die("Failed to encode server folder.");
strbuf_addstr(&path, uri_encoded_folder);
curl_free(uri_encoded_folder);
@@ -1708,7 +1708,7 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
@@ -1792,13 +1792,13 @@ int cmd_main(int argc, const char **argv)
server.port = server.use_ssl ? 993 : 143;
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "No IMAP store specified.\n");
ret = 1;
goto out;
}
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "No IMAP host specified.\n");
ret = 1;
goto out;
}
@@ -1807,20 +1807,20 @@ int cmd_main(int argc, const char **argv)
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
- error_errno(_("could not read from stdin"));
+ error_errno(_("Could not read from stdin."));
ret = 1;
goto out;
}
if (all_msgs.len == 0) {
- fprintf(stderr, "nothing to send\n");
+ fprintf(stderr, "Nothing to send.\n");
ret = 1;
goto out;
}
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "No messages found to send.\n");
ret = 1;
goto out;
}
diff --git a/t/t1517-outside-repo.sh b/t/t1517-outside-repo.sh
index 6824581317..bc6e79613f 100755
--- a/t/t1517-outside-repo.sh
+++ b/t/t1517-outside-repo.sh
@@ -59,7 +59,7 @@ test_expect_success 'imap-send outside repository' '
test_config_global imap.host imaps://localhost &&
test_config_global imap.folder Drafts &&
- echo nothing to send >expect &&
+ echo Nothing to send. >expect &&
test_must_fail git imap-send -v </dev/null 2>actual &&
test_cmp expect actual &&
--
2.49.0.638.g67a2d115ec
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v10 7/9] imap-send: display port alongwith host when git credential is invoked
2025-06-01 7:10 ` [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-06-01 7:10 ` [PATCH v10 6/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
@ 2025-06-01 7:10 ` Aditya Garg
2025-06-01 7:10 ` [PATCH v10 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-01 7:10 ` [PATCH v10 9/9] imap-send: add ability to list the available folders Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 7:10 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
FWIW, if no port is specified by the user, the default port, 993 for
IMAPS and 143 for IMAP is used by the code. So, the case of no port
defined for the helper is not possible, and therefore is not added.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index d791cbff43..3ffe3ae5cc 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1107,7 +1107,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0.638.g67a2d115ec
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v10 8/9] imap-send: display the destination mailbox when sending a message
2025-06-01 7:10 ` [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-06-01 7:10 ` [PATCH v10 7/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-06-01 7:10 ` Aditya Garg
2025-06-01 7:10 ` [PATCH v10 9/9] imap-send: add ability to list the available folders Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 7:10 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
Sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 3ffe3ae5cc..86d46395de 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1599,7 +1599,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1708,7 +1709,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0.638.g67a2d115ec
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v10 9/9] imap-send: add ability to list the available folders
2025-06-01 7:10 ` [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-06-01 7:10 ` [PATCH v10 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-06-01 7:10 ` Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 7:10 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Various IMAP servers have different ways to name common folders.
For example, the folder where all deleted messages are stored is often
named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
on Gmail it is named "[Gmail]/Drafts".
This commit adds a `--list` command to the `imap-send` tool that lists
the available folders on the IMAP server, allowing users to see
which folders are available and how they are named. A sample output
looks like this when run against a Gmail server:
Fetching the list of available folders...
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasChildren \Noselect) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
For OpenSSL, this is achived by running the 'IMAP LIST' command and
parsing the response. This command is specified in RFC6154:
https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
For libcurl, the example code published in the libcurl documentation
is used to implement this functionality:
https://curl.se/libcurl/c/imap-list.html
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/git-imap-send.adoc | 6 +-
imap-send.c | 98 ++++++++++++++++++++++++++------
2 files changed, 87 insertions(+), 17 deletions(-)
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 8f221240d0..379a371c08 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+'git imap-send' --list
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
+--list::
+ Run the IMAP LIST command to output a list of all the folders present.
CONFIGURATION
-------------
@@ -123,7 +126,8 @@ Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
-that the "Folder doesn't exist".
+that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
+list of available folders.
[NOTE]
If your Gmail account is set to another language than English, the name of the "Drafts"
diff --git a/imap-send.c b/imap-send.c
index 86d46395de..4a7130c20b 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -45,15 +45,21 @@
#endif
static int verbosity;
+static int list_folders = 0;
static int use_curl = USE_CURL_DEFAULT;
static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
+ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
+ "git imap-send --list",
+ NULL
+};
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
+ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
OPT_END()
};
@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
if (b->buf[b->offset + 1] == '\n') {
b->buf[b->offset] = 0; /* terminate the string */
b->offset += 2; /* next line */
- if (0 < verbosity)
+ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
puts(*s);
return 0;
}
@@ -1622,6 +1628,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
+static int list_imap_folders(struct imap_server_conf *server)
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
+ fprintf(stderr, "Failed to connect to IMAP server.\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
+ fprintf(stderr, "Failed to list folders.\n");
+ imap_close_store(ctx);
+ return 1;
+ }
+
+ imap_close_store(ctx);
+ return 0;
+}
+
#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
@@ -1650,11 +1676,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
- die("Failed to encode server folder.");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+ die("Failed to encode server folder.");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ -1685,10 +1713,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
-
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(curl);
@@ -1707,6 +1731,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
struct credential cred = CREDENTIAL_INIT;
curl = setup_curl(server, &cred);
+
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
fprintf(stderr, "Sending %d message%s to %s folder...\n",
@@ -1753,6 +1781,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
+
+static int curl_list_imap_folders(struct imap_server_conf *server)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct credential cred = CREDENTIAL_INIT;
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ curl = setup_curl(server, &cred);
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ if (cred.username) {
+ if (res == CURLE_OK)
+ credential_approve(the_repository, &cred);
+ else if (res == CURLE_LOGIN_DENIED)
+ credential_reject(the_repository, &cred);
+ }
+
+ credential_clear(&cred);
+
+ return res != CURLE_OK;
+}
#endif
int cmd_main(int argc, const char **argv)
@@ -1793,11 +1846,6 @@ int cmd_main(int argc, const char **argv)
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
- fprintf(stderr, "No IMAP store specified.\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "No IMAP host specified.\n");
@@ -1807,6 +1855,24 @@ int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
+ if (list_folders) {
+ if (server.tunnel)
+ ret = list_imap_folders(&server);
+#ifdef USE_CURL_FOR_IMAP_SEND
+ else if (use_curl)
+ ret = curl_list_imap_folders(&server);
+#endif
+ else
+ ret = list_imap_folders(&server);
+ goto out;
+ }
+
+ if (!server.folder) {
+ fprintf(stderr, "No IMAP store specified.\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("Could not read from stdin."));
--
2.49.0.638.g67a2d115ec
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v10 6/9] imap-send: fix numerous spelling and grammar mistakes in logs
2025-06-01 7:10 ` [PATCH v10 6/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
@ 2025-06-01 7:28 ` Eric Sunshine
2025-06-01 7:30 ` Aditya Garg
2025-06-01 7:32 ` Aditya Garg
0 siblings, 2 replies; 248+ messages in thread
From: Eric Sunshine @ 2025-06-01 7:28 UTC (permalink / raw)
To: Aditya Garg
Cc: Junio C Hamano, git, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble, Phillip Wood
On Sun, Jun 1, 2025 at 3:12 AM Aditya Garg <gargaditya08@live.com> wrote:
> A lot of spelling and grammar mistakes were found in the logs shown to
> the user while using imap-send. Most of them are lack of a full stop at
> the end of a sentence and first word of a sentence not being capitalized.
>
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
> ---
> diff --git a/imap-send.c b/imap-send.c
> @@ -249,9 +249,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
> if (!(subj = X509_get_subject_name(cert)))
> - return error("cannot get certificate subject");
> + return error("Cannot get certificate subject");
> if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
> - return error("cannot get certificate common name");
> + return error("Cannot get certificate common name");
This patch generally seems to be taking the code in a direction
opposite the rest of the project. Quoting from
Documentation/CodingGuidelines:
Error Messages
- Do not end a single-sentence error message with a full stop.
- Do not capitalize the first word, only because it is the first
word in the message ("unable to open '%s'", not "Unable to open
'%s'"). But "SHA-3 not supported" is fine, because the reason the
first word is capitalized is not because it is at the beginning
of the sentence, but because the word would be spelled in capital
letters even when it appeared in the middle of the sentence.
> @@ -1053,7 +1053,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
> if (ret != strlen(response)) {
> free(response);
> - return error("IMAP error: sending response failed");
> + return error("IMAP error: sending CRAM-MD5 response failed");
> }
Providing more context in the error message, as done here, seems welcome.
> @@ -1223,7 +1223,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
> - fputs("Error: unable to connect to server.\n", stderr);
> + fputs("Error: unable to connect to server\n", stderr);
> @@ -1343,13 +1343,13 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
> - fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
> + fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN.\n",
> @@ -1594,12 +1594,12 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
> - fprintf(stderr, "failed to open store\n");
> + fprintf(stderr, "Failed to open store.\n");
Taking the above guidelines into account, these probably ought to be
changed to start with lowercase.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v10 6/9] imap-send: fix numerous spelling and grammar mistakes in logs
2025-06-01 7:28 ` Eric Sunshine
@ 2025-06-01 7:30 ` Aditya Garg
2025-06-01 7:32 ` Aditya Garg
1 sibling, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 7:30 UTC (permalink / raw)
To: Eric Sunshine
Cc: Junio C Hamano, git, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble, Phillip Wood
On 01/06/25 12:58 pm, Eric Sunshine wrote:
> On Sun, Jun 1, 2025 at 3:12 AM Aditya Garg <gargaditya08@live.com> wrote:
>> A lot of spelling and grammar mistakes were found in the logs shown to
>> the user while using imap-send. Most of them are lack of a full stop at
>> the end of a sentence and first word of a sentence not being capitalized.
>>
>> Signed-off-by: Aditya Garg <gargaditya08@live.com>
>> ---
>> diff --git a/imap-send.c b/imap-send.c
>> @@ -249,9 +249,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
>> if (!(subj = X509_get_subject_name(cert)))
>> - return error("cannot get certificate subject");
>> + return error("Cannot get certificate subject");
>> if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
>> - return error("cannot get certificate common name");
>> + return error("Cannot get certificate common name");
>
> This patch generally seems to be taking the code in a direction
> opposite the rest of the project. Quoting from
> Documentation/CodingGuidelines:
>
Lets drop this patch itself
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v10 6/9] imap-send: fix numerous spelling and grammar mistakes in logs
2025-06-01 7:28 ` Eric Sunshine
2025-06-01 7:30 ` Aditya Garg
@ 2025-06-01 7:32 ` Aditya Garg
1 sibling, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 7:32 UTC (permalink / raw)
To: Eric Sunshine
Cc: Junio C Hamano, git, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble, Phillip Wood
On 01/06/25 12:58 pm, Eric Sunshine wrote:
> On Sun, Jun 1, 2025 at 3:12 AM Aditya Garg <gargaditya08@live.com> wrote:
>> A lot of spelling and grammar mistakes were found in the logs shown to
>> the user while using imap-send. Most of them are lack of a full stop at
>> the end of a sentence and first word of a sentence not being capitalized.
>>
>> Signed-off-by: Aditya Garg <gargaditya08@live.com>
>> ---
>> diff --git a/imap-send.c b/imap-send.c
>> @@ -249,9 +249,9 @@ static int verify_hostname(X509 *cert, const char *hostname)
>> if (!(subj = X509_get_subject_name(cert)))
>> - return error("cannot get certificate subject");
>> + return error("Cannot get certificate subject");
>> if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
>> - return error("cannot get certificate common name");
>> + return error("Cannot get certificate common name");
>
> This patch generally seems to be taking the code in a direction
> opposite the rest of the project. Quoting from
> Documentation/CodingGuidelines:
>
> Error Messages
>
> - Do not end a single-sentence error message with a full stop.
>
> - Do not capitalize the first word, only because it is the first
> word in the message ("unable to open '%s'", not "Unable to open
> '%s'"). But "SHA-3 not supported" is fine, because the reason the
> first word is capitalized is not because it is at the beginning
> of the sentence, but because the word would be spelled in capital
> letters even when it appeared in the middle of the sentence.
>
>> @@ -1053,7 +1053,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
>> if (ret != strlen(response)) {
>> free(response);
>> - return error("IMAP error: sending response failed");
>> + return error("IMAP error: sending CRAM-MD5 response failed");
>> }
>
> Providing more context in the error message, as done here, seems welcome.
Hmm, ok
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (14 preceding siblings ...)
2025-06-01 7:10 ` [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-01 8:38 ` Aditya Garg
2025-06-01 8:38 ` [PATCH v11 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (8 more replies)
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 subsequent siblings)
23 siblings, 9 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 8:38 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch series does the following things:
Firstly it basically makes the imap-send command usable again since it
was broken because of not being able to correctly parse the config file.
Further it adds support for OAuth2.0 and PLAIN authentication to git
imap-send.
Last, it does some minor improvements including adding the ability to
specify the folder using the command line and ability to list the
available folders by adding a `--list` option.
P.S.: I am surprised this thing even exists xD.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
v8: - Drop the patch that enabled user to choose between libcurl and openssl using the config
- Add ability to list the available folders by adding a `--list` option
v9: - Encourage users to use OAuth2.0 for Gmail (similar change done for send-email docs).
v10: - Fix comment styles
- Fix failing tests
v11: - Use lower case letters for the first word of a sendtence in an error message
and avoid using full stops at the end of a sentence.
Aditya Garg (9):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: enable specifying the folder using the command line
imap-send: fix minor mistakes in the logs
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
imap-send: add ability to list the available folders
Documentation/config/imap.adoc | 10 +-
Documentation/git-imap-send.adoc | 68 +++++-
imap-send.c | 407 +++++++++++++++++++++++++++----
3 files changed, 429 insertions(+), 56 deletions(-)
Range-diff against v10:
-: ---------- > 1: 3e3ddf7077 imap-send: fix bug causing cfg->folder being set to NULL
1: 991f978c22 ! 2: 02037873a1 imap-send: fix numerous spelling and grammar mistakes in logs
@@ Metadata
Author: Aditya Garg <gargaditya08@live.com>
## Commit message ##
- imap-send: fix numerous spelling and grammar mistakes in logs
+ imap-send: add support for OAuth2.0 authentication
- A lot of spelling and grammar mistakes were found in the logs shown to
- the user while using imap-send. Most of them are lack of a full stop at
- the end of a sentence and first word of a sentence not being capitalized.
+ OAuth2.0 is a new way of authentication supported by various email providers
+ these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
+ for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
+ XOAUTH2 is Google's proprietary mechanism (See [3]).
+
+ [1]: https://datatracker.ietf.org/doc/html/rfc5801
+ [2]: https://datatracker.ietf.org/doc/html/rfc7628
+ [3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
+ ## Documentation/config/imap.adoc ##
+@@ Documentation/config/imap.adoc: imap.authMethod::
+ Specify the authentication method for authenticating with the IMAP server.
+ If Git was built with the NO_CURL option, or if your curl version is older
+ than 7.34.0, or if you're running git-imap-send with the `--no-curl`
+- option, the only supported method is 'CRAM-MD5'. If this is not set
+- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
++ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
++ 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
++ plaintext LOGIN command.
+
+ ## Documentation/git-imap-send.adoc ##
+@@ Documentation/git-imap-send.adoc: Using Gmail's IMAP interface:
+
+ ---------
+ [imap]
+- folder = "[Gmail]/Drafts"
+- host = imaps://imap.gmail.com
+- user = user@gmail.com
+- port = 993
++ folder = "[Gmail]/Drafts"
++ host = imaps://imap.gmail.com
++ user = user@gmail.com
++ port = 993
+ ---------
+
++Gmail does not allow using your regular password for `git imap-send`.
++If you have multi-factor authentication set up on your Gmail account, you can generate
++an app-specific password for use with `git imap-send`.
++Visit https://security.google.com/settings/security/apppasswords to create it.
++Alternatively, use OAuth2.0 authentication as described below.
++
+ [NOTE]
+ You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
+ that the "Folder doesn't exist".
+@@ Documentation/git-imap-send.adoc: that the "Folder doesn't exist".
+ If your Gmail account is set to another language than English, the name of the "Drafts"
+ folder will be localized.
+
++If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
++or `XOAUTH2` mechanism in your config. It is more secure than using app-specific
++passwords, and also does not enforce the need of having multi-factor authentication.
++You will have to use an OAuth2.0 access token in place of your password when using this
++authentication.
++
++---------
++[imap]
++ folder = "[Gmail]/Drafts"
++ host = imaps://imap.gmail.com
++ user = user@gmail.com
++ port = 993
++ authmethod = OAUTHBEARER
++---------
++
++Using Outlook's IMAP interface:
++
++Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
++supports only `XOAUTH2` as the mechanism.
++
++---------
++[imap]
++ folder = "Drafts"
++ host = imaps://outlook.office365.com
++ user = user@outlook.com
++ port = 993
++ authmethod = XOAUTH2
++---------
++
+ Once the commits are ready to be sent, run the following command:
+
+ $ git format-patch --cover-letter -M --stdout origin/master | git imap-send
+@@ Documentation/git-imap-send.adoc: Just make sure to disable line wrapping in the email client (Gmail's web
+ interface will wrap lines no matter what, so you need to use a real
+ IMAP client).
+
++In case you are using OAuth2.0 authentication, it is easier to use credential
++helpers to generate tokens. Credential helpers suggested in
++linkgit:git-send-email[1] can be used for `git imap-send` as well.
++
+ CAUTION
+ -------
+ It is still your responsibility to make sure that the email message
+
## imap-send.c ##
-@@ imap-send.c: static int ssl_socket_connect(struct imap_socket *sock UNUSED,
- const struct imap_server_conf *cfg UNUSED,
- int use_tls_only UNUSED)
- {
-- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
-+ fprintf(stderr, "SSL requested, but SSL support is not compiled in.\n");
- return -1;
- }
+@@ imap-send.c: enum CAPABILITY {
+ LITERALPLUS,
+ NAMESPACE,
+ STARTTLS,
+- AUTH_CRAM_MD5
++ AUTH_CRAM_MD5,
++ AUTH_OAUTHBEARER,
++ AUTH_XOAUTH2
+ };
-@@ imap-send.c: static int verify_hostname(X509 *cert, const char *hostname)
-
- /* try the common name */
- if (!(subj = X509_get_subject_name(cert)))
-- return error("cannot get certificate subject");
-+ return error("Cannot get certificate subject");
- if ((len = X509_NAME_get_text_by_NID(subj, NID_commonName, cname, sizeof(cname))) < 0)
-- return error("cannot get certificate common name");
-+ return error("Cannot get certificate common name");
- if (strlen(cname) == (size_t)len && host_matches(hostname, cname))
- return 0;
- return error("certificate owner '%s' does not match hostname '%s'",
-@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const char *pass)
- decoded_len = EVP_DecodeBlock((unsigned char *)challenge,
- (unsigned char *)challenge_64, encoded_len);
- if (decoded_len < 0)
-- die("invalid challenge %s", challenge_64);
-+ die("Invalid challenge %s", challenge_64);
- if (!HMAC(EVP_md5(), pass, strlen(pass), (unsigned char *)challenge, decoded_len, hash, NULL))
- die("HMAC error");
+ static const char *cap_list[] = {
+@@ imap-send.c: static const char *cap_list[] = {
+ "NAMESPACE",
+ "STARTTLS",
+ "AUTH=CRAM-MD5",
++ "AUTH=OAUTHBEARER",
++ "AUTH=XOAUTH2",
+ };
-@@ imap-send.c: static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
- ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response)) {
- free(response);
-- return error("IMAP error: sending response failed");
-+ return error("IMAP error: sending CRAM-MD5 response failed");
- }
+ #define RESP_OK 0
+@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const char *pass)
+ return (char *)response_64;
+ }
- free(response);
-@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
- tunnel.in = -1;
- tunnel.out = -1;
- if (start_command(&tunnel))
-- die("cannot start proxy %s", srvc->tunnel);
-+ die("Cannot start proxy %s", srvc->tunnel);
-
- imap->buf.sock.fd[0] = tunnel.out;
- imap->buf.sock.fd[1] = tunnel.in;
-
-- imap_info("ok\n");
-+ imap_info("OK\n");
- } else {
- #ifndef NO_IPV6
- struct addrinfo hints, *ai0, *ai;
-@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
- fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
- goto bail;
- }
-- imap_info("ok\n");
-+ imap_info("OK\n");
-
- for (ai0 = ai; ai; ai = ai->ai_next) {
- char addr[NI_MAXHOST];
-@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
- perror("gethostbyname");
- goto bail;
- }
-- imap_info("ok\n");
-+ imap_info("OK\n");
++static char *oauthbearer_base64(const char *user, const char *access_token)
++{
++ int raw_len, b64_len;
++ char *raw, *b64;
++
++ /*
++ * Compose the OAUTHBEARER string
++ *
++ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
++ *
++ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
++ * * gs2-cb-flag `n` -> client does not support CB
++ * * gs2-authzid `a=" {User} "`
++ *
++ * The second part are key value pairs containing host, port and auth as
++ * described in RFC7628.
++ *
++ * https://datatracker.ietf.org/doc/html/rfc5801
++ * https://datatracker.ietf.org/doc/html/rfc7628
++ */
++ raw_len = strlen(user) + strlen(access_token) + 20;
++ raw = xmallocz(raw_len + 1);
++ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
++
++ /* Base64 encode */
++ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
++ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
++ free(raw);
++
++ if (b64_len < 0) {
++ free(b64);
++ return NULL;
++ }
++ return b64;
++}
++
++static char *xoauth2_base64(const char *user, const char *access_token)
++{
++ int raw_len, b64_len;
++ char *raw, *b64;
++
++ /*
++ * Compose the XOAUTH2 string
++ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
++ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
++ */
++ raw_len = strlen(user) + strlen(access_token) + 20;
++ raw = xmallocz(raw_len + 1);
++ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
++
++ /* Base64 encode */
++ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
++ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
++ free(raw);
++
++ if (b64_len < 0) {
++ free(b64);
++ return NULL;
++ }
++ return b64;
++}
++
+ #else
- addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
+ static char *cram(const char *challenge_64 UNUSED,
+@@ imap-send.c: static char *cram(const char *challenge_64 UNUSED,
+ "you have to build git-imap-send with OpenSSL library.");
+ }
-@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
- }
++static char *oauthbearer_base64(const char *user UNUSED,
++ const char *access_token UNUSED)
++{
++ die("You are trying to use OAUTHBEARER authenticate method "
++ "with OpenSSL library, but its support has not been compiled in.");
++}
++
++static char *xoauth2_base64(const char *user UNUSED,
++ const char *access_token UNUSED)
++{
++ die("You are trying to use XOAUTH2 authenticate method "
++ "with OpenSSL library, but its support has not been compiled in.");
++}
++
#endif
- if (s < 0) {
-- fputs("Error: unable to connect to server.\n", stderr);
-+ fputs("Error: unable to connect to server\n", stderr);
- goto bail;
- }
-@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
- close(s);
- goto bail;
- }
-- imap_info("ok\n");
-+ imap_info("OK\n");
- }
+ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+@@ imap-send.c: static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+ return 0;
+ }
- /* read the greeting string */
++static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
++{
++ int ret;
++ char *b64;
++
++ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
++ if (!b64)
++ return error("OAUTHBEARER: base64 encoding failed");
++
++ /* Send the base64-encoded response */
++ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
++ if (ret != (int)strlen(b64)) {
++ free(b64);
++ return error("IMAP error: sending OAUTHBEARER response failed");
++ }
++
++ free(b64);
++ return 0;
++}
++
++static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
++{
++ int ret;
++ char *b64;
++
++ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
++ if (!b64)
++ return error("XOAUTH2: base64 encoding failed");
++
++ /* Send the base64-encoded response */
++ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
++ if (ret != (int)strlen(b64)) {
++ free(b64);
++ return error("IMAP error: sending XOAUTH2 response failed");
++ }
++
++ free(b64);
++ return 0;
++}
++
+ static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
+ {
+ if (srvc->user && srvc->pass)
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
- }
- } else {
- if (CAP(NOLOGIN)) {
-- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
-+ fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN.\n",
- srvc->user, srvc->host);
+ fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
+ goto bail;
+ }
++ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
++ if (!CAP(AUTH_OAUTHBEARER)) {
++ fprintf(stderr, "You specified "
++ "OAUTHBEARER as authentication method, "
++ "but %s doesn't support it.\n", srvc->host);
++ goto bail;
++ }
++ /* OAUTHBEARER */
++
++ memset(&cb, 0, sizeof(cb));
++ cb.cont = auth_oauthbearer;
++ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
++ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
++ goto bail;
++ }
++ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
++ if (!CAP(AUTH_XOAUTH2)) {
++ fprintf(stderr, "You specified "
++ "XOAUTH2 as authentication method, "
++ "but %s doesn't support it.\n", srvc->host);
++ goto bail;
++ }
++ /* XOAUTH2 */
++
++ memset(&cb, 0, sizeof(cb));
++ cb.cont = auth_xoauth2;
++ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
++ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
++ goto bail;
++ }
+ } else {
+ fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
- }
- if (!imap->buf.sock.ssl)
- imap_warn("*** IMAP Warning *** Password is being "
-- "sent in the clear\n");
-+ "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;
-@@ imap-send.c: static int append_msgs_to_imap(struct imap_server_conf *server,
-
- ctx = imap_open_store(server, server->folder);
- if (!ctx) {
-- fprintf(stderr, "failed to open store\n");
-+ fprintf(stderr, "Failed to open store.\n");
- return 1;
- }
- ctx->name = server->folder;
-
-- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
-+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
- while (1) {
- unsigned percent = n * 100 / total;
-
@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
-- die("failed to encode server folder");
-+ die("Failed to encode server folder.");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
-
-@@ imap-send.c: static int curl_append_msgs_to_imap(struct imap_server_conf *server,
- curl = setup_curl(server, &cred);
- curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
-
-- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
-+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
- while (1) {
- unsigned percent = n * 100 / total;
- int prev_len;
-@@ imap-send.c: int cmd_main(int argc, const char **argv)
- server.port = server.use_ssl ? 993 : 143;
-
- if (!server.folder) {
-- fprintf(stderr, "no imap store specified\n");
-+ fprintf(stderr, "No IMAP store specified.\n");
- ret = 1;
- goto out;
- }
- if (!server.host) {
- if (!server.tunnel) {
-- fprintf(stderr, "no imap host specified\n");
-+ fprintf(stderr, "No IMAP host specified.\n");
- ret = 1;
- goto out;
- }
-@@ imap-send.c: int cmd_main(int argc, const char **argv)
-
- /* read the messages */
- if (strbuf_read(&all_msgs, 0, 0) < 0) {
-- error_errno(_("could not read from stdin"));
-+ error_errno(_("Could not read from stdin."));
- ret = 1;
- goto out;
- }
+ server_fill_credential(srvc, cred);
+ curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
+- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
++
++ if (!srvc->auth_method ||
++ strcmp(srvc->auth_method, "XOAUTH2") ||
++ strcmp(srvc->auth_method, "OAUTHBEARER"))
++ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
- if (all_msgs.len == 0) {
-- fprintf(stderr, "nothing to send\n");
-+ fprintf(stderr, "Nothing to send.\n");
- ret = 1;
- goto out;
- }
+ strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
+ strbuf_addstr(&path, srvc->host);
+@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
+ curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
- total = count_messages(&all_msgs);
- if (!total) {
-- fprintf(stderr, "no messages to send\n");
-+ fprintf(stderr, "No messages found to send.\n");
- ret = 1;
- goto out;
+ if (srvc->auth_method) {
+- struct strbuf auth = STRBUF_INIT;
+- strbuf_addstr(&auth, "AUTH=");
+- strbuf_addstr(&auth, srvc->auth_method);
+- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+- strbuf_release(&auth);
++ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
++ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
++
++ /*
++ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
++ * upon debugging, it has been found that it is capable of detecting
++ * the best option out of OAUTHBEARER and XOAUTH2.
++ */
++ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
++ } else {
++ struct strbuf auth = STRBUF_INIT;
++ strbuf_addstr(&auth, "AUTH=");
++ strbuf_addstr(&auth, srvc->auth_method);
++ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
++ strbuf_release(&auth);
++ }
}
-
- ## t/t1517-outside-repo.sh ##
-@@ t/t1517-outside-repo.sh: test_expect_success 'imap-send outside repository' '
- test_config_global imap.host imaps://localhost &&
- test_config_global imap.folder Drafts &&
-
-- echo nothing to send >expect &&
-+ echo Nothing to send. >expect &&
- test_must_fail git imap-send -v </dev/null 2>actual &&
- test_cmp expect actual &&
+ if (!srvc->use_ssl)
-: ---------- > 3: 3a0be43838 imap-send: add PLAIN authentication method to OpenSSL
-: ---------- > 4: 45f5b3f1ff imap-send: fix memory leak in case auth_cram_md5 fails
-: ---------- > 5: 8899f686d7 imap-send: enable specifying the folder using the command line
-: ---------- > 6: c2dfd0178c imap-send: fix minor mistakes in the logs
2: e436a12198 = 7: 4e1b51acd5 imap-send: display port alongwith host when git credential is invoked
3: 5183253004 = 8: 85c40d8491 imap-send: display the destination mailbox when sending a message
4: c33469a5db ! 9: 5e24c6cde8 imap-send: add ability to list the available folders
@@ imap-send.c: static int append_msgs_to_imap(struct imap_server_conf *server,
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
-+ fprintf(stderr, "Failed to connect to IMAP server.\n");
++ fprintf(stderr, "failed to connect to IMAP server\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
-+ fprintf(stderr, "Failed to list folders.\n");
++ fprintf(stderr, "failed to list folders\n");
+ imap_close_store(ctx);
+ return 1;
+ }
@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct crede
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
-- die("Failed to encode server folder.");
+- die("failed to encode server folder");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
-+ die("Failed to encode server folder.");
++ die("failed to encode server folder");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
@@ imap-send.c: int cmd_main(int argc, const char **argv)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
-- fprintf(stderr, "No IMAP store specified.\n");
+- fprintf(stderr, "no IMAP store specified\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "No IMAP host specified.\n");
+ fprintf(stderr, "no IMAP host specified\n");
@@ imap-send.c: int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
@@ imap-send.c: int cmd_main(int argc, const char **argv)
+ }
+
+ if (!server.folder) {
-+ fprintf(stderr, "No IMAP store specified.\n");
++ fprintf(stderr, "no IMAP store specified\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
- error_errno(_("Could not read from stdin."));
+ error_errno(_("could not read from stdin"));
--
2.49.0.638.g5e24c6cde8
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v11 1/9] imap-send: fix bug causing cfg->folder being set to NULL
2025-06-01 8:38 ` [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-01 8:38 ` Aditya Garg
2025-06-01 8:38 ` [PATCH v11 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (7 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 8:38 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0.638.g5e24c6cde8
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v11 2/9] imap-send: add support for OAuth2.0 authentication
2025-06-01 8:38 ` [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-01 8:38 ` [PATCH v11 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-06-01 8:38 ` Aditya Garg
2025-06-02 0:13 ` Junio C Hamano
2025-06-01 8:38 ` [PATCH v11 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (6 subsequent siblings)
8 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 8:38 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 47 +++++++-
imap-send.c | 179 +++++++++++++++++++++++++++++--
3 files changed, 218 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..fef6487293 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
+ 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext LOGIN command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..08ecb1e829 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,18 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your regular password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you can generate
+an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create it.
+Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +122,35 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
+or `XOAUTH2` mechanism in your config. It is more secure than using app-specific
+passwords, and also does not enforce the need of having multi-factor authentication.
+You will have to use an OAuth2.0 access token in place of your password when using this
+authentication.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +159,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..4f3a1fb5b1 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,68 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +961,20 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+static char *oauthbearer_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use OAUTHBEARER authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
+static char *xoauth2_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use XOAUTH2 authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -913,6 +993,46 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1104,6 +1224,36 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1555,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
+ strcmp(srvc->auth_method, "XOAUTH2") ||
+ strcmp(srvc->auth_method, "OAUTHBEARER"))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1577,22 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /*
+ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0.638.g5e24c6cde8
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v11 3/9] imap-send: add PLAIN authentication method to OpenSSL
2025-06-01 8:38 ` [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-01 8:38 ` [PATCH v11 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-01 8:38 ` [PATCH v11 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-01 8:38 ` Aditya Garg
2025-06-02 0:27 ` Junio C Hamano
2025-06-01 8:38 ` [PATCH v11 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (5 subsequent siblings)
8 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 8:38 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +-
imap-send.c | 81 +++++++++++++++++++++++++++++++++-
2 files changed, 82 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index fef6487293..24e88228d0 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
- 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are 'PLAIN', 'CRAM-MD5', 'OAUTHBEARER'
+ and 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
plaintext LOGIN command.
diff --git a/imap-send.c b/imap-send.c
index 4f3a1fb5b1..bc26abd150 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,41 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -953,6 +990,13 @@ static char *xoauth2_base64(const char *user, const char *access_token)
#else
+static char *plain_base64(const char *user UNUSED,
+ const char *access_token UNUSED)
+{
+ die("You are trying to use PLAIN authenticate method "
+ "with OpenSSL library, but its support has not been compiled in.");
+}
+
static char *cram(const char *challenge_64 UNUSED,
const char *user UNUSED,
const char *pass UNUSED)
@@ -977,6 +1021,26 @@ static char *xoauth2_base64(const char *user UNUSED,
#endif
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1209,7 +1273,22 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (srvc->auth_method) {
struct imap_cmd_cb cb;
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (!CAP(AUTH_PLAIN)) {
+ fprintf(stderr, "You specified "
+ "PLAIN as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+ /* PLAIN */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_plain;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
"CRAM-MD5 as authentication method, "
--
2.49.0.638.g5e24c6cde8
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v11 4/9] imap-send: fix memory leak in case auth_cram_md5 fails
2025-06-01 8:38 ` [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-06-01 8:38 ` [PATCH v11 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-06-01 8:38 ` Aditya Garg
2025-06-01 8:38 ` [PATCH v11 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
` (4 subsequent siblings)
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 8:38 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index bc26abd150..e169c5e919 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1049,8 +1049,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0.638.g5e24c6cde8
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v11 5/9] imap-send: enable specifying the folder using the command line
2025-06-01 8:38 ` [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-06-01 8:38 ` [PATCH v11 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-06-01 8:38 ` Aditya Garg
2025-06-02 0:39 ` Junio C Hamano
2025-06-01 8:38 ` [PATCH v11 6/9] imap-send: fix minor mistakes in the logs Aditya Garg
` (3 subsequent siblings)
8 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 8:38 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 22 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 24e88228d0..829d9e0bac 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,8 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: 'INBOX.Drafts', 'INBOX/Drafts' or
+ '[Gmail]/Drafts'. Required if `--folder` argument is not used. If
+ set and `--folder` is also used, `--folder` will be preferred.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 08ecb1e829..8f221240d0 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields 'From', 'Date', and 'Subject' in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index e169c5e919..cfa335b647 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1766,6 +1768,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0.638.g5e24c6cde8
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v11 6/9] imap-send: fix minor mistakes in the logs
2025-06-01 8:38 ` [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-06-01 8:38 ` [PATCH v11 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-06-01 8:38 ` Aditya Garg
2025-06-02 0:42 ` Junio C Hamano
2025-06-01 8:38 ` [PATCH v11 7/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
` (2 subsequent siblings)
8 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 8:38 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some minor mistakes have been found in the logs. Most of them include
error messages starting with a capital letter, and ending with a period.
Also, abbreviations like "IMAP" and "OK" should be in uppercase. Fix them.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index cfa335b647..97e7fb197f 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -205,7 +205,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in.\n");
return -1;
}
@@ -1053,7 +1053,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1152,7 +1152,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1171,7 +1171,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1209,7 +1209,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1223,7 +1223,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1235,7 +1235,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1338,12 +1338,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
goto bail;
}
} else {
- fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ fprintf(stderr, "unknown authentication method:%s\n", srvc->host);
goto bail;
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "skipping account %s@%s, server forbids LOGIN\n",
srvc->user, srvc->host);
goto bail;
}
@@ -1599,7 +1599,7 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
@@ -1708,7 +1708,7 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
@@ -1792,13 +1792,13 @@ int cmd_main(int argc, const char **argv)
server.port = server.use_ssl ? 993 : 143;
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "no IMAP store specified\n");
ret = 1;
goto out;
}
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "no IMAP host specified\n");
ret = 1;
goto out;
}
@@ -1820,7 +1820,7 @@ int cmd_main(int argc, const char **argv)
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "no messages found to send\n");
ret = 1;
goto out;
}
--
2.49.0.638.g5e24c6cde8
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v11 7/9] imap-send: display port alongwith host when git credential is invoked
2025-06-01 8:38 ` [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-06-01 8:38 ` [PATCH v11 6/9] imap-send: fix minor mistakes in the logs Aditya Garg
@ 2025-06-01 8:38 ` Aditya Garg
2025-06-01 8:38 ` [PATCH v11 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-01 8:38 ` [PATCH v11 9/9] imap-send: add ability to list the available folders Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 8:38 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
FWIW, if no port is specified by the user, the default port, 993 for
IMAPS and 143 for IMAP is used by the code. So, the case of no port
defined for the helper is not possible, and therefore is not added.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 97e7fb197f..9c3c8d8c3c 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1107,7 +1107,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0.638.g5e24c6cde8
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v11 8/9] imap-send: display the destination mailbox when sending a message
2025-06-01 8:38 ` [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-06-01 8:38 ` [PATCH v11 7/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-06-01 8:38 ` Aditya Garg
2025-06-02 0:43 ` Junio C Hamano
2025-06-01 8:38 ` [PATCH v11 9/9] imap-send: add ability to list the available folders Aditya Garg
8 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 8:38 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
Sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 9c3c8d8c3c..3565a91ca3 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1599,7 +1599,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1708,7 +1709,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0.638.g5e24c6cde8
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v11 9/9] imap-send: add ability to list the available folders
2025-06-01 8:38 ` [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-06-01 8:38 ` [PATCH v11 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-06-01 8:38 ` Aditya Garg
8 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-01 8:38 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Various IMAP servers have different ways to name common folders.
For example, the folder where all deleted messages are stored is often
named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
on Gmail it is named "[Gmail]/Drafts".
This commit adds a `--list` command to the `imap-send` tool that lists
the available folders on the IMAP server, allowing users to see
which folders are available and how they are named. A sample output
looks like this when run against a Gmail server:
Fetching the list of available folders...
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasChildren \Noselect) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
For OpenSSL, this is achived by running the 'IMAP LIST' command and
parsing the response. This command is specified in RFC6154:
https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
For libcurl, the example code published in the libcurl documentation
is used to implement this functionality:
https://curl.se/libcurl/c/imap-list.html
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/git-imap-send.adoc | 6 +-
imap-send.c | 98 ++++++++++++++++++++++++++------
2 files changed, 87 insertions(+), 17 deletions(-)
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 8f221240d0..379a371c08 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+'git imap-send' --list
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
+--list::
+ Run the IMAP LIST command to output a list of all the folders present.
CONFIGURATION
-------------
@@ -123,7 +126,8 @@ Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
-that the "Folder doesn't exist".
+that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
+list of available folders.
[NOTE]
If your Gmail account is set to another language than English, the name of the "Drafts"
diff --git a/imap-send.c b/imap-send.c
index 3565a91ca3..ca95eef652 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -45,15 +45,21 @@
#endif
static int verbosity;
+static int list_folders = 0;
static int use_curl = USE_CURL_DEFAULT;
static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
+ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
+ "git imap-send --list",
+ NULL
+};
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
+ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
OPT_END()
};
@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
if (b->buf[b->offset + 1] == '\n') {
b->buf[b->offset] = 0; /* terminate the string */
b->offset += 2; /* next line */
- if (0 < verbosity)
+ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
puts(*s);
return 0;
}
@@ -1622,6 +1628,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
+static int list_imap_folders(struct imap_server_conf *server)
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
+ fprintf(stderr, "failed to connect to IMAP server\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
+ fprintf(stderr, "failed to list folders\n");
+ imap_close_store(ctx);
+ return 1;
+ }
+
+ imap_close_store(ctx);
+ return 0;
+}
+
#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
@@ -1650,11 +1676,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
- die("failed to encode server folder");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+ die("failed to encode server folder");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ -1685,10 +1713,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
-
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(curl);
@@ -1707,6 +1731,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
struct credential cred = CREDENTIAL_INIT;
curl = setup_curl(server, &cred);
+
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
fprintf(stderr, "Sending %d message%s to %s folder...\n",
@@ -1753,6 +1781,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
+
+static int curl_list_imap_folders(struct imap_server_conf *server)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct credential cred = CREDENTIAL_INIT;
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ curl = setup_curl(server, &cred);
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ if (cred.username) {
+ if (res == CURLE_OK)
+ credential_approve(the_repository, &cred);
+ else if (res == CURLE_LOGIN_DENIED)
+ credential_reject(the_repository, &cred);
+ }
+
+ credential_clear(&cred);
+
+ return res != CURLE_OK;
+}
#endif
int cmd_main(int argc, const char **argv)
@@ -1793,11 +1846,6 @@ int cmd_main(int argc, const char **argv)
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
- fprintf(stderr, "no IMAP store specified\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "no IMAP host specified\n");
@@ -1807,6 +1855,24 @@ int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
+ if (list_folders) {
+ if (server.tunnel)
+ ret = list_imap_folders(&server);
+#ifdef USE_CURL_FOR_IMAP_SEND
+ else if (use_curl)
+ ret = curl_list_imap_folders(&server);
+#endif
+ else
+ ret = list_imap_folders(&server);
+ goto out;
+ }
+
+ if (!server.folder) {
+ fprintf(stderr, "no IMAP store specified\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("could not read from stdin"));
--
2.49.0.638.g5e24c6cde8
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v11 2/9] imap-send: add support for OAuth2.0 authentication
2025-06-01 8:38 ` [PATCH v11 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-02 0:13 ` Junio C Hamano
0 siblings, 0 replies; 248+ messages in thread
From: Junio C Hamano @ 2025-06-02 0:13 UTC (permalink / raw)
To: Aditya Garg
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m . carlson,
Jeff King, Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> OAuth2.0 is a new way of authentication supported by various email providers
> these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
> for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
> XOAUTH2 is Google's proprietary mechanism (See [3]).
>
> [1]: https://datatracker.ietf.org/doc/html/rfc5801
> [2]: https://datatracker.ietf.org/doc/html/rfc7628
> [3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
>
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
> ---
> Documentation/config/imap.adoc | 5 +-
> Documentation/git-imap-send.adoc | 47 +++++++-
> imap-send.c | 179 +++++++++++++++++++++++++++++--
> 3 files changed, 218 insertions(+), 13 deletions(-)
>
> diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
> index 3d28f72643..fef6487293 100644
> --- a/Documentation/config/imap.adoc
> +++ b/Documentation/config/imap.adoc
> @@ -40,5 +40,6 @@ imap.authMethod::
> Specify the authentication method for authenticating with the IMAP server.
> If Git was built with the NO_CURL option, or if your curl version is older
> than 7.34.0, or if you're running git-imap-send with the `--no-curl`
> - option, the only supported method is 'CRAM-MD5'. If this is not set
> - then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
> + option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
> + 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
> + plaintext LOGIN command.
> diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
> index 26ccf4e433..08ecb1e829 100644
> --- a/Documentation/git-imap-send.adoc
> +++ b/Documentation/git-imap-send.adoc
> @@ -102,12 +102,18 @@ Using Gmail's IMAP interface:
>
> ---------
> [imap]
> - folder = "[Gmail]/Drafts"
> - host = imaps://imap.gmail.com
> - user = user@gmail.com
> - port = 993
> + folder = "[Gmail]/Drafts"
> + host = imaps://imap.gmail.com
> + user = user@gmail.com
> + port = 993
Nice to see such an attention to the detail here.
> ---------
>
> +Gmail does not allow using your regular password for `git imap-send`.
> +If you have multi-factor authentication set up on your Gmail account, you can generate
> +an app-specific password for use with `git imap-send`.
> +Visit https://security.google.com/settings/security/apppasswords to create it.
> +Alternatively, use OAuth2.0 authentication as described below.
The new lines added by this part of the documentation tends to be
overly long but with minor rewrapping you can stay under 75 columns
or so without too much effort.
> +If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
> +or `XOAUTH2` mechanism in your config. It is more secure than using app-specific
> +passwords, and also does not enforce the need of having multi-factor authentication.
> +You will have to use an OAuth2.0 access token in place of your password when using this
Ditto.
> @@ -124,6 +159,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
> interface will wrap lines no matter what, so you need to use a real
> IMAP client).
>
> +In case you are using OAuth2.0 authentication, it is easier to use credential
> +helpers to generate tokens. Credential helpers suggested in
> +linkgit:git-send-email[1] can be used for `git imap-send` as well.
> +
> CAUTION
> -------
> It is still your responsibility to make sure that the email message
> diff --git a/imap-send.c b/imap-send.c
> index 37f94a37e8..4f3a1fb5b1 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -139,7 +139,9 @@ enum CAPABILITY {
> LITERALPLUS,
> NAMESPACE,
> STARTTLS,
> - AUTH_CRAM_MD5
> + AUTH_CRAM_MD5,
> + AUTH_OAUTHBEARER,
> + AUTH_XOAUTH2
> };
These days, we allow and encourage ending the last member of an enum
with a trailing comma (cf. Documentation/CodingGuidelines) to reduce
future patch noise, just like you did ...
> static const char *cap_list[] = {
> @@ -149,6 +151,8 @@ static const char *cap_list[] = {
> "NAMESPACE",
> "STARTTLS",
> "AUTH=CRAM-MD5",
> + "AUTH=OAUTHBEARER",
> + "AUTH=XOAUTH2",
> };
... here for an array initializer elements.
> +static char *oauthbearer_base64(const char *user UNUSED,
> + const char *access_token UNUSED)
> +{
> + die("You are trying to use OAUTHBEARER authenticate method "
> + "with OpenSSL library, but its support has not been compiled in.");
> +}
> +
> +static char *xoauth2_base64(const char *user UNUSED,
> + const char *access_token UNUSED)
> +{
> + die("You are trying to use XOAUTH2 authenticate method "
> + "with OpenSSL library, but its support has not been compiled in.");
> +}
> +
> #endif
Let's keep a mental note that the lowest layer of the auth_*
function can die() for the methods that are not supported.
> static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
> @@ -913,6 +993,46 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
> return 0;
> }
>
> +static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
> +{
> + int ret;
> + char *b64;
> +
> + b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
> + if (!b64)
> + return error("OAUTHBEARER: base64 encoding failed");
> +
> + /* Send the base64-encoded response */
> + ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
> + if (ret != (int)strlen(b64)) {
> + free(b64);
> + return error("IMAP error: sending OAUTHBEARER response failed");
> + }
> +
> + free(b64);
> + return 0;
> +}
> +
> +static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
> +{
> + int ret;
> + char *b64;
> +
> + b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
> + if (!b64)
> + return error("XOAUTH2: base64 encoding failed");
> +
> + /* Send the base64-encoded response */
> + ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
> + if (ret != (int)strlen(b64)) {
> + free(b64);
> + return error("IMAP error: sending XOAUTH2 response failed");
> + }
> +
> + free(b64);
> + return 0;
> +}
It feels very strange to see auth_xoauth2() defined unconditionally
and leave xoauth2_base64() to die when built without OpenSSL.
Exactly the same comment applies to auth_oauthbearer() vs
oauthbearer_base64(), and auth_cram_md5() vs cram().
The reason why it looks strange to me is that I suspect that we'd
end up with an inconsistent behaviour like we see below.
For example, here, when we at runtime detect that ...
> + } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
> + if (!CAP(AUTH_OAUTHBEARER)) {
> + fprintf(stderr, "You specified "
> + "OAUTHBEARER as authentication method, "
> + "but %s doesn't support it.\n", srvc->host);
> + goto bail;
... the other end of the connection does not support the method the
end user specified, we gracefully fail like so, but we do not even
bother detecting at runtime that _we_ do not support the method the
end user specified, until ...
> + }
> + /* OAUTHBEARER */
> +
> + memset(&cb, 0, sizeof(cb));
> + cb.cont = auth_oauthbearer;
... this callback function, which is a stub when we do not support
the method, gets called ...
> + if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
... here inside imap_exec(). It is probably no use that the
imap_exec() call is prepared to catch an error, as the unimplemented
method would call die() as we saw above.
> + fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
> + goto bail;
> + }
Instead of making the lowest level helpers like cram(),
oauthbearer_base64(), and xoauth2_base64() conditionally be stubs
that die(), wouldn't it make more sense to conditionally define
helpers one lebel higher, i.e. those you stuff in cb.cont, only when
the user permits Git to be linked with OpenSSL, e.g.
#ifdef OpenSSL
static int auth_oauthbearer(struct imap_store *ctx, const char *p UNUSED)
{
...
}
#else /* !OpenSSL */
#define auth_oauthbearer NULL
#endif
and then write this part of the code more like this?
} else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
if (!CAP(AUTH_OAUTHBEARER)) {
... the other side does not support ...
goto bail;
}
if (!auth_oauthbearer) {
... we do not support ...
goto bail;
}
memset(&cb, 0, sizeof(cb));
cb.cont = auth_oauthbearer;
if (imap_exec(ctx, &cb, ...) != RESP_OK) {
... we both support but we failed ...
goto bail;
}
Exactly the same comments appli to other methods.
Including "CRAM-MD5", that is. That one may have been iffy before
you started touching this file, but it is not a good excuse for
adding two similarly bad code in the patch series.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v11 3/9] imap-send: add PLAIN authentication method to OpenSSL
2025-06-01 8:38 ` [PATCH v11 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-06-02 0:27 ` Junio C Hamano
2025-06-02 4:01 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-02 0:27 UTC (permalink / raw)
To: Aditya Garg
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m . carlson,
Jeff King, Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> #else
>
> +static char *plain_base64(const char *user UNUSED,
> + const char *access_token UNUSED)
> +{
> + die("You are trying to use PLAIN authenticate method "
> + "with OpenSSL library, but its support has not been compiled in.");
> +}
This comment may apply also to the earlier OAuth related two stub
functions, but this is the "#else" side of "#ifndef NO_OPENSSL";
double negation always makes all our spin, but in short, this is
"You are not building with OpenSSL". We cannot quite look at the
post context of this hunk for sanity check, but inside the cram()
stub function ...
> static char *cram(const char *challenge_64 UNUSED,
> const char *user UNUSED,
> const char *pass UNUSED)
die("If you want to use CRAM-MD5 authenticate method, "
"you have to build git-imap-send with OpenSSL library.");
... is the message it dies with. So, shouldn't the error from the
new stub function also say "If you want to use PLAIN, you have to
build with OpenSSL"?
> +static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
> +{
> + int ret;
> + char *b64;
> +
> + b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
> + if (!b64)
> + return error("PLAIN: base64 encoding failed");
> +
> + /* Send the base64-encoded response */
> + ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
> + if (ret != (int)strlen(b64)) {
> + free(b64);
> + return error("IMAP error: sending PLAIN response failed");
> + }
> +
> + free(b64);
> + return 0;
> +}
And the same comment about not gracefully failing when our side lack
support, even though we gracefully fail when the other side lacks
support, given to an earlier step also applies here.
> @@ -1209,7 +1273,22 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
> if (srvc->auth_method) {
> struct imap_cmd_cb cb;
>
> - if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
> + if (!strcmp(srvc->auth_method, "PLAIN")) {
> + if (!CAP(AUTH_PLAIN)) {
> + fprintf(stderr, "You specified "
> + "PLAIN as authentication method, "
> + "but %s doesn't support it.\n", srvc->host);
> + goto bail;
> + }
> + /* PLAIN */
> +
> + memset(&cb, 0, sizeof(cb));
> + cb.cont = auth_plain;
> + if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
> + fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
> + goto bail;
> + }
> + } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
> if (!CAP(AUTH_CRAM_MD5)) {
> fprintf(stderr, "You specified "
> "CRAM-MD5 as authentication method, "
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v11 5/9] imap-send: enable specifying the folder using the command line
2025-06-01 8:38 ` [PATCH v11 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-06-02 0:39 ` Junio C Hamano
2025-06-02 3:45 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-02 0:39 UTC (permalink / raw)
To: Aditya Garg
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m . carlson,
Jeff King, Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> Some users may very often want to imap-send messages to a folder
> other than the default set in the config. Add a command line
> argument for the same.
>
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
> ---
> Documentation/config/imap.adoc | 5 +++--
> Documentation/git-imap-send.adoc | 15 +++++++++++----
> imap-send.c | 9 ++++++++-
> 3 files changed, 22 insertions(+), 7 deletions(-)
>
> diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
> index 24e88228d0..829d9e0bac 100644
> --- a/Documentation/config/imap.adoc
> +++ b/Documentation/config/imap.adoc
> @@ -1,7 +1,8 @@
> imap.folder::
> The folder to drop the mails into, which is typically the Drafts
> - folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
> - "[Gmail]/Drafts". Required.
> + folder. For example: 'INBOX.Drafts', 'INBOX/Drafts' or
> + '[Gmail]/Drafts'. Required if `--folder` argument is not used. If
> + set and `--folder` is also used, `--folder` will be preferred.
Shouldn't these literals be `typeset like this` with backquotes?
More importantly, when we mention that the command line option
trumps the corresponding configuration variable, the more common
verb we use than "prefer" is "override". Because it is a general
rule that the configuration variable is used as a back-up in case
there is no command line option is given, it is less confusing if
you omitted the last sentence. Perhaps rewrite the last two
sentence with something like this?
The IMAP folder to interact with MUST be specified; the
value of this configuration variable is used as the fallback
default value when the `--folder` option is not given.
I dunno.
> @@ -37,6 +39,11 @@ OPTIONS
> --quiet::
> Be quiet.
>
> +-f <folder>::
> +--folder=<folder>::
> + Specify the folder in which the emails have to saved.
> + For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
> +
> --curl::
> Use libcurl to communicate with the IMAP server, unless tunneling
> into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
There are four existing options and this adds another. I am
debating myself if this deserves a preliminary clean-up patch so
that the enumerated options are more like
`-v`::
`--verbose`::
Be verbose.
If we did so, this patch can add
`-f` _<folder>_::
`--folder=<folder>`::
Specify the folder to save the e-mails in.
Required. Defaults to the value of the `imap.folder`
configuration variable
without worrying about it not following the prevailing (and stale)
style.
If we are not doing a preliminary clean-up patch, what you sent is
more in line. We'll leave the clean-up to somebody else and adding
one new option in a stale style to 4 existing ones may not be too
bad. At least such an intermediate state is locally consistent.
Thanks.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v11 6/9] imap-send: fix minor mistakes in the logs
2025-06-01 8:38 ` [PATCH v11 6/9] imap-send: fix minor mistakes in the logs Aditya Garg
@ 2025-06-02 0:42 ` Junio C Hamano
2025-06-02 3:41 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-02 0:42 UTC (permalink / raw)
To: Aditya Garg
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m . carlson,
Jeff King, Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> Some minor mistakes have been found in the logs. Most of them include
> error messages starting with a capital letter, and ending with a period.
> Also, abbreviations like "IMAP" and "OK" should be in uppercase. Fix them.
>
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
> ---
> imap-send.c | 28 ++++++++++++++--------------
> 1 file changed, 14 insertions(+), 14 deletions(-)
Quite honestly, I am not sure if this churn is worth it.
Unless we are moving to the same error reporting mechanism more
prevalently used elsewhere in our codebase and consistenly use the
error() function instead of calling fprintf().
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v11 8/9] imap-send: display the destination mailbox when sending a message
2025-06-01 8:38 ` [PATCH v11 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-06-02 0:43 ` Junio C Hamano
0 siblings, 0 replies; 248+ messages in thread
From: Junio C Hamano @ 2025-06-02 0:43 UTC (permalink / raw)
To: Aditya Garg
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m . carlson,
Jeff King, Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> Whenever we sent a message using the `imap-send` command, it would
> display a log showing the number of messages which are to be sent.
> For example:
>
> Sending 1 message
> 100% (1/1) done
>
> This had been made more informative by adding the name of the destination
> folder as well:
>
> Sending 1 message to Drafts folder...
Nice ;-)
> 100% (1/1) done
>
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
> ---
> imap-send.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> diff --git a/imap-send.c b/imap-send.c
> index 9c3c8d8c3c..3565a91ca3 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -1599,7 +1599,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
> }
> ctx->name = server->folder;
>
> - fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
> + fprintf(stderr, "Sending %d message%s to %s folder...\n",
> + total, (total != 1) ? "s" : "", server->folder);
> while (1) {
> unsigned percent = n * 100 / total;
>
> @@ -1708,7 +1709,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
> curl = setup_curl(server, &cred);
> curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
>
> - fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
> + fprintf(stderr, "Sending %d message%s to %s folder...\n",
> + total, (total != 1) ? "s" : "", server->folder);
> while (1) {
> unsigned percent = n * 100 / total;
> int prev_len;
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v11 6/9] imap-send: fix minor mistakes in the logs
2025-06-02 0:42 ` Junio C Hamano
@ 2025-06-02 3:41 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 3:41 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
> On 2 Jun 2025, at 6:12 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>> Some minor mistakes have been found in the logs. Most of them include
>> error messages starting with a capital letter, and ending with a period.
>> Also, abbreviations like "IMAP" and "OK" should be in uppercase. Fix them.
>>
>> Signed-off-by: Aditya Garg <gargaditya08@live.com>
>> ---
>> imap-send.c | 28 ++++++++++++++--------------
>> 1 file changed, 14 insertions(+), 14 deletions(-)
>
> Quite honestly, I am not sure if this churn is worth it.
It was a significantly long patch before Eric's review, even I was thinking
about dropping it. Then I saw the amount of conflicts it will cause .......
I think its just fine to keep it, its not causing anything degrading.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v11 5/9] imap-send: enable specifying the folder using the command line
2025-06-02 0:39 ` Junio C Hamano
@ 2025-06-02 3:45 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 3:45 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
> On 2 Jun 2025, at 6:10 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>> Some users may very often want to imap-send messages to a folder
>> other than the default set in the config. Add a command line
>> argument for the same.
>>
>> Signed-off-by: Aditya Garg <gargaditya08@live.com>
>> ---
>> Documentation/config/imap.adoc | 5 +++--
>> Documentation/git-imap-send.adoc | 15 +++++++++++----
>> imap-send.c | 9 ++++++++-
>> 3 files changed, 22 insertions(+), 7 deletions(-)
>>
>> diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
>> index 24e88228d0..829d9e0bac 100644
>> --- a/Documentation/config/imap.adoc
>> +++ b/Documentation/config/imap.adoc
>> @@ -1,7 +1,8 @@
>> imap.folder::
>> The folder to drop the mails into, which is typically the Drafts
>> - folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
>> - "[Gmail]/Drafts". Required.
>> + folder. For example: 'INBOX.Drafts', 'INBOX/Drafts' or
>> + '[Gmail]/Drafts'. Required if `--folder` argument is not used. If
>> + set and `--folder` is also used, `--folder` will be preferred.
>
> Shouldn't these literals be `typeset like this` with backquotes?
>
> More importantly, when we mention that the command line option
> trumps the corresponding configuration variable, the more common
> verb we use than "prefer" is "override". Because it is a general
> rule that the configuration variable is used as a back-up in case
> there is no command line option is given, it is less confusing if
> you omitted the last sentence. Perhaps rewrite the last two
> sentence with something like this?
>
> The IMAP folder to interact with MUST be specified; the
> value of this configuration variable is used as the fallback
> default value when the `--folder` option is not given.
Ok
>
> I dunno.
>
>> @@ -37,6 +39,11 @@ OPTIONS
>> --quiet::
>> Be quiet.
>>
>> +-f <folder>::
>> +--folder=<folder>::
>> + Specify the folder in which the emails have to saved.
>> + For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
>> +
>> --curl::
>> Use libcurl to communicate with the IMAP server, unless tunneling
>> into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
>
> There are four existing options and this adds another. I am
> debating myself if this deserves a preliminary clean-up patch so
> that the enumerated options are more like
>
> `-v`::
> `--verbose`::
> Be verbose.
>
> If we did so, this patch can add
>
> `-f` _<folder>_::
> `--folder=<folder>`::
> Specify the folder to save the e-mails in.
> Required. Defaults to the value of the `imap.folder`
> configuration variable
>
> without worrying about it not following the prevailing (and stale)
> style.
>
> If we are not doing a preliminary clean-up patch, what you sent is
> more in line. We'll leave the clean-up to somebody else and adding
> one new option in a stale style to 4 existing ones may not be too
> bad. At least such an intermediate state is locally consistent.
Let's keep this style change for some other patch series, since many
other docs also would need this change.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v11 3/9] imap-send: add PLAIN authentication method to OpenSSL
2025-06-02 0:27 ` Junio C Hamano
@ 2025-06-02 4:01 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 4:01 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
> On 2 Jun 2025, at 5:57 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>> #else
>>
>> +static char *plain_base64(const char *user UNUSED,
>> + const char *access_token UNUSED)
>> +{
>> + die("You are trying to use PLAIN authenticate method "
>> + "with OpenSSL library, but its support has not been compiled in.");
>> +}
>
> This comment may apply also to the earlier OAuth related two stub
> functions, but this is the "#else" side of "#ifndef NO_OPENSSL";
> double negation always makes all our spin, but in short, this is
> "You are not building with OpenSSL". We cannot quite look at the
> post context of this hunk for sanity check, but inside the cram()
> stub function ...
>
>> static char *cram(const char *challenge_64 UNUSED,
>> const char *user UNUSED,
>> const char *pass UNUSED)
>
> die("If you want to use CRAM-MD5 authenticate method, "
> "you have to build git-imap-send with OpenSSL library.");
>
> ... is the message it dies with. So, shouldn't the error from the
> new stub function also say "If you want to use PLAIN, you have to
> build with OpenSSL"?
No, there is a difference here. CRAM-MD5 works ONLY with OpenSSL.
OAuth2 and PLAIN work with BOTH OpenSSL and libcurl.
Anyways, taking comments from the OAuth2.0 reply, let me see if I can
remove these statements.
>> +static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
>> +{
>> + int ret;
>> + char *b64;
>> +
>> + b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
>> + if (!b64)
>> + return error("PLAIN: base64 encoding failed");
>> +
>> + /* Send the base64-encoded response */
>> + ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
>> + if (ret != (int)strlen(b64)) {
>> + free(b64);
>> + return error("IMAP error: sending PLAIN response failed");
>> + }
>> +
>> + free(b64);
>> + return 0;
>> +}
>
> And the same comment about not gracefully failing when our side lack
> support, even though we gracefully fail when the other side lacks
> support, given to an earlier step also applies here.
>
>> @@ -1209,7 +1273,22 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
>> if (srvc->auth_method) {
>> struct imap_cmd_cb cb;
>>
>> - if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
>> + if (!strcmp(srvc->auth_method, "PLAIN")) {
>> + if (!CAP(AUTH_PLAIN)) {
>> + fprintf(stderr, "You specified "
>> + "PLAIN as authentication method, "
>> + "but %s doesn't support it.\n", srvc->host);
>> + goto bail;
>> + }
>> + /* PLAIN */
>> +
>> + memset(&cb, 0, sizeof(cb));
>> + cb.cont = auth_plain;
>> + if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
>> + fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
>> + goto bail;
>> + }
>> + } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
>> if (!CAP(AUTH_CRAM_MD5)) {
>> fprintf(stderr, "You specified "
>> "CRAM-MD5 as authentication method, "
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (15 preceding siblings ...)
2025-06-01 8:38 ` [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-02 10:59 ` Aditya Garg
2025-06-02 10:59 ` [PATCH v12 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (9 more replies)
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 subsequent siblings)
23 siblings, 10 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 10:59 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch series does the following things:
Firstly it basically makes the imap-send command usable again since it
was broken because of not being able to correctly parse the config file.
Further it adds support for OAuth2.0 and PLAIN authentication to git
imap-send.
Last, it does some minor improvements including adding the ability to
specify the folder using the command line and ability to list the
available folders by adding a `--list` option.
P.S.: I am surprised this thing even exists xD.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
v8: - Drop the patch that enabled user to choose between libcurl and openssl using the config
- Add ability to list the available folders by adding a `--list` option
v9: - Encourage users to use OAuth2.0 for Gmail (similar change done for send-email docs).
v10: - Fix comment styles
- Fix failing tests
v11: - Use lower case letters for the first word of a sendtence in an error message
and avoid using full stops at the end of a sentence.
v12: - Gracefully exit PLAIN, CRAM-MD5, OAUTHBEARER and XOAUTH2 authentication methods
if OpenSSL support is not compiled in, but is requested by the user.
- Use backticks for string literals.
- Wrap documentation text to 75 columns.
- End the last member of enum CAPABILITY with a trailing comma.
Aditya Garg (10):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: gracefully fail if CRAM-MD5 authentication is requested
without OpenSSL
imap-send: enable specifying the folder using the command line
imap-send: fix minor mistakes in the logs
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
imap-send: add ability to list the available folders
Documentation/config/imap.adoc | 11 +-
Documentation/git-imap-send.adoc | 68 ++++-
imap-send.c | 425 +++++++++++++++++++++++++++----
3 files changed, 441 insertions(+), 63 deletions(-)
Range-diff against v11:
-: ---------- > 1: 3e3ddf7077 imap-send: fix bug causing cfg->folder being set to NULL
1: 02037873a1 ! 2: ab12f713d2 imap-send: add support for OAuth2.0 authentication
@@ Documentation/config/imap.adoc: imap.authMethod::
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
-+ option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
-+ 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
-+ plaintext LOGIN command.
++ option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
++ `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
++ plaintext `LOGIN` command.
## Documentation/git-imap-send.adoc ##
@@ Documentation/git-imap-send.adoc: Using Gmail's IMAP interface:
@@ Documentation/git-imap-send.adoc: Using Gmail's IMAP interface:
---------
+Gmail does not allow using your regular password for `git imap-send`.
-+If you have multi-factor authentication set up on your Gmail account, you can generate
-+an app-specific password for use with `git imap-send`.
-+Visit https://security.google.com/settings/security/apppasswords to create it.
-+Alternatively, use OAuth2.0 authentication as described below.
++If you have multi-factor authentication set up on your Gmail account, you
++can generate an app-specific password for use with `git imap-send`.
++Visit https://security.google.com/settings/security/apppasswords to create
++it. Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
@@ Documentation/git-imap-send.adoc: that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
-+If you want to use OAuth2.0 based authentication, you can specify `OAUTHBEARER`
-+or `XOAUTH2` mechanism in your config. It is more secure than using app-specific
-+passwords, and also does not enforce the need of having multi-factor authentication.
-+You will have to use an OAuth2.0 access token in place of your password when using this
-+authentication.
++If you want to use OAuth2.0 based authentication, you can specify
++`OAUTHBEARER` or `XOAUTH2` mechanism in your config. It is more secure
++than using app-specific passwords, and also does not enforce the need of
++having multi-factor authentication. You will have to use an OAuth2.0
++access token in place of your password when using this authentication.
+
+---------
+[imap]
@@ imap-send.c: enum CAPABILITY {
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
-+ AUTH_XOAUTH2
++ AUTH_XOAUTH2,
};
static const char *cap_list[] = {
@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const
+ return b64;
+}
+
- #else
-
- static char *cram(const char *challenge_64 UNUSED,
-@@ imap-send.c: static char *cram(const char *challenge_64 UNUSED,
- "you have to build git-imap-send with OpenSSL library.");
- }
-
-+static char *oauthbearer_base64(const char *user UNUSED,
-+ const char *access_token UNUSED)
-+{
-+ die("You are trying to use OAUTHBEARER authenticate method "
-+ "with OpenSSL library, but its support has not been compiled in.");
-+}
-+
-+static char *xoauth2_base64(const char *user UNUSED,
-+ const char *access_token UNUSED)
-+{
-+ die("You are trying to use XOAUTH2 authenticate method "
-+ "with OpenSSL library, but its support has not been compiled in.");
-+}
-+
- #endif
-
- static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
-@@ imap-send.c: static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
- return 0;
- }
-
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
@@ imap-send.c: static int auth_cram_md5(struct imap_store *ctx, const char *prompt
+ return 0;
+}
+
- static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
- {
- if (srvc->user && srvc->pass)
+ #else
+
+ static char *cram(const char *challenge_64 UNUSED,
+@@ imap-send.c: static char *cram(const char *challenge_64 UNUSED,
+ "you have to build git-imap-send with OpenSSL library.");
+ }
+
++#define auth_oauthbearer NULL
++#define auth_xoauth2 NULL
++
+ #endif
+
+ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
++
++ #ifdef NO_OPENSSL
++ fprintf(stderr, "You are trying to use OAUTHBEARER authentication mechanism "
++ "with OpenSSL library, but its support has not been compiled in.");
++ goto bail;
++ #endif
++
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
++
++ #ifdef NO_OPENSSL
++ fprintf(stderr, "You are trying to use XOAUTH2 authentication mechanism "
++ "with OpenSSL library, but its support has not been compiled in.");
++ goto bail;
++ #endif
++
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
2: 3a0be43838 ! 3: ba9c3fb756 imap-send: add PLAIN authentication method to OpenSSL
@@ Documentation/config/imap.adoc: imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
-- option, the only supported methods are 'CRAM-MD5', 'OAUTHBEARER' and
-- 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
-+ option, the only supported methods are 'PLAIN', 'CRAM-MD5', 'OAUTHBEARER'
-+ and 'XOAUTH2'. If this is not set then `git imap-send` uses the basic IMAP
- plaintext LOGIN command.
+- option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
+- `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
++ option, the only supported methods are `PLAIN`, `CRAM-MD5`, `OAUTHBEARER`
++ and `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext `LOGIN` command.
## imap-send.c ##
@@ imap-send.c: enum CAPABILITY {
@@ imap-send.c: enum CAPABILITY {
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
- AUTH_XOAUTH2
+ AUTH_XOAUTH2,
@@ imap-send.c: static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
@@ imap-send.c: static char hexchar(unsigned int b)
{
int i, resp_len, encoded_len, decoded_len;
@@ imap-send.c: static char *xoauth2_base64(const char *user, const char *access_token)
-
- #else
-
-+static char *plain_base64(const char *user UNUSED,
-+ const char *access_token UNUSED)
-+{
-+ die("You are trying to use PLAIN authenticate method "
-+ "with OpenSSL library, but its support has not been compiled in.");
-+}
-+
- static char *cram(const char *challenge_64 UNUSED,
- const char *user UNUSED,
- const char *pass UNUSED)
-@@ imap-send.c: static char *xoauth2_base64(const char *user UNUSED,
-
- #endif
+ return b64;
+ }
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
@@ imap-send.c: static char *xoauth2_base64(const char *user UNUSED,
+ return 0;
+}
+
- static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+ static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
{
int ret;
+@@ imap-send.c: static char *cram(const char *challenge_64 UNUSED,
+ "you have to build git-imap-send with OpenSSL library.");
+ }
+
++#define auth_plain NULL
+ #define auth_oauthbearer NULL
+ #define auth_xoauth2 NULL
+
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (srvc->auth_method) {
struct imap_cmd_cb cb;
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
++
++ #ifdef NO_OPENSSL
++ fprintf(stderr, "You are trying to use PLAIN authentication mechanism "
++ "with OpenSSL library, but its support has not been compiled in.");
++ goto bail;
++ #endif
++
+ /* PLAIN */
+
+ memset(&cb, 0, sizeof(cb));
3: 45f5b3f1ff = 4: 3d1a66da57 imap-send: fix memory leak in case auth_cram_md5 fails
-: ---------- > 5: 70bb9388b8 imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
4: 8899f686d7 ! 6: 0d00a5e135 imap-send: enable specifying the folder using the command line
@@ Documentation/config/imap.adoc
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
-+ folder. For example: 'INBOX.Drafts', 'INBOX/Drafts' or
-+ '[Gmail]/Drafts'. Required if `--folder` argument is not used. If
-+ set and `--folder` is also used, `--folder` will be preferred.
++ folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
++ `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
++ the value of this configuration variable is used as the fallback
++ default value when the `--folder` option is not given.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
@@ Documentation/git-imap-send.adoc: git-imap-send - Send a collection of patches f
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
-+in which emails have the fields 'From', 'Date', and 'Subject' in
++in which emails have the fields `From`, `Date`, and `Subject` in
that order.
Typical usage is something like:
5: c2dfd0178c = 7: 999c65438f imap-send: fix minor mistakes in the logs
6: 4e1b51acd5 = 8: d0315aebd4 imap-send: display port alongwith host when git credential is invoked
7: 85c40d8491 = 9: 73352a18cf imap-send: display the destination mailbox when sending a message
8: 5e24c6cde8 ! 10: 36d50d01f0 imap-send: add ability to list the available folders
@@ Documentation/git-imap-send.adoc: OPTIONS
CONFIGURATION
-------------
-@@ Documentation/git-imap-send.adoc: Alternatively, use OAuth2.0 authentication as described below.
+@@ Documentation/git-imap-send.adoc: it. Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
--
2.49.0.639.g36d50d01f0
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v12 01/10] imap-send: fix bug causing cfg->folder being set to NULL
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-02 10:59 ` Aditya Garg
2025-06-02 10:59 ` [PATCH v12 02/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (8 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 10:59 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0.639.g36d50d01f0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v12 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-02 10:59 ` [PATCH v12 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-06-02 10:59 ` Aditya Garg
2025-06-05 8:00 ` Jeff King
2025-06-02 10:59 ` [PATCH v12 03/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (7 subsequent siblings)
9 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 10:59 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 47 +++++++-
imap-send.c | 182 +++++++++++++++++++++++++++++--
3 files changed, 221 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..29b998d5ff 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
+ `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext `LOGIN` command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..8adf0e5aac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,18 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your regular password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you
+can generate an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create
+it. Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +122,35 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify
+`OAUTHBEARER` or `XOAUTH2` mechanism in your config. It is more secure
+than using app-specific passwords, and also does not enforce the need of
+having multi-factor authentication. You will have to use an OAuth2.0
+access token in place of your password when using this authentication.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +159,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..37a8b48ea2 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2,
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,108 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +1001,9 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+#define auth_oauthbearer NULL
+#define auth_xoauth2 NULL
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -1104,6 +1213,50 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+
+ #ifdef NO_OPENSSL
+ fprintf(stderr, "You are trying to use OAUTHBEARER authentication mechanism "
+ "with OpenSSL library, but its support has not been compiled in.");
+ goto bail;
+ #endif
+
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+
+ #ifdef NO_OPENSSL
+ fprintf(stderr, "You are trying to use XOAUTH2 authentication mechanism "
+ "with OpenSSL library, but its support has not been compiled in.");
+ goto bail;
+ #endif
+
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1558,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
+ strcmp(srvc->auth_method, "XOAUTH2") ||
+ strcmp(srvc->auth_method, "OAUTHBEARER"))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1580,22 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /*
+ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0.639.g36d50d01f0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v12 03/10] imap-send: add PLAIN authentication method to OpenSSL
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-02 10:59 ` [PATCH v12 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-02 10:59 ` [PATCH v12 02/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-02 10:59 ` Aditya Garg
2025-06-02 10:59 ` [PATCH v12 04/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (6 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 10:59 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +-
imap-send.c | 82 +++++++++++++++++++++++++++++++++-
2 files changed, 83 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 29b998d5ff..7c8b2dcce4 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
- `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are `PLAIN`, `CRAM-MD5`, `OAUTHBEARER`
+ and `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
plaintext `LOGIN` command.
diff --git a/imap-send.c b/imap-send.c
index 37a8b48ea2..67077c2bd2 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2,
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,41 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +988,26 @@ static char *xoauth2_base64(const char *user, const char *access_token)
return b64;
}
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
{
int ret;
@@ -1001,6 +1058,7 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+#define auth_plain NULL
#define auth_oauthbearer NULL
#define auth_xoauth2 NULL
@@ -1198,7 +1256,29 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (srvc->auth_method) {
struct imap_cmd_cb cb;
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (!CAP(AUTH_PLAIN)) {
+ fprintf(stderr, "You specified "
+ "PLAIN as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+
+ #ifdef NO_OPENSSL
+ fprintf(stderr, "You are trying to use PLAIN authentication mechanism "
+ "with OpenSSL library, but its support has not been compiled in.");
+ goto bail;
+ #endif
+
+ /* PLAIN */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_plain;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
"CRAM-MD5 as authentication method, "
--
2.49.0.639.g36d50d01f0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v12 04/10] imap-send: fix memory leak in case auth_cram_md5 fails
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-06-02 10:59 ` [PATCH v12 03/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-06-02 10:59 ` Aditya Garg
2025-06-02 10:59 ` [PATCH v12 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
` (5 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 10:59 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 67077c2bd2..5f31dad3b0 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1072,8 +1072,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0.639.g36d50d01f0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v12 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-06-02 10:59 ` [PATCH v12 04/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-06-02 10:59 ` Aditya Garg
2025-06-02 10:59 ` [PATCH v12 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
` (4 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 10:59 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Unlike PLAIN, XOAUTH2 and OAUTHBEARER, CRAM-MD5 authentication is not
supported by libcurl and requires OpenSSL. If the user tries to use
CRAM-MD5 authentication without OpenSSL, the previous behaviour was to
attempt to authenticate and fail with a die(error). Handle this in a
better way by first checking if OpenSSL is available and then attempting
to authenticate. If OpenSSL is not available, print an error message and
exit gracefully.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 52 ++++++++++++++++++++++++++--------------------------
1 file changed, 26 insertions(+), 26 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 5f31dad3b0..879c72a606 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1008,6 +1008,24 @@ static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
return 0;
}
+static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+{
+ int ret;
+ char *response;
+
+ response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
+
+ ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
+ if (ret != strlen(response)) {
+ free(response);
+ return error("IMAP error: sending response failed");
+ }
+
+ free(response);
+
+ return 0;
+}
+
static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
{
int ret;
@@ -1050,38 +1068,13 @@ static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
#else
-static char *cram(const char *challenge_64 UNUSED,
- const char *user UNUSED,
- const char *pass UNUSED)
-{
- die("If you want to use CRAM-MD5 authenticate method, "
- "you have to build git-imap-send with OpenSSL library.");
-}
-
#define auth_plain NULL
+#define auth_cram_md5 NULL
#define auth_oauthbearer NULL
#define auth_xoauth2 NULL
#endif
-static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
-{
- int ret;
- char *response;
-
- response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
-
- ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response)) {
- free(response);
- return error("IMAP error: sending response failed");
- }
-
- free(response);
-
- return 0;
-}
-
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1287,6 +1280,13 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
"but %s doesn't support it.\n", srvc->host);
goto bail;
}
+
+ #ifdef NO_OPENSSL
+ fprintf(stderr, "If you want to use CRAM-MD5 authentication mechanism, "
+ "you have to build git-imap-send with OpenSSL library.");
+ goto bail;
+ #endif
+
/* CRAM-MD5 */
memset(&cb, 0, sizeof(cb));
--
2.49.0.639.g36d50d01f0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v12 06/10] imap-send: enable specifying the folder using the command line
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-06-02 10:59 ` [PATCH v12 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
@ 2025-06-02 10:59 ` Aditya Garg
2025-06-02 10:59 ` [PATCH v12 07/10] imap-send: fix minor mistakes in the logs Aditya Garg
` (3 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 10:59 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 6 ++++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 7c8b2dcce4..4682a6bd03 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,9 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
+ `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
+ the value of this configuration variable is used as the fallback
+ default value when the `--folder` option is not given.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 8adf0e5aac..4a0487b66e 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields `From`, `Date`, and `Subject` in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index 879c72a606..0e33baca7d 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1770,6 +1772,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0.639.g36d50d01f0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v12 07/10] imap-send: fix minor mistakes in the logs
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-06-02 10:59 ` [PATCH v12 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-06-02 10:59 ` Aditya Garg
2025-06-02 10:59 ` [PATCH v12 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
` (2 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 10:59 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some minor mistakes have been found in the logs. Most of them include
error messages starting with a capital letter, and ending with a period.
Also, abbreviations like "IMAP" and "OK" should be in uppercase. Fix them.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 0e33baca7d..3a1940e4a4 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -205,7 +205,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in.\n");
return -1;
}
@@ -1020,7 +1020,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1128,7 +1128,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1147,7 +1147,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1185,7 +1185,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1199,7 +1199,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1211,7 +1211,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1342,12 +1342,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
goto bail;
}
} else {
- fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ fprintf(stderr, "unknown authentication method:%s\n", srvc->host);
goto bail;
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "skipping account %s@%s, server forbids LOGIN\n",
srvc->user, srvc->host);
goto bail;
}
@@ -1603,7 +1603,7 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
@@ -1712,7 +1712,7 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
@@ -1796,13 +1796,13 @@ int cmd_main(int argc, const char **argv)
server.port = server.use_ssl ? 993 : 143;
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "no IMAP store specified\n");
ret = 1;
goto out;
}
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "no IMAP host specified\n");
ret = 1;
goto out;
}
@@ -1824,7 +1824,7 @@ int cmd_main(int argc, const char **argv)
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "no messages found to send\n");
ret = 1;
goto out;
}
--
2.49.0.639.g36d50d01f0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v12 08/10] imap-send: display port alongwith host when git credential is invoked
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-06-02 10:59 ` [PATCH v12 07/10] imap-send: fix minor mistakes in the logs Aditya Garg
@ 2025-06-02 10:59 ` Aditya Garg
2025-06-02 10:59 ` [PATCH v12 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-02 10:59 ` [PATCH v12 10/10] imap-send: add ability to list the available folders Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 10:59 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
FWIW, if no port is specified by the user, the default port, 993 for
IMAPS and 143 for IMAP is used by the code. So, the case of no port
defined for the helper is not possible, and therefore is not added.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 3a1940e4a4..61d52878c9 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1083,7 +1083,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0.639.g36d50d01f0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v12 09/10] imap-send: display the destination mailbox when sending a message
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-06-02 10:59 ` [PATCH v12 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-06-02 10:59 ` Aditya Garg
2025-06-02 10:59 ` [PATCH v12 10/10] imap-send: add ability to list the available folders Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 10:59 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
Sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 61d52878c9..39a42e6bc8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1603,7 +1603,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1712,7 +1713,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0.639.g36d50d01f0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v12 10/10] imap-send: add ability to list the available folders
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (8 preceding siblings ...)
2025-06-02 10:59 ` [PATCH v12 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-06-02 10:59 ` Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-02 10:59 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Various IMAP servers have different ways to name common folders.
For example, the folder where all deleted messages are stored is often
named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
on Gmail it is named "[Gmail]/Drafts".
This commit adds a `--list` command to the `imap-send` tool that lists
the available folders on the IMAP server, allowing users to see
which folders are available and how they are named. A sample output
looks like this when run against a Gmail server:
Fetching the list of available folders...
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasChildren \Noselect) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
For OpenSSL, this is achived by running the 'IMAP LIST' command and
parsing the response. This command is specified in RFC6154:
https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
For libcurl, the example code published in the libcurl documentation
is used to implement this functionality:
https://curl.se/libcurl/c/imap-list.html
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/git-imap-send.adoc | 6 +-
imap-send.c | 98 ++++++++++++++++++++++++++------
2 files changed, 87 insertions(+), 17 deletions(-)
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 4a0487b66e..17147f93c3 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+'git imap-send' --list
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
+--list::
+ Run the IMAP LIST command to output a list of all the folders present.
CONFIGURATION
-------------
@@ -123,7 +126,8 @@ it. Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
-that the "Folder doesn't exist".
+that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
+list of available folders.
[NOTE]
If your Gmail account is set to another language than English, the name of the "Drafts"
diff --git a/imap-send.c b/imap-send.c
index 39a42e6bc8..f6049222fd 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -45,15 +45,21 @@
#endif
static int verbosity;
+static int list_folders = 0;
static int use_curl = USE_CURL_DEFAULT;
static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
+ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
+ "git imap-send --list",
+ NULL
+};
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
+ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
OPT_END()
};
@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
if (b->buf[b->offset + 1] == '\n') {
b->buf[b->offset] = 0; /* terminate the string */
b->offset += 2; /* next line */
- if (0 < verbosity)
+ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
puts(*s);
return 0;
}
@@ -1626,6 +1632,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
+static int list_imap_folders(struct imap_server_conf *server)
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
+ fprintf(stderr, "failed to connect to IMAP server\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
+ fprintf(stderr, "failed to list folders\n");
+ imap_close_store(ctx);
+ return 1;
+ }
+
+ imap_close_store(ctx);
+ return 0;
+}
+
#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
@@ -1654,11 +1680,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
- die("failed to encode server folder");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+ die("failed to encode server folder");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ -1689,10 +1717,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
-
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(curl);
@@ -1711,6 +1735,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
struct credential cred = CREDENTIAL_INIT;
curl = setup_curl(server, &cred);
+
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
fprintf(stderr, "Sending %d message%s to %s folder...\n",
@@ -1757,6 +1785,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
+
+static int curl_list_imap_folders(struct imap_server_conf *server)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct credential cred = CREDENTIAL_INIT;
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ curl = setup_curl(server, &cred);
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ if (cred.username) {
+ if (res == CURLE_OK)
+ credential_approve(the_repository, &cred);
+ else if (res == CURLE_LOGIN_DENIED)
+ credential_reject(the_repository, &cred);
+ }
+
+ credential_clear(&cred);
+
+ return res != CURLE_OK;
+}
#endif
int cmd_main(int argc, const char **argv)
@@ -1797,11 +1850,6 @@ int cmd_main(int argc, const char **argv)
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
- fprintf(stderr, "no IMAP store specified\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "no IMAP host specified\n");
@@ -1811,6 +1859,24 @@ int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
+ if (list_folders) {
+ if (server.tunnel)
+ ret = list_imap_folders(&server);
+#ifdef USE_CURL_FOR_IMAP_SEND
+ else if (use_curl)
+ ret = curl_list_imap_folders(&server);
+#endif
+ else
+ ret = list_imap_folders(&server);
+ goto out;
+ }
+
+ if (!server.folder) {
+ fprintf(stderr, "no IMAP store specified\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("could not read from stdin"));
--
2.49.0.639.g36d50d01f0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v12 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-02 10:59 ` [PATCH v12 02/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-05 8:00 ` Jeff King
2025-06-05 8:12 ` Aditya Garg
2025-06-05 16:28 ` Junio C Hamano
0 siblings, 2 replies; 248+ messages in thread
From: Jeff King @ 2025-06-05 8:00 UTC (permalink / raw)
To: Aditya Garg
Cc: Junio C Hamano, git, Eric Sunshine, Zi Yao, brian m . carlson,
Ben Knoble, Phillip Wood
On Mon, Jun 02, 2025 at 04:29:33PM +0530, Aditya Garg wrote:
> @@ -1405,7 +1558,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
>
> server_fill_credential(srvc, cred);
> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
> - curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
> +
> + if (!srvc->auth_method ||
> + strcmp(srvc->auth_method, "XOAUTH2") ||
> + strcmp(srvc->auth_method, "OAUTHBEARER"))
> + curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
Coverity complains that this "if" will always be true, since one of the
strcmp() calls must return non-zero (srvc->auth_method cannot match both
strings!).
I'm not sure what the logic is supposed to be here. If we are matching
either string, it should be !strcmp() for both. If we want to match
neither, then it should be &&, not ||.
-Peff
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v12 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-05 8:00 ` Jeff King
@ 2025-06-05 8:12 ` Aditya Garg
2025-06-05 16:08 ` Junio C Hamano
2025-06-05 16:28 ` Junio C Hamano
1 sibling, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 8:12 UTC (permalink / raw)
To: Jeff King
Cc: Junio C Hamano, git@vger.kernel.org, Eric Sunshine, Zi Yao,
brian m carlson, Ben Knoble, Phillip Wood
> On 5 Jun 2025, at 1:30 PM, Jeff King <peff@peff.net> wrote:
>
> On Mon, Jun 02, 2025 at 04:29:33PM +0530, Aditya Garg wrote:
>
>> @@ -1405,7 +1558,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
>>
>> server_fill_credential(srvc, cred);
>> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
>> - curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>> +
>> + if (!srvc->auth_method ||
>> + strcmp(srvc->auth_method, "XOAUTH2") ||
>> + strcmp(srvc->auth_method, "OAUTHBEARER"))
>> + curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>
> Coverity complains that this "if" will always be true, since one of the
> strcmp() calls must return non-zero (srvc->auth_method cannot match both
> strings!).
>
> I'm not sure what the logic is supposed to be here. If we are matching
> either string, it should be !strcmp() for both. If we want to match
> neither, then it should be &&, not ||.
Good catch. The aim was to not execute that statement if authentication is
XOAUTH2 or OAUTHBEARER. I'll fix this logic.
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (16 preceding siblings ...)
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-05 8:42 ` Aditya Garg
2025-06-05 8:42 ` [PATCH v13 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (9 more replies)
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 subsequent siblings)
23 siblings, 10 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 8:42 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch series does the following things:
Firstly it basically makes the imap-send command usable again since it
was broken because of not being able to correctly parse the config file.
Further it adds support for OAuth2.0 and PLAIN authentication to git
imap-send.
Last, it does some minor improvements including adding the ability to
specify the folder using the command line and ability to list the
available folders by adding a `--list` option.
P.S.: I am surprised this thing even exists xD.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
v8: - Drop the patch that enabled user to choose between libcurl and openssl using the config
- Add ability to list the available folders by adding a `--list` option
v9: - Encourage users to use OAuth2.0 for Gmail (similar change done for send-email docs).
v10: - Fix comment styles
- Fix failing tests
v11: - Use lower case letters for the first word of a sendtence in an error message
and avoid using full stops at the end of a sentence.
v12: - Gracefully exit PLAIN, CRAM-MD5, OAUTHBEARER and XOAUTH2 authentication methods
if OpenSSL support is not compiled in, but is requested by the user.
- Use backticks for string literals.
- Wrap documentation text to 75 columns.
- End the last member of enum CAPABILITY with a trailing comma.
v13: - Fix logic error which was using || instead of && when checking if
the authentication method is neither XOAUTH2 nor OAUTHBEARER.
Aditya Garg (10):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: gracefully fail if CRAM-MD5 authentication is requested
without OpenSSL
imap-send: enable specifying the folder using the command line
imap-send: fix minor mistakes in the logs
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
imap-send: add ability to list the available folders
Documentation/config/imap.adoc | 11 +-
Documentation/git-imap-send.adoc | 68 ++++-
imap-send.c | 425 +++++++++++++++++++++++++++----
3 files changed, 441 insertions(+), 63 deletions(-)
Range-diff against v12:
-: ---------- > 1: 3e3ddf7077 imap-send: fix bug causing cfg->folder being set to NULL
1: ab12f713d2 ! 2: 0d28e337cf imap-send: add support for OAuth2.0 authentication
@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct crede
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
-+ strcmp(srvc->auth_method, "XOAUTH2") ||
-+ strcmp(srvc->auth_method, "OAUTHBEARER"))
++ (strcmp(srvc->auth_method, "XOAUTH2") &&
++ strcmp(srvc->auth_method, "OAUTHBEARER")))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
2: ba9c3fb756 = 3: d934bdcb82 imap-send: add PLAIN authentication method to OpenSSL
3: 3d1a66da57 = 4: f2773c646f imap-send: fix memory leak in case auth_cram_md5 fails
4: 70bb9388b8 = 5: c111ee6bc1 imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
5: 0d00a5e135 = 6: f12713f24b imap-send: enable specifying the folder using the command line
6: 999c65438f = 7: d38caeae5e imap-send: fix minor mistakes in the logs
7: d0315aebd4 = 8: 3ba02f2b0c imap-send: display port alongwith host when git credential is invoked
8: 73352a18cf = 9: 6dbd0bf0bc imap-send: display the destination mailbox when sending a message
9: 36d50d01f0 = 10: f77f2423e1 imap-send: add ability to list the available folders
--
2.49.0.639.gf77f2423e1
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v13 01/10] imap-send: fix bug causing cfg->folder being set to NULL
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-05 8:42 ` Aditya Garg
2025-06-05 8:42 ` [PATCH v13 02/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (8 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 8:42 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0.639.gf77f2423e1
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v13 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-05 8:42 ` [PATCH v13 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-06-05 8:42 ` Aditya Garg
2025-06-05 16:33 ` Junio C Hamano
2025-06-05 8:42 ` [PATCH v13 04/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (7 subsequent siblings)
9 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 8:42 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 47 +++++++-
imap-send.c | 182 +++++++++++++++++++++++++++++--
3 files changed, 221 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..29b998d5ff 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
+ `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext `LOGIN` command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..8adf0e5aac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,18 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your regular password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you
+can generate an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create
+it. Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +122,35 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify
+`OAUTHBEARER` or `XOAUTH2` mechanism in your config. It is more secure
+than using app-specific passwords, and also does not enforce the need of
+having multi-factor authentication. You will have to use an OAuth2.0
+access token in place of your password when using this authentication.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +159,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..829e957abd 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2,
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,108 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +1001,9 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+#define auth_oauthbearer NULL
+#define auth_xoauth2 NULL
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -1104,6 +1213,50 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (!CAP(AUTH_OAUTHBEARER)) {
+ fprintf(stderr, "You specified "
+ "OAUTHBEARER as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+
+ #ifdef NO_OPENSSL
+ fprintf(stderr, "You are trying to use OAUTHBEARER authentication mechanism "
+ "with OpenSSL library, but its support has not been compiled in.");
+ goto bail;
+ #endif
+
+ /* OAUTHBEARER */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_oauthbearer;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (!CAP(AUTH_XOAUTH2)) {
+ fprintf(stderr, "You specified "
+ "XOAUTH2 as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+
+ #ifdef NO_OPENSSL
+ fprintf(stderr, "You are trying to use XOAUTH2 authentication mechanism "
+ "with OpenSSL library, but its support has not been compiled in.");
+ goto bail;
+ #endif
+
+ /* XOAUTH2 */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_xoauth2;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
+ goto bail;
+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1558,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ if (!srvc->auth_method ||
+ (strcmp(srvc->auth_method, "XOAUTH2") &&
+ strcmp(srvc->auth_method, "OAUTHBEARER")))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1580,22 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /*
+ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0.639.gf77f2423e1
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v13 03/10] imap-send: add PLAIN authentication method to OpenSSL
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-06-05 8:42 ` [PATCH v13 04/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-06-05 8:42 ` Aditya Garg
2025-06-05 8:42 ` [PATCH v13 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
` (5 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 8:42 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +-
imap-send.c | 82 +++++++++++++++++++++++++++++++++-
2 files changed, 83 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 29b998d5ff..7c8b2dcce4 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
- `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are `PLAIN`, `CRAM-MD5`, `OAUTHBEARER`
+ and `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
plaintext `LOGIN` command.
diff --git a/imap-send.c b/imap-send.c
index 829e957abd..38f09f1f02 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2,
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,41 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +988,26 @@ static char *xoauth2_base64(const char *user, const char *access_token)
return b64;
}
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
{
int ret;
@@ -1001,6 +1058,7 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+#define auth_plain NULL
#define auth_oauthbearer NULL
#define auth_xoauth2 NULL
@@ -1198,7 +1256,29 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (srvc->auth_method) {
struct imap_cmd_cb cb;
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (!CAP(AUTH_PLAIN)) {
+ fprintf(stderr, "You specified "
+ "PLAIN as authentication method, "
+ "but %s doesn't support it.\n", srvc->host);
+ goto bail;
+ }
+
+ #ifdef NO_OPENSSL
+ fprintf(stderr, "You are trying to use PLAIN authentication mechanism "
+ "with OpenSSL library, but its support has not been compiled in.");
+ goto bail;
+ #endif
+
+ /* PLAIN */
+
+ memset(&cb, 0, sizeof(cb));
+ cb.cont = auth_plain;
+ if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
+ goto bail;
+ }
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
"CRAM-MD5 as authentication method, "
--
2.49.0.639.gf77f2423e1
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v13 04/10] imap-send: fix memory leak in case auth_cram_md5 fails
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-05 8:42 ` [PATCH v13 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-05 8:42 ` [PATCH v13 02/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-05 8:42 ` Aditya Garg
2025-06-05 8:42 ` [PATCH v13 03/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (6 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 8:42 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 38f09f1f02..072c8f4e39 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1072,8 +1072,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0.639.gf77f2423e1
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v13 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-06-05 8:42 ` [PATCH v13 03/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-06-05 8:42 ` Aditya Garg
2025-06-05 8:42 ` [PATCH v13 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
` (4 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 8:42 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Unlike PLAIN, XOAUTH2 and OAUTHBEARER, CRAM-MD5 authentication is not
supported by libcurl and requires OpenSSL. If the user tries to use
CRAM-MD5 authentication without OpenSSL, the previous behaviour was to
attempt to authenticate and fail with a die(error). Handle this in a
better way by first checking if OpenSSL is available and then attempting
to authenticate. If OpenSSL is not available, print an error message and
exit gracefully.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 52 ++++++++++++++++++++++++++--------------------------
1 file changed, 26 insertions(+), 26 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 072c8f4e39..6c7175ced0 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1008,6 +1008,24 @@ static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
return 0;
}
+static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+{
+ int ret;
+ char *response;
+
+ response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
+
+ ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
+ if (ret != strlen(response)) {
+ free(response);
+ return error("IMAP error: sending response failed");
+ }
+
+ free(response);
+
+ return 0;
+}
+
static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
{
int ret;
@@ -1050,38 +1068,13 @@ static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
#else
-static char *cram(const char *challenge_64 UNUSED,
- const char *user UNUSED,
- const char *pass UNUSED)
-{
- die("If you want to use CRAM-MD5 authenticate method, "
- "you have to build git-imap-send with OpenSSL library.");
-}
-
#define auth_plain NULL
+#define auth_cram_md5 NULL
#define auth_oauthbearer NULL
#define auth_xoauth2 NULL
#endif
-static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
-{
- int ret;
- char *response;
-
- response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
-
- ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response)) {
- free(response);
- return error("IMAP error: sending response failed");
- }
-
- free(response);
-
- return 0;
-}
-
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1287,6 +1280,13 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
"but %s doesn't support it.\n", srvc->host);
goto bail;
}
+
+ #ifdef NO_OPENSSL
+ fprintf(stderr, "If you want to use CRAM-MD5 authentication mechanism, "
+ "you have to build git-imap-send with OpenSSL library.");
+ goto bail;
+ #endif
+
/* CRAM-MD5 */
memset(&cb, 0, sizeof(cb));
--
2.49.0.639.gf77f2423e1
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v13 06/10] imap-send: enable specifying the folder using the command line
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-06-05 8:42 ` [PATCH v13 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
@ 2025-06-05 8:42 ` Aditya Garg
2025-06-05 8:42 ` [PATCH v13 07/10] imap-send: fix minor mistakes in the logs Aditya Garg
` (3 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 8:42 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 6 ++++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 7c8b2dcce4..4682a6bd03 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,9 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
+ `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
+ the value of this configuration variable is used as the fallback
+ default value when the `--folder` option is not given.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 8adf0e5aac..4a0487b66e 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields `From`, `Date`, and `Subject` in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index 6c7175ced0..0e51bf2b85 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1770,6 +1772,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0.639.gf77f2423e1
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v13 07/10] imap-send: fix minor mistakes in the logs
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-06-05 8:42 ` [PATCH v13 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-06-05 8:42 ` Aditya Garg
2025-06-05 8:42 ` [PATCH v13 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
` (2 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 8:42 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some minor mistakes have been found in the logs. Most of them include
error messages starting with a capital letter, and ending with a period.
Also, abbreviations like "IMAP" and "OK" should be in uppercase. Fix them.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 0e51bf2b85..dcc12e5468 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -205,7 +205,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in.\n");
return -1;
}
@@ -1020,7 +1020,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1128,7 +1128,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1147,7 +1147,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1185,7 +1185,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1199,7 +1199,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1211,7 +1211,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1342,12 +1342,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
goto bail;
}
} else {
- fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ fprintf(stderr, "unknown authentication method:%s\n", srvc->host);
goto bail;
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "skipping account %s@%s, server forbids LOGIN\n",
srvc->user, srvc->host);
goto bail;
}
@@ -1603,7 +1603,7 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
@@ -1712,7 +1712,7 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
@@ -1796,13 +1796,13 @@ int cmd_main(int argc, const char **argv)
server.port = server.use_ssl ? 993 : 143;
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "no IMAP store specified\n");
ret = 1;
goto out;
}
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "no IMAP host specified\n");
ret = 1;
goto out;
}
@@ -1824,7 +1824,7 @@ int cmd_main(int argc, const char **argv)
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "no messages found to send\n");
ret = 1;
goto out;
}
--
2.49.0.639.gf77f2423e1
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v13 08/10] imap-send: display port alongwith host when git credential is invoked
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-06-05 8:42 ` [PATCH v13 07/10] imap-send: fix minor mistakes in the logs Aditya Garg
@ 2025-06-05 8:42 ` Aditya Garg
2025-06-05 8:42 ` [PATCH v13 10/10] imap-send: add ability to list the available folders Aditya Garg
2025-06-05 8:42 ` [PATCH v13 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 8:42 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
FWIW, if no port is specified by the user, the default port, 993 for
IMAPS and 143 for IMAP is used by the code. So, the case of no port
defined for the helper is not possible, and therefore is not added.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index dcc12e5468..edc6b1ec25 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1083,7 +1083,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0.639.gf77f2423e1
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v13 09/10] imap-send: display the destination mailbox when sending a message
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (8 preceding siblings ...)
2025-06-05 8:42 ` [PATCH v13 10/10] imap-send: add ability to list the available folders Aditya Garg
@ 2025-06-05 8:42 ` Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 8:42 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
Sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index edc6b1ec25..3ad916c6da 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1603,7 +1603,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1712,7 +1713,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0.639.gf77f2423e1
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v13 10/10] imap-send: add ability to list the available folders
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-06-05 8:42 ` [PATCH v13 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-06-05 8:42 ` Aditya Garg
2025-06-05 8:42 ` [PATCH v13 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 8:42 UTC (permalink / raw)
To: Junio C Hamano, git@vger.kernel.org
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Various IMAP servers have different ways to name common folders.
For example, the folder where all deleted messages are stored is often
named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
on Gmail it is named "[Gmail]/Drafts".
This commit adds a `--list` command to the `imap-send` tool that lists
the available folders on the IMAP server, allowing users to see
which folders are available and how they are named. A sample output
looks like this when run against a Gmail server:
Fetching the list of available folders...
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasChildren \Noselect) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
For OpenSSL, this is achived by running the 'IMAP LIST' command and
parsing the response. This command is specified in RFC6154:
https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
For libcurl, the example code published in the libcurl documentation
is used to implement this functionality:
https://curl.se/libcurl/c/imap-list.html
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/git-imap-send.adoc | 6 +-
imap-send.c | 98 ++++++++++++++++++++++++++------
2 files changed, 87 insertions(+), 17 deletions(-)
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 4a0487b66e..17147f93c3 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+'git imap-send' --list
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
+--list::
+ Run the IMAP LIST command to output a list of all the folders present.
CONFIGURATION
-------------
@@ -123,7 +126,8 @@ it. Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
-that the "Folder doesn't exist".
+that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
+list of available folders.
[NOTE]
If your Gmail account is set to another language than English, the name of the "Drafts"
diff --git a/imap-send.c b/imap-send.c
index 3ad916c6da..910e0ea133 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -45,15 +45,21 @@
#endif
static int verbosity;
+static int list_folders = 0;
static int use_curl = USE_CURL_DEFAULT;
static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
+ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
+ "git imap-send --list",
+ NULL
+};
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
+ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
OPT_END()
};
@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
if (b->buf[b->offset + 1] == '\n') {
b->buf[b->offset] = 0; /* terminate the string */
b->offset += 2; /* next line */
- if (0 < verbosity)
+ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
puts(*s);
return 0;
}
@@ -1626,6 +1632,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
+static int list_imap_folders(struct imap_server_conf *server)
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
+ fprintf(stderr, "failed to connect to IMAP server\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
+ fprintf(stderr, "failed to list folders\n");
+ imap_close_store(ctx);
+ return 1;
+ }
+
+ imap_close_store(ctx);
+ return 0;
+}
+
#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
@@ -1654,11 +1680,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
- die("failed to encode server folder");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+ die("failed to encode server folder");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ -1689,10 +1717,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
-
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(curl);
@@ -1711,6 +1735,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
struct credential cred = CREDENTIAL_INIT;
curl = setup_curl(server, &cred);
+
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
fprintf(stderr, "Sending %d message%s to %s folder...\n",
@@ -1757,6 +1785,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
+
+static int curl_list_imap_folders(struct imap_server_conf *server)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct credential cred = CREDENTIAL_INIT;
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ curl = setup_curl(server, &cred);
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ if (cred.username) {
+ if (res == CURLE_OK)
+ credential_approve(the_repository, &cred);
+ else if (res == CURLE_LOGIN_DENIED)
+ credential_reject(the_repository, &cred);
+ }
+
+ credential_clear(&cred);
+
+ return res != CURLE_OK;
+}
#endif
int cmd_main(int argc, const char **argv)
@@ -1797,11 +1850,6 @@ int cmd_main(int argc, const char **argv)
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
- fprintf(stderr, "no IMAP store specified\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "no IMAP host specified\n");
@@ -1811,6 +1859,24 @@ int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
+ if (list_folders) {
+ if (server.tunnel)
+ ret = list_imap_folders(&server);
+#ifdef USE_CURL_FOR_IMAP_SEND
+ else if (use_curl)
+ ret = curl_list_imap_folders(&server);
+#endif
+ else
+ ret = list_imap_folders(&server);
+ goto out;
+ }
+
+ if (!server.folder) {
+ fprintf(stderr, "no IMAP store specified\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("could not read from stdin"));
--
2.49.0.639.gf77f2423e1
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v12 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-05 8:12 ` Aditya Garg
@ 2025-06-05 16:08 ` Junio C Hamano
2025-06-05 16:17 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-05 16:08 UTC (permalink / raw)
To: Aditya Garg
Cc: Jeff King, git@vger.kernel.org, Eric Sunshine, Zi Yao,
brian m carlson, Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
>> On 5 Jun 2025, at 1:30 PM, Jeff King <peff@peff.net> wrote:
>>
>> On Mon, Jun 02, 2025 at 04:29:33PM +0530, Aditya Garg wrote:
>>
>>> @@ -1405,7 +1558,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
>>>
>>> server_fill_credential(srvc, cred);
>>> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
>>> - curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>>> +
>>> + if (!srvc->auth_method ||
>>> + strcmp(srvc->auth_method, "XOAUTH2") ||
>>> + strcmp(srvc->auth_method, "OAUTHBEARER"))
>>> + curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>>
>> Coverity complains that this "if" will always be true, since one of the
>> strcmp() calls must return non-zero (srvc->auth_method cannot match both
>> strings!).
>>
>> I'm not sure what the logic is supposed to be here. If we are matching
>> either string, it should be !strcmp() for both. If we want to match
>> neither, then it should be &&, not ||.
>
> Good catch. The aim was to not execute that statement if authentication is
> XOAUTH2 or OAUTHBEARER. I'll fix this logic.
Yup. I'll refrain from merging it down before the reroll.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v12 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-05 16:08 ` Junio C Hamano
@ 2025-06-05 16:17 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 16:17 UTC (permalink / raw)
To: Junio C Hamano
Cc: Jeff King, git@vger.kernel.org, Eric Sunshine, Zi Yao,
brian m carlson, Ben Knoble, Phillip Wood
> On 5 Jun 2025, at 9:38 PM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>>>> On 5 Jun 2025, at 1:30 PM, Jeff King <peff@peff.net> wrote:
>>>
>>> On Mon, Jun 02, 2025 at 04:29:33PM +0530, Aditya Garg wrote:
>>>
>>>> @@ -1405,7 +1558,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
>>>>
>>>> server_fill_credential(srvc, cred);
>>>> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
>>>> - curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>>>> +
>>>> + if (!srvc->auth_method ||
>>>> + strcmp(srvc->auth_method, "XOAUTH2") ||
>>>> + strcmp(srvc->auth_method, "OAUTHBEARER"))
>>>> + curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>>>
>>> Coverity complains that this "if" will always be true, since one of the
>>> strcmp() calls must return non-zero (srvc->auth_method cannot match both
>>> strings!).
>>>
>>> I'm not sure what the logic is supposed to be here. If we are matching
>>> either string, it should be !strcmp() for both. If we want to match
>>> neither, then it should be &&, not ||.
>>
>> Good catch. The aim was to not execute that statement if authentication is
>> XOAUTH2 or OAUTHBEARER. I'll fix this logic.
>
> Yup. I'll refrain from merging it down before the reroll.
Already sent a v13 :)
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v12 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-05 8:00 ` Jeff King
2025-06-05 8:12 ` Aditya Garg
@ 2025-06-05 16:28 ` Junio C Hamano
2025-06-05 22:50 ` Jeff King
1 sibling, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-05 16:28 UTC (permalink / raw)
To: Jeff King
Cc: Aditya Garg, git, Eric Sunshine, Zi Yao, brian m . carlson,
Ben Knoble, Phillip Wood
Jeff King <peff@peff.net> writes:
> On Mon, Jun 02, 2025 at 04:29:33PM +0530, Aditya Garg wrote:
>
>> @@ -1405,7 +1558,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
>>
>> server_fill_credential(srvc, cred);
>> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
>> - curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>> +
>> + if (!srvc->auth_method ||
>> + strcmp(srvc->auth_method, "XOAUTH2") ||
>> + strcmp(srvc->auth_method, "OAUTHBEARER"))
>> + curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>
> Coverity complains that this "if" will always be true, since one of the
> strcmp() calls must return non-zero (srvc->auth_method cannot match both
> strings!).
>
> I'm not sure what the logic is supposed to be here. If we are matching
> either string, it should be !strcmp() for both. If we want to match
> neither, then it should be &&, not ||.
"If XOAUTH2 or OAUTHBEARER, use the password" sounds somewhat
strange (unless the bearer token is stored in .pass and passed as if
it is a password).
"Unless XOAUTH2 or OAUTHBEARER, use the password" sounds even more
strange. What about other methods that are not a plain simple
password authentication? Will we remember extending this code when
we add yet another one to exclude it like XOAUTH2 and OAUTHBEARER
are excluded with this patch?
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v13 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-05 8:42 ` [PATCH v13 02/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-05 16:33 ` Junio C Hamano
2025-06-05 17:16 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-05 16:33 UTC (permalink / raw)
To: Aditya Garg
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m . carlson,
Jeff King, Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> + } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
> + if (!CAP(AUTH_OAUTHBEARER)) {
> + fprintf(stderr, "You specified "
> + "OAUTHBEARER as authentication method, "
> + "but %s doesn't support it.\n", srvc->host);
> + goto bail;
> + }
> +
> + #ifdef NO_OPENSSL
> + fprintf(stderr, "You are trying to use OAUTHBEARER authentication mechanism "
> + "with OpenSSL library, but its support has not been compiled in.");
> + goto bail;
> + #endif
Ugly. Can we avoid #ifdef/#endif in the middle of such a main flow
of the logic? Hiding such ugliness by indenting the #ifdef/#endif
directives as if they are just one of the code lines is doubly ugly.
> server_fill_credential(srvc, cred);
> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
> - curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
> +
> + if (!srvc->auth_method ||
> + (strcmp(srvc->auth_method, "XOAUTH2") &&
> + strcmp(srvc->auth_method, "OAUTHBEARER")))
> + curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
Can we clarify this part, possibly with an in-code comment?
"Unless XOAUTH2 or OAUTHBEARER, use the password" sounds a bit
strange. What about methods other than these two that are not a
plain simple password authentication? Will we remember extending
this code when we add yet another one to exclude it like XOAUTH2 and
OAUTHBEARER are excluded with this patch?
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v13 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-05 16:33 ` Junio C Hamano
@ 2025-06-05 17:16 ` Aditya Garg
2025-06-05 18:48 ` Junio C Hamano
0 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-05 17:16 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m . carlson,
Jeff King, Ben Knoble, Phillip Wood
On 5 June 2025 10:03:51 pm IST, Junio C Hamano <gitster@pobox.com> wrote:
>Aditya Garg <gargaditya08@live.com> writes:
>
>> + } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
>> + if (!CAP(AUTH_OAUTHBEARER)) {
>> + fprintf(stderr, "You specified "
>> + "OAUTHBEARER as authentication method, "
>> + "but %s doesn't support it.\n", srvc->host);
>> + goto bail;
>> + }
>> +
>> + #ifdef NO_OPENSSL
>> + fprintf(stderr, "You are trying to use OAUTHBEARER authentication mechanism "
>> + "with OpenSSL library, but its support has not been compiled in.");
>> + goto bail;
>> + #endif
>
>Ugly. Can we avoid #ifdef/#endif in the middle of such a main flow
>of the logic? Hiding such ugliness by indenting the #ifdef/#endif
>directives as if they are just one of the code lines is doubly ugly.
>
RESENDING AS PLAIN TEXT
Your suggestion in a previous review said:
if (!auth_oauthbearer) {
... we do not support ...
goto bail;
}
Might look less ugly, but will result in a compiler warning that this will always
be true if compiled with NO_OPENSSL. If you are fine with that, good. Else tbh
I am out of ideas :(.
>> server_fill_credential(srvc, cred);
>> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
>> - curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>> +
>> + if (!srvc->auth_method ||
>> + (strcmp(srvc->auth_method, "XOAUTH2") &&
>> + strcmp(srvc->auth_method, "OAUTHBEARER")))
>> + curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>
>Can we clarify this part, possibly with an in-code comment?
>
>"Unless XOAUTH2 or OAUTHBEARER, use the password" sounds a bit
>strange. What about methods other than these two that are not a
>plain simple password authentication? Will we remember extending
>this code when we add yet another one to exclude it like XOAUTH2 and
>OAUTHBEARER are excluded with this patch?
>
Let me answer this first. CURLOPT_PASSWORD is for plain or login type
authentication, and if srvc->auth_method is not defined, curl's behaviour
defaults to them. OAUTHBEARER and XOAUTH2 use CURLOPT_XOAUTH2_BEARER
in curl, which can use either of them based on what server says. Other auth methods
are not supported yet in this code, and this is the reason CRAM_MD5 is supported
by only OpenSSL.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v13 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-05 17:16 ` Aditya Garg
@ 2025-06-05 18:48 ` Junio C Hamano
2025-06-06 3:28 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-05 18:48 UTC (permalink / raw)
To: Aditya Garg
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m . carlson,
Jeff King, Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> Might look less ugly, but will result in a compiler warning that this will always
> be true if compiled with NO_OPENSSL. If you are fine with that, good. Else tbh
> I am out of ideas :(.
Sounds like a good place to use NOT_CONSTANT(), it seems?
if (NOT_CONSTANT(!auth_oauthbearer)) {
... skip the thing ...
}
>>> server_fill_credential(srvc, cred);
>>> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
>>> - curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>>> +
>>> + if (!srvc->auth_method ||
>>> + (strcmp(srvc->auth_method, "XOAUTH2") &&
>>> + strcmp(srvc->auth_method, "OAUTHBEARER")))
>>> + curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>>
>>Can we clarify this part, possibly with an in-code comment?
>>
>>"Unless XOAUTH2 or OAUTHBEARER, use the password" sounds a bit
>>strange. What about methods other than these two that are not a
>>plain simple password authentication? Will we remember extending
>>this code when we add yet another one to exclude it like XOAUTH2 and
>>OAUTHBEARER are excluded with this patch?
> Let me answer this first. CURLOPT_PASSWORD is for plain or login type
> authentication, and if srvc->auth_method is not defined, curl's behaviour
> defaults to them.
Which makes it sound like if (!srvc->auth_method) is enough?
> OAUTHBEARER and XOAUTH2 use CURLOPT_XOAUTH2_BEARER
> in curl, which can use either of them based on what server says.
That is what we can read from the updated code.
The question is what happens when the user sets srvc->auth_method to
something other than NULL (unused---use plain password), "XOAUTH2"
or "OAUTHBEARER".
If the answer to that question is ...
> Other auth methods
> are not supported yet in this code, and this is the reason CRAM_MD5 is supported
> by only OpenSSL.
... "with srvc->auth_method set to other methods like CRAM_MD5, the
control would never enter this codepath, as they are implemented
elsewhere", then I think it would make more sense to write the above
like this:
if (!srvc->auth_method)
curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
else if (strcmp(srvc->auth_method, "XOAUTH2") &&
strcmp(srvc->auth_method, "OAUTHBEARER"))
BUG("we only support XOAUTH2 and OAUTHBEARER in this codepath");
Or the code is not protecting this code path so control can reach
with auth_method set to CRAM_MD5 here (e.g. when built without
OpenSSL)? If so, replace BUG("message") with die(_("message"))
above.
On the other hand, if you are trying to fall back to plain password
when other unhandled methods are specified, I would expect that the
code to read more like:
if (srvc->auth_method &&
(!strcmp(srvc->auth_method, "XOAUTH2") ||
!strcmp(srvc->auth_method, "OAUTHBEARER")))
;
else {
if (srvc->auth_method)
warning("auth method %s not supported,
falling back to plain password",
srvc->auth_method);
curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
}
I cannot quite tell which one you meant, but I am guessing that the
former is the case from your explanation.
Thanks.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v12 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-05 16:28 ` Junio C Hamano
@ 2025-06-05 22:50 ` Jeff King
0 siblings, 0 replies; 248+ messages in thread
From: Jeff King @ 2025-06-05 22:50 UTC (permalink / raw)
To: Junio C Hamano
Cc: Aditya Garg, git, Eric Sunshine, Zi Yao, brian m . carlson,
Ben Knoble, Phillip Wood
On Thu, Jun 05, 2025 at 09:28:18AM -0700, Junio C Hamano wrote:
> Jeff King <peff@peff.net> writes:
>
> > On Mon, Jun 02, 2025 at 04:29:33PM +0530, Aditya Garg wrote:
> >
> >> @@ -1405,7 +1558,11 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
> >>
> >> server_fill_credential(srvc, cred);
> >> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
> >> - curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
> >> +
> >> + if (!srvc->auth_method ||
> >> + strcmp(srvc->auth_method, "XOAUTH2") ||
> >> + strcmp(srvc->auth_method, "OAUTHBEARER"))
> >> + curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
> >
> > Coverity complains that this "if" will always be true, since one of the
> > strcmp() calls must return non-zero (srvc->auth_method cannot match both
> > strings!).
> >
> > I'm not sure what the logic is supposed to be here. If we are matching
> > either string, it should be !strcmp() for both. If we want to match
> > neither, then it should be &&, not ||.
>
> "If XOAUTH2 or OAUTHBEARER, use the password" sounds somewhat
> strange (unless the bearer token is stored in .pass and passed as if
> it is a password).
>
> "Unless XOAUTH2 or OAUTHBEARER, use the password" sounds even more
> strange. What about other methods that are not a plain simple
> password authentication? Will we remember extending this code when
> we add yet another one to exclude it like XOAUTH2 and OAUTHBEARER
> are excluded with this patch?
That was my gut feeling, but I confess to not engaging my brain and was
just relaying the coverity message before logging off for the day. ;)
-Peff
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v13 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-05 18:48 ` Junio C Hamano
@ 2025-06-06 3:28 ` Aditya Garg
2025-06-06 4:04 ` Aditya Garg
2025-06-06 4:35 ` Junio C Hamano
0 siblings, 2 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 3:28 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m . carlson,
Jeff King, Ben Knoble, Phillip Wood
On 6 June 2025 12:18:25 am IST, Junio C Hamano <gitster@pobox.com> wrote:
>Aditya Garg <gargaditya08@live.com> writes:
>
>> Might look less ugly, but will result in a compiler warning that this will always
>> be true if compiled with NO_OPENSSL. If you are fine with that, good. Else tbh
>> I am out of ideas :(.
>
>Sounds like a good place to use NOT_CONSTANT(), it seems?
>
> if (NOT_CONSTANT(!auth_oauthbearer)) {
> ... skip the thing ...
> }
>
>
Ok
>>>> server_fill_credential(srvc, cred);
>>>> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
>>>> - curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>>>> +
>>>> + if (!srvc->auth_method ||
>>>> + (strcmp(srvc->auth_method, "XOAUTH2") &&
>>>> + strcmp(srvc->auth_method, "OAUTHBEARER")))
>>>> + curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>>>
>>>Can we clarify this part, possibly with an in-code comment?
>>>
>>>"Unless XOAUTH2 or OAUTHBEARER, use the password" sounds a bit
>>>strange. What about methods other than these two that are not a
>>>plain simple password authentication? Will we remember extending
>>>this code when we add yet another one to exclude it like XOAUTH2 and
>>>OAUTHBEARER are excluded with this patch?
>
>> Let me answer this first. CURLOPT_PASSWORD is for plain or login type
>> authentication, and if srvc->auth_method is not defined, curl's behaviour
>> defaults to them.
>
>Which makes it sound like if (!srvc->auth_method) is enough?
>
No. If the user specifies PLAIN or LOGIN then it's not enough.
>> OAUTHBEARER and XOAUTH2 use CURLOPT_XOAUTH2_BEARER
>> in curl, which can use either of them based on what server says.
>
>That is what we can read from the updated code.
>
>The question is what happens when the user sets srvc->auth_method to
>something other than NULL (unused---use plain password), "XOAUTH2"
>or "OAUTHBEARER".
>
>If the answer to that question is ...
>
>> Other auth methods
>> are not supported yet in this code, and this is the reason CRAM_MD5 is supported
>> by only OpenSSL.
>
>... "with srvc->auth_method set to other methods like CRAM_MD5, the
>control would never enter this codepath, as they are implemented
>elsewhere", then I think it would make more sense to write the above
>like this:
>
> if (!srvc->auth_method)
> curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
> else if (strcmp(srvc->auth_method, "XOAUTH2") &&
> strcmp(srvc->auth_method, "OAUTHBEARER"))
> BUG("we only support XOAUTH2 and OAUTHBEARER in this codepath");
>
We can implement this, but:
1. It will fail if user specifies PLAIN or LOGIN as auth method.
2. We have this in the code as well:
if (srvc->auth_method) {
struct strbuf auth = STRBUF_INIT;
strbuf_addstr(&auth, "AUTH=");
strbuf_addstr(&auth, srvc->auth_method);
curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
strbuf_release(&auth);
}
Which basically means that if a user specifies an auth method,
curl will try to use SMTP AUTH command with that method.
So ideally, this should have worked for OAUTHBEAER and XOAUTH2
But the problem with that would be a) we would need to format
the access token as per the specifications of these mechanisms.
and b) curl simply says these methods are not supported when
we try with that.
I filed a bug report regarding this and they were not really clear
on whether CURLOPT_LOGIN_OPTIONS is meant for PLAIN only or should work like this with other methods too.
But, the docs indicate it's for PLAIN auth only.
So, considering the fact that the original code for imap-send
was setting CURLOPT_LOGIN_OPTIONS
unconditionally and
was running the AUTH command even if auth was set to CRAM-MD5
or whatever, I just preferred to not change that behaviour since I
may cause some regression. There is a tiny possibility that CRAM-MD5
may work, but I don't really have any free SMTP server which uses
that method itself.
In short, just to be very safe here, I decided to not mingle with the
logic much and simple decided to use a seperate tested logic
for OAuth2.0 and let the same logic be used for rest cases.
Therefore, the previous logic said:
"Set CURLOPT_LOGIN_OPTIONS irrespective of whether there is
an auth method specified or not."
Now it says
"Set CURLOPT_LOGIN_OPTIONS irrespective of whether there is
an auth method specified or not, unless it's OAuth2.0, where we
use a different curl API"
The bug report I filed with curl for reference:
https://github.com/curl/curl/issues/17420
>Or the code is not protecting this code path so control can reach
>with auth_method set to CRAM_MD5 here (e.g. when built without
>OpenSSL)? If so, replace BUG("message") with die(_("message"))
>above.
>
>On the other hand, if you are trying to fall back to plain password
>when other unhandled methods are specified, I would expect that the
>code to read more like:
>
> if (srvc->auth_method &&
> (!strcmp(srvc->auth_method, "XOAUTH2") ||
> !strcmp(srvc->auth_method, "OAUTHBEARER")))
> ;
> else {
> if (srvc->auth_method)
> warning("auth method %s not supported,
> falling back to plain password",
> srvc->auth_method);
> curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
> }
>
>I cannot quite tell which one you meant, but I am guessing that the
>former is the case from your explanation.
>
>Thanks.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v13 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-06 3:28 ` Aditya Garg
@ 2025-06-06 4:04 ` Aditya Garg
2025-06-06 4:35 ` Junio C Hamano
1 sibling, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 4:04 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
> On 6 Jun 2025, at 8:58 AM, Aditya Garg <gargaditya08@live.com> wrote:
>
>
>
>> On 6 June 2025 12:18:25 am IST, Junio C Hamano <gitster@pobox.com> wrote:
>> Aditya Garg <gargaditya08@live.com> writes:
>>
>>> Might look less ugly, but will result in a compiler warning that this will always
>>> be true if compiled with NO_OPENSSL. If you are fine with that, good. Else tbh
>>> I am out of ideas :(.
>>
>> Sounds like a good place to use NOT_CONSTANT(), it seems?
>>
>> if (NOT_CONSTANT(!auth_oauthbearer)) {
>> ... skip the thing ...
>> }
>>
>>
>
> Ok
>
>>>>> server_fill_credential(srvc, cred);
>>>>> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
>>>>> - curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>>>>> +
>>>>> + if (!srvc->auth_method ||
>>>>> + (strcmp(srvc->auth_method, "XOAUTH2") &&
>>>>> + strcmp(srvc->auth_method, "OAUTHBEARER")))
>>>>> + curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>>>>
>>>> Can we clarify this part, possibly with an in-code comment?
>>>>
>>>> "Unless XOAUTH2 or OAUTHBEARER, use the password" sounds a bit
>>>> strange. What about methods other than these two that are not a
>>>> plain simple password authentication? Will we remember extending
>>>> this code when we add yet another one to exclude it like XOAUTH2 and
>>>> OAUTHBEARER are excluded with this patch?
>>
>>> Let me answer this first. CURLOPT_PASSWORD is for plain or login type
>>> authentication, and if srvc->auth_method is not defined, curl's behaviour
>>> defaults to them.
>>
>> Which makes it sound like if (!srvc->auth_method) is enough?
>>
>
> No. If the user specifies PLAIN or LOGIN then it's not enough.
>
>>> OAUTHBEARER and XOAUTH2 use CURLOPT_XOAUTH2_BEARER
>>> in curl, which can use either of them based on what server says.
>>
>> That is what we can read from the updated code.
>>
>> The question is what happens when the user sets srvc->auth_method to
>> something other than NULL (unused---use plain password), "XOAUTH2"
>> or "OAUTHBEARER".
>>
>> If the answer to that question is ...
>>
>>> Other auth methods
>>> are not supported yet in this code, and this is the reason CRAM_MD5 is supported
>>> by only OpenSSL.
>>
>> ... "with srvc->auth_method set to other methods like CRAM_MD5, the
>> control would never enter this codepath, as they are implemented
>> elsewhere", then I think it would make more sense to write the above
>> like this:
>>
>> if (!srvc->auth_method)
>> curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>> else if (strcmp(srvc->auth_method, "XOAUTH2") &&
>> strcmp(srvc->auth_method, "OAUTHBEARER"))
>> BUG("we only support XOAUTH2 and OAUTHBEARER in this codepath");
>>
>
> We can implement this, but:
>
> 1. It will fail if user specifies PLAIN or LOGIN as auth method.
>
> 2. We have this in the code as well:
>
> if (srvc->auth_method) {
> struct strbuf auth = STRBUF_INIT;
> strbuf_addstr(&auth, "AUTH=");
> strbuf_addstr(&auth, srvc->auth_method);
> curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
> strbuf_release(&auth);
> }
>
> Which basically means that if a user specifies an auth method,
> curl will try to use SMTP AUTH command with that method.
> So ideally, this should have worked for OAUTHBEAER and XOAUTH2
>
> But the problem with that would be a) we would need to format
> the access token as per the specifications of these mechanisms.
> and b) curl simply says these methods are not supported when
> we try with that.
>
> I filed a bug report regarding this and they were not really clear
> on whether CURLOPT_LOGIN_OPTIONS is meant for PLAIN only or should work like this with other methods too.
>
> But, the docs indicate it's for PLAIN auth only.
>
> So, considering the fact that the original code for imap-send
> was setting CURLOPT_LOGIN_OPTIONS
> unconditionally and
> was running the AUTH command even if auth was set to CRAM-MD5
> or whatever, I just preferred to not change that behaviour since I
> may cause some regression. There is a tiny possibility that CRAM-MD5
> may work, but I don't really have any free SMTP server which uses
SMTP was a typo. It's IMAP.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v13 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-06 3:28 ` Aditya Garg
2025-06-06 4:04 ` Aditya Garg
@ 2025-06-06 4:35 ` Junio C Hamano
2025-06-06 4:40 ` Aditya Garg
1 sibling, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-06 4:35 UTC (permalink / raw)
To: Aditya Garg
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m . carlson,
Jeff King, Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
>>Which makes it sound like if (!srvc->auth_method) is enough?
>>
>
> No. If the user specifies PLAIN or LOGIN then it's not enough.
That invites another question. Why aren't we checking for PLAIN or
LOGIN and when one of them is given use the password? What is
written in the patch looks backwards, in that we seem to assume that
a method that is not XOAUTH2 and OAUTHBEARER must be PLAIN or LOGIN
(or anything that wants us to pass CURLOPT_PASSWORD). IOW, why
isn't the code more like
if (/* not using the more advanced method interface? */
!srvc->auth_method ||
/* method interface that takes password? */
!strcmp(srvc->auth_method, "PLAIN") ||
!strcmp(srvc->auth_method, "LOGIN"))
curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
if, after all, PLAIN/LOGIN are what triggers password
authentication?
> So, considering the fact that the original code for imap-send
> was setting CURLOPT_LOGIN_OPTIONS
> unconditionally and
> was running the AUTH command even if auth was set to CRAM-MD5
> or whatever, I just preferred to not change that behaviour since I
> may cause some regression. There is a tiny possibility that CRAM-MD5
> may work, but I don't really have any free SMTP server which uses
> that method itself.
>
> In short, just to be very safe here, I decided to not mingle with the
> logic much and simple decided to use a seperate tested logic
> for OAuth2.0 and let the same logic be used for rest cases.
Don't be short ;-) Be long in your log message to help future
developers. In short, you want to make your proposed log message so
clear that future developers who found this commit in "git log -p"
to come asking you these questions---that is why reviewers are
supposed to ask questions and ask for clarifications.
> "Set CURLOPT_LOGIN_OPTIONS irrespective of whether there is
> an auth method specified or not, unless it's OAuth2.0, where we
> use a different curl API"
That is a very good thing to write down either in-code comment
and/or the log message to avoid future developers come bugging you
with the same questions as I did.
Thanks.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v13 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-06 4:35 ` Junio C Hamano
@ 2025-06-06 4:40 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 4:40 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
> On 6 Jun 2025, at 10:05 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>>> Which makes it sound like if (!srvc->auth_method) is enough?
>>>
>>
>> No. If the user specifies PLAIN or LOGIN then it's not enough.
>
> That invites another question. Why aren't we checking for PLAIN or
> LOGIN and when one of them is given use the password? What is
> written in the patch looks backwards, in that we seem to assume that
> a method that is not XOAUTH2 and OAUTHBEARER must be PLAIN or LOGIN
> (or anything that wants us to pass CURLOPT_PASSWORD). IOW, why
> isn't the code more like
>
> if (/* not using the more advanced method interface? */
> !srvc->auth_method ||
> /* method interface that takes password? */
> !strcmp(srvc->auth_method, "PLAIN") ||
> !strcmp(srvc->auth_method, "LOGIN"))
> curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>
> if, after all, PLAIN/LOGIN are what triggers password
> authentication?
>
>> So, considering the fact that the original code for imap-send
>> was setting CURLOPT_LOGIN_OPTIONS
>> unconditionally and
>> was running the AUTH command even if auth was set to CRAM-MD5
>> or whatever, I just preferred to not change that behaviour since I
>> may cause some regression. There is a tiny possibility that CRAM-MD5
>> may work, but I don't really have any free SMTP server which uses
>> that method itself.
>>
>> In short, just to be very safe here, I decided to not mingle with the
>> logic much and simple decided to use a seperate tested logic
>> for OAuth2.0 and let the same logic be used for rest cases.
>
> Don't be short ;-) Be long in your log message to help future
> developers. In short, you want to make your proposed log message so
> clear that future developers who found this commit in "git log -p"
> to come asking you these questions---that is why reviewers are
> supposed to ask questions and ask for clarifications.
Ok :). So, you want me to add checks for PLAIN and LOGIN, or the current
logic is fine. I'd prefer using the current logic to avoid potential regressions,
but its your call.
>
>> "Set CURLOPT_LOGIN_OPTIONS irrespective of whether there is
>> an auth method specified or not, unless it's OAuth2.0, where we
>> use a different curl API"
>
> That is a very good thing to write down either in-code comment
> and/or the log message to avoid future developers come bugging you
> with the same questions as I did.
Alright, I'll add it as a comment in the code.
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (17 preceding siblings ...)
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-06 20:06 ` Aditya Garg
2025-06-06 20:06 ` [PATCH v14 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (9 more replies)
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 subsequent siblings)
23 siblings, 10 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 20:06 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch series does the following things:
Firstly it basically makes the imap-send command usable again since it
was broken because of not being able to correctly parse the config file.
Further it adds support for OAuth2.0 and PLAIN authentication to git
imap-send.
Last, it does some minor improvements including adding the ability to
specify the folder using the command line and ability to list the
available folders by adding a `--list` option.
P.S.: I am surprised this thing even exists xD.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
v8: - Drop the patch that enabled user to choose between libcurl and openssl using the config
- Add ability to list the available folders by adding a `--list` option
v9: - Encourage users to use OAuth2.0 for Gmail (similar change done for send-email docs).
v10: - Fix comment styles
- Fix failing tests
v11: - Use lower case letters for the first word of a sendtence in an error message
and avoid using full stops at the end of a sentence.
v12: - Gracefully exit PLAIN, CRAM-MD5, OAUTHBEARER and XOAUTH2 authentication methods
if OpenSSL support is not compiled in, but is requested by the user.
- Use backticks for string literals.
- Wrap documentation text to 75 columns.
- End the last member of enum CAPABILITY with a trailing comma.
v13: - Fix logic error which was using || instead of && when checking if
the authentication method is neither XOAUTH2 nor OAUTHBEARER.
v14: - Specify why we are not using CURLOPT_PASSWORD for OAuth2.0
methods using a comment.
- Add a function try_auth_method() to reduce code duplication
when trying to authenticate using a specific method.
Aditya Garg (10):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: gracefully fail if CRAM-MD5 authentication is requested
without OpenSSL
imap-send: enable specifying the folder using the command line
imap-send: fix minor mistakes in the logs
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
imap-send: add ability to list the available folders
Documentation/config/imap.adoc | 11 +-
Documentation/git-imap-send.adoc | 68 ++++-
imap-send.c | 412 ++++++++++++++++++++++++++-----
3 files changed, 414 insertions(+), 77 deletions(-)
Range-diff against v13:
-: ---------- > 1: 3e3ddf7077 imap-send: fix bug causing cfg->folder being set to NULL
1: 0d28e337cf ! 2: 34d56c3b57 imap-send: add support for OAuth2.0 authentication
@@ imap-send.c: static char *cram(const char *challenge_64 UNUSED,
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+@@ imap-send.c: static void server_fill_credential(struct imap_server_conf *srvc, struct credent
+ srvc->pass = xstrdup(cred->password);
+ }
+
++static int try_auth_method(struct imap_server_conf *srvc,
++ struct imap_store *ctx,
++ struct imap *imap,
++ const char *auth_method,
++ enum CAPABILITY cap,
++ int (*fn)(struct imap_store *, const char *))
++{
++ struct imap_cmd_cb cb = {0};
++
++ if (!CAP(cap)) {
++ fprintf(stderr, "You specified "
++ "%s as authentication method, "
++ "but %s doesn't support it.\n",
++ auth_method, srvc->host);
++ return -1;
++ }
++ cb.cont = fn;
++
++ if (NOT_CONSTANT(!cb.cont)) {
++ fprintf(stderr, "If you want to use %s authentication mechanism, "
++ "you have to build git-imap-send with OpenSSL library.",
++ auth_method);
++ return -1;
++ }
++ if (imap_exec(ctx, &cb, "AUTHENTICATE %s", auth_method) != RESP_OK) {
++ fprintf(stderr, "IMAP error: AUTHENTICATE %s failed\n",
++ auth_method);
++ return -1;
++ }
++ return 0;
++}
++
+ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const char *folder)
+ {
+ struct credential cred = CREDENTIAL_INIT;
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
-+ if (!CAP(AUTH_OAUTHBEARER)) {
-+ fprintf(stderr, "You specified "
-+ "OAUTHBEARER as authentication method, "
-+ "but %s doesn't support it.\n", srvc->host);
++ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
+ goto bail;
-+ }
-+
-+ #ifdef NO_OPENSSL
-+ fprintf(stderr, "You are trying to use OAUTHBEARER authentication mechanism "
-+ "with OpenSSL library, but its support has not been compiled in.");
-+ goto bail;
-+ #endif
-+
-+ /* OAUTHBEARER */
-+
-+ memset(&cb, 0, sizeof(cb));
-+ cb.cont = auth_oauthbearer;
-+ if (imap_exec(ctx, &cb, "AUTHENTICATE OAUTHBEARER") != RESP_OK) {
-+ fprintf(stderr, "IMAP error: AUTHENTICATE OAUTHBEARER failed\n");
-+ goto bail;
-+ }
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
-+ if (!CAP(AUTH_XOAUTH2)) {
-+ fprintf(stderr, "You specified "
-+ "XOAUTH2 as authentication method, "
-+ "but %s doesn't support it.\n", srvc->host);
-+ goto bail;
-+ }
-+
-+ #ifdef NO_OPENSSL
-+ fprintf(stderr, "You are trying to use XOAUTH2 authentication mechanism "
-+ "with OpenSSL library, but its support has not been compiled in.");
-+ goto bail;
-+ #endif
-+
-+ /* XOAUTH2 */
-+
-+ memset(&cb, 0, sizeof(cb));
-+ cb.cont = auth_xoauth2;
-+ if (imap_exec(ctx, &cb, "AUTHENTICATE XOAUTH2") != RESP_OK) {
-+ fprintf(stderr, "IMAP error: AUTHENTICATE XOAUTH2 failed\n");
++ if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
+ goto bail;
-+ }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct crede
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
++ /*
++ * Use CURLOPT_PASSWORD irrespective of whether there is
++ * an auth method specified or not, unless it's OAuth2.0,
++ * where we use CURLOPT_XOAUTH2_BEARER.
++ */
+ if (!srvc->auth_method ||
+ (strcmp(srvc->auth_method, "XOAUTH2") &&
+ strcmp(srvc->auth_method, "OAUTHBEARER")))
2: d934bdcb82 ! 3: 69fb8f63f1 imap-send: add PLAIN authentication method to OpenSSL
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
-+ if (!CAP(AUTH_PLAIN)) {
-+ fprintf(stderr, "You specified "
-+ "PLAIN as authentication method, "
-+ "but %s doesn't support it.\n", srvc->host);
++ if (try_auth_method(srvc, ctx, imap, "PLAIN", AUTH_PLAIN, auth_plain))
+ goto bail;
-+ }
-+
-+ #ifdef NO_OPENSSL
-+ fprintf(stderr, "You are trying to use PLAIN authentication mechanism "
-+ "with OpenSSL library, but its support has not been compiled in.");
-+ goto bail;
-+ #endif
-+
-+ /* PLAIN */
-+
-+ memset(&cb, 0, sizeof(cb));
-+ cb.cont = auth_plain;
-+ if (imap_exec(ctx, &cb, "AUTHENTICATE PLAIN") != RESP_OK) {
-+ fprintf(stderr, "IMAP error: AUTHENTICATE PLAIN failed\n");
-+ goto bail;
-+ }
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
3: f2773c646f = 4: 1510127888 imap-send: fix memory leak in case auth_cram_md5 fails
4: c111ee6bc1 ! 5: 731fcbb602 imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
@@ imap-send.c: static int auth_xoauth2(struct imap_store *ctx, const char *prompt
{
if (srvc->user && srvc->pass)
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
- "but %s doesn't support it.\n", srvc->host);
- goto bail;
- }
-+
-+ #ifdef NO_OPENSSL
-+ fprintf(stderr, "If you want to use CRAM-MD5 authentication mechanism, "
-+ "you have to build git-imap-send with OpenSSL library.");
-+ goto bail;
-+ #endif
-+
- /* CRAM-MD5 */
+ server_fill_credential(srvc, &cred);
- memset(&cb, 0, sizeof(cb));
+ if (srvc->auth_method) {
+- struct imap_cmd_cb cb;
+-
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (try_auth_method(srvc, ctx, imap, "PLAIN", AUTH_PLAIN, auth_plain))
+ goto bail;
+ } else 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");
++ if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
+ goto bail;
+- }
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
+ goto bail;
5: f12713f24b = 6: 36154d3276 imap-send: enable specifying the folder using the command line
6: d38caeae5e ! 7: 85ce1205ca imap-send: fix minor mistakes in the logs
@@ imap-send.c: static int ssl_socket_connect(struct imap_socket *sock UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
-+ fprintf(stderr, "SSL requested, but SSL support is not compiled in.\n");
++ fprintf(stderr, "SSL requested, but SSL support is not compiled in\n");
return -1;
}
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *
/* read the greeting string */
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
+ if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
goto bail;
- }
} else {
- fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ fprintf(stderr, "unknown authentication method:%s\n", srvc->host);
7: 3ba02f2b0c = 8: 8dd19a4613 imap-send: display port alongwith host when git credential is invoked
8: 6dbd0bf0bc = 9: cc1398bb7c imap-send: display the destination mailbox when sending a message
9: f77f2423e1 = 10: 0975df9fc0 imap-send: add ability to list the available folders
--
2.49.0
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v14 01/10] imap-send: fix bug causing cfg->folder being set to NULL
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-06 20:06 ` Aditya Garg
2025-06-06 20:06 ` [PATCH v14 02/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (8 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 20:06 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v14 02/10] imap-send: add support for OAuth2.0 authentication
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-06 20:06 ` [PATCH v14 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-06-06 20:06 ` Aditya Garg
2025-06-06 20:06 ` [PATCH v14 03/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (7 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 20:06 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 47 +++++++-
imap-send.c | 181 +++++++++++++++++++++++++++++--
3 files changed, 220 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..29b998d5ff 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
+ `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext `LOGIN` command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..8adf0e5aac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,18 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your regular password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you
+can generate an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create
+it. Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +122,35 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify
+`OAUTHBEARER` or `XOAUTH2` mechanism in your config. It is more secure
+than using app-specific passwords, and also does not enforce the need of
+having multi-factor authentication. You will have to use an OAuth2.0
+access token in place of your password when using this authentication.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +159,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..9df4519fa3 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2,
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,108 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
#else
static char *cram(const char *challenge_64 UNUSED,
@@ -895,6 +1001,9 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+#define auth_oauthbearer NULL
+#define auth_xoauth2 NULL
+
#endif
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
@@ -932,6 +1041,38 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
srvc->pass = xstrdup(cred->password);
}
+static int try_auth_method(struct imap_server_conf *srvc,
+ struct imap_store *ctx,
+ struct imap *imap,
+ const char *auth_method,
+ enum CAPABILITY cap,
+ int (*fn)(struct imap_store *, const char *))
+{
+ struct imap_cmd_cb cb = {0};
+
+ if (!CAP(cap)) {
+ fprintf(stderr, "You specified "
+ "%s as authentication method, "
+ "but %s doesn't support it.\n",
+ auth_method, srvc->host);
+ return -1;
+ }
+ cb.cont = fn;
+
+ if (NOT_CONSTANT(!cb.cont)) {
+ fprintf(stderr, "If you want to use %s authentication mechanism, "
+ "you have to build git-imap-send with OpenSSL library.",
+ auth_method);
+ return -1;
+ }
+ if (imap_exec(ctx, &cb, "AUTHENTICATE %s", auth_method) != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE %s failed\n",
+ auth_method);
+ return -1;
+ }
+ return 0;
+}
+
static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const char *folder)
{
struct credential cred = CREDENTIAL_INIT;
@@ -1104,6 +1245,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
goto bail;
}
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
+ goto bail;
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1405,7 +1552,16 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ /*
+ * Use CURLOPT_PASSWORD irrespective of whether there is
+ * an auth method specified or not, unless it's OAuth2.0,
+ * where we use CURLOPT_XOAUTH2_BEARER.
+ */
+ if (!srvc->auth_method ||
+ (strcmp(srvc->auth_method, "XOAUTH2") &&
+ strcmp(srvc->auth_method, "OAUTHBEARER")))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1423,11 +1579,22 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /*
+ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v14 03/10] imap-send: add PLAIN authentication method to OpenSSL
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-06 20:06 ` [PATCH v14 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-06 20:06 ` [PATCH v14 02/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-06 20:06 ` Aditya Garg
2025-06-06 20:06 ` [PATCH v14 04/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (6 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 20:06 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +--
imap-send.c | 63 +++++++++++++++++++++++++++++++++-
2 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 29b998d5ff..7c8b2dcce4 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
- `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are `PLAIN`, `CRAM-MD5`, `OAUTHBEARER`
+ and `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
plaintext `LOGIN` command.
diff --git a/imap-send.c b/imap-send.c
index 9df4519fa3..f7e59397d0 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2,
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,41 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +988,26 @@ static char *xoauth2_base64(const char *user, const char *access_token)
return b64;
}
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
{
int ret;
@@ -1001,6 +1058,7 @@ static char *cram(const char *challenge_64 UNUSED,
"you have to build git-imap-send with OpenSSL library.");
}
+#define auth_plain NULL
#define auth_oauthbearer NULL
#define auth_xoauth2 NULL
@@ -1230,7 +1288,10 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (srvc->auth_method) {
struct imap_cmd_cb cb;
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (try_auth_method(srvc, ctx, imap, "PLAIN", AUTH_PLAIN, auth_plain))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (!CAP(AUTH_CRAM_MD5)) {
fprintf(stderr, "You specified "
"CRAM-MD5 as authentication method, "
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v14 04/10] imap-send: fix memory leak in case auth_cram_md5 fails
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-06-06 20:06 ` [PATCH v14 03/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-06-06 20:06 ` Aditya Garg
2025-06-06 20:06 ` [PATCH v14 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
` (5 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 20:06 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index f7e59397d0..6522f80964 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1072,8 +1072,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v14 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-06-06 20:06 ` [PATCH v14 04/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-06-06 20:06 ` Aditya Garg
2025-06-07 15:32 ` Junio C Hamano
2025-06-06 20:06 ` [PATCH v14 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
` (4 subsequent siblings)
9 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 20:06 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Unlike PLAIN, XOAUTH2 and OAUTHBEARER, CRAM-MD5 authentication is not
supported by libcurl and requires OpenSSL. If the user tries to use
CRAM-MD5 authentication without OpenSSL, the previous behaviour was to
attempt to authenticate and fail with a die(error). Handle this in a
better way by first checking if OpenSSL is available and then attempting
to authenticate. If OpenSSL is not available, print an error message and
exit gracefully.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 61 ++++++++++++++++++-----------------------------------
1 file changed, 20 insertions(+), 41 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 6522f80964..c6e47ddc42 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1008,6 +1008,24 @@ static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
return 0;
}
+static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+{
+ int ret;
+ char *response;
+
+ response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
+
+ ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
+ if (ret != strlen(response)) {
+ free(response);
+ return error("IMAP error: sending response failed");
+ }
+
+ free(response);
+
+ return 0;
+}
+
static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
{
int ret;
@@ -1050,38 +1068,13 @@ static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
#else
-static char *cram(const char *challenge_64 UNUSED,
- const char *user UNUSED,
- const char *pass UNUSED)
-{
- die("If you want to use CRAM-MD5 authenticate method, "
- "you have to build git-imap-send with OpenSSL library.");
-}
-
#define auth_plain NULL
+#define auth_cram_md5 NULL
#define auth_oauthbearer NULL
#define auth_xoauth2 NULL
#endif
-static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
-{
- int ret;
- char *response;
-
- response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
-
- ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response)) {
- free(response);
- return error("IMAP error: sending response failed");
- }
-
- free(response);
-
- return 0;
-}
-
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -1288,26 +1281,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
server_fill_credential(srvc, &cred);
if (srvc->auth_method) {
- struct imap_cmd_cb cb;
-
if (!strcmp(srvc->auth_method, "PLAIN")) {
if (try_auth_method(srvc, ctx, imap, "PLAIN", AUTH_PLAIN, auth_plain))
goto bail;
} else 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");
+ if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
- }
} else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
goto bail;
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v14 06/10] imap-send: enable specifying the folder using the command line
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-06-06 20:06 ` [PATCH v14 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
@ 2025-06-06 20:06 ` Aditya Garg
2025-06-06 20:06 ` [PATCH v14 07/10] imap-send: fix minor mistakes in the logs Aditya Garg
` (3 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 20:06 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 6 ++++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 7c8b2dcce4..4682a6bd03 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,9 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
+ `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
+ the value of this configuration variable is used as the fallback
+ default value when the `--folder` option is not given.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 8adf0e5aac..4a0487b66e 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields `From`, `Date`, and `Subject` in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index c6e47ddc42..a4cccb9110 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1729,6 +1731,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v14 07/10] imap-send: fix minor mistakes in the logs
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-06-06 20:06 ` [PATCH v14 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-06-06 20:06 ` Aditya Garg
2025-06-06 20:06 ` [PATCH v14 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
` (2 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 20:06 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some minor mistakes have been found in the logs. Most of them include
error messages starting with a capital letter, and ending with a period.
Also, abbreviations like "IMAP" and "OK" should be in uppercase. Fix them.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index a4cccb9110..a9dc6cfad6 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -205,7 +205,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in\n");
return -1;
}
@@ -1020,7 +1020,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1160,7 +1160,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1179,7 +1179,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1217,7 +1217,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1231,7 +1231,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1243,7 +1243,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1296,12 +1296,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
goto bail;
} else {
- fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ fprintf(stderr, "unknown authentication method:%s\n", srvc->host);
goto bail;
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "skipping account %s@%s, server forbids LOGIN\n",
srvc->user, srvc->host);
goto bail;
}
@@ -1557,7 +1557,7 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
@@ -1671,7 +1671,7 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
@@ -1755,13 +1755,13 @@ int cmd_main(int argc, const char **argv)
server.port = server.use_ssl ? 993 : 143;
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "no IMAP store specified\n");
ret = 1;
goto out;
}
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "no IMAP host specified\n");
ret = 1;
goto out;
}
@@ -1783,7 +1783,7 @@ int cmd_main(int argc, const char **argv)
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "no messages found to send\n");
ret = 1;
goto out;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v14 08/10] imap-send: display port alongwith host when git credential is invoked
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-06-06 20:06 ` [PATCH v14 07/10] imap-send: fix minor mistakes in the logs Aditya Garg
@ 2025-06-06 20:06 ` Aditya Garg
2025-06-06 20:06 ` [PATCH v14 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-06 20:06 ` [PATCH v14 10/10] imap-send: add ability to list the available folders Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 20:06 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
FWIW, if no port is specified by the user, the default port, 993 for
IMAPS and 143 for IMAP is used by the code. So, the case of no port
defined for the helper is not possible, and therefore is not added.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index a9dc6cfad6..e3068ef1fe 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1083,7 +1083,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v14 09/10] imap-send: display the destination mailbox when sending a message
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-06-06 20:06 ` [PATCH v14 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-06-06 20:06 ` Aditya Garg
2025-06-06 20:06 ` [PATCH v14 10/10] imap-send: add ability to list the available folders Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 20:06 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
Sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index e3068ef1fe..9281112bea 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1557,7 +1557,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1671,7 +1672,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v14 10/10] imap-send: add ability to list the available folders
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (8 preceding siblings ...)
2025-06-06 20:06 ` [PATCH v14 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-06-06 20:06 ` Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-06 20:06 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Various IMAP servers have different ways to name common folders.
For example, the folder where all deleted messages are stored is often
named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
on Gmail it is named "[Gmail]/Drafts".
This commit adds a `--list` command to the `imap-send` tool that lists
the available folders on the IMAP server, allowing users to see
which folders are available and how they are named. A sample output
looks like this when run against a Gmail server:
Fetching the list of available folders...
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasChildren \Noselect) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
For OpenSSL, this is achived by running the 'IMAP LIST' command and
parsing the response. This command is specified in RFC6154:
https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
For libcurl, the example code published in the libcurl documentation
is used to implement this functionality:
https://curl.se/libcurl/c/imap-list.html
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/git-imap-send.adoc | 6 +-
imap-send.c | 98 ++++++++++++++++++++++++++------
2 files changed, 87 insertions(+), 17 deletions(-)
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 4a0487b66e..17147f93c3 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+'git imap-send' --list
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
+--list::
+ Run the IMAP LIST command to output a list of all the folders present.
CONFIGURATION
-------------
@@ -123,7 +126,8 @@ it. Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
-that the "Folder doesn't exist".
+that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
+list of available folders.
[NOTE]
If your Gmail account is set to another language than English, the name of the "Drafts"
diff --git a/imap-send.c b/imap-send.c
index 9281112bea..16c2e641ac 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -45,15 +45,21 @@
#endif
static int verbosity;
+static int list_folders = 0;
static int use_curl = USE_CURL_DEFAULT;
static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
+ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
+ "git imap-send --list",
+ NULL
+};
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
+ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
OPT_END()
};
@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
if (b->buf[b->offset + 1] == '\n') {
b->buf[b->offset] = 0; /* terminate the string */
b->offset += 2; /* next line */
- if (0 < verbosity)
+ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
puts(*s);
return 0;
}
@@ -1580,6 +1586,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
+static int list_imap_folders(struct imap_server_conf *server)
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
+ fprintf(stderr, "failed to connect to IMAP server\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
+ fprintf(stderr, "failed to list folders\n");
+ imap_close_store(ctx);
+ return 1;
+ }
+
+ imap_close_store(ctx);
+ return 0;
+}
+
#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
@@ -1613,11 +1639,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
- die("failed to encode server folder");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+ die("failed to encode server folder");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ -1648,10 +1676,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
-
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(curl);
@@ -1670,6 +1694,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
struct credential cred = CREDENTIAL_INIT;
curl = setup_curl(server, &cred);
+
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
fprintf(stderr, "Sending %d message%s to %s folder...\n",
@@ -1716,6 +1744,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
+
+static int curl_list_imap_folders(struct imap_server_conf *server)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct credential cred = CREDENTIAL_INIT;
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ curl = setup_curl(server, &cred);
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ if (cred.username) {
+ if (res == CURLE_OK)
+ credential_approve(the_repository, &cred);
+ else if (res == CURLE_LOGIN_DENIED)
+ credential_reject(the_repository, &cred);
+ }
+
+ credential_clear(&cred);
+
+ return res != CURLE_OK;
+}
#endif
int cmd_main(int argc, const char **argv)
@@ -1756,11 +1809,6 @@ int cmd_main(int argc, const char **argv)
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
- fprintf(stderr, "no IMAP store specified\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "no IMAP host specified\n");
@@ -1770,6 +1818,24 @@ int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
+ if (list_folders) {
+ if (server.tunnel)
+ ret = list_imap_folders(&server);
+#ifdef USE_CURL_FOR_IMAP_SEND
+ else if (use_curl)
+ ret = curl_list_imap_folders(&server);
+#endif
+ else
+ ret = list_imap_folders(&server);
+ goto out;
+ }
+
+ if (!server.folder) {
+ fprintf(stderr, "no IMAP store specified\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("could not read from stdin"));
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v14 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-06 20:06 ` [PATCH v14 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
@ 2025-06-07 15:32 ` Junio C Hamano
2025-06-07 17:13 ` Aditya Garg
2025-06-08 10:56 ` Aditya Garg
0 siblings, 2 replies; 248+ messages in thread
From: Junio C Hamano @ 2025-06-07 15:32 UTC (permalink / raw)
To: Aditya Garg
Cc: git, Eric Sunshine, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> Unlike PLAIN, XOAUTH2 and OAUTHBEARER, CRAM-MD5 authentication is not
> supported by libcurl and requires OpenSSL. If the user tries to use
> CRAM-MD5 authentication without OpenSSL, the previous behaviour was to
> attempt to authenticate and fail with a die(error). Handle this in a
> better way by first checking if OpenSSL is available and then attempting
> to authenticate. If OpenSSL is not available, print an error message and
> exit gracefully.
>
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
> ---
> imap-send.c | 61 ++++++++++++++++++-----------------------------------
> 1 file changed, 20 insertions(+), 41 deletions(-)
This is a good thing to do, but I would have expected that it would
come a lot earlier in the series, perhaps immediately after 01/10
fixes the copy-and-paste bug. If this is moved earlier in the
series, it would need to introduce the try_auth_method() helper at
the same time. Since there is no new authentication methods
introduced at that stage in the series yet, it would be quite
straight-forward to read and understand the patch, and on top of
such a solidified ground, the series can add OAuth2.0 and PLAIN
support on top.
Thanks.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v14 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-07 15:32 ` Junio C Hamano
@ 2025-06-07 17:13 ` Aditya Garg
2025-06-08 4:20 ` Junio C Hamano
2025-06-08 10:56 ` Aditya Garg
1 sibling, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-07 17:13 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
> On 7 Jun 2025, at 9:02 PM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>> Unlike PLAIN, XOAUTH2 and OAUTHBEARER, CRAM-MD5 authentication is not
>> supported by libcurl and requires OpenSSL. If the user tries to use
>> CRAM-MD5 authentication without OpenSSL, the previous behaviour was to
>> attempt to authenticate and fail with a die(error). Handle this in a
>> better way by first checking if OpenSSL is available and then attempting
>> to authenticate. If OpenSSL is not available, print an error message and
>> exit gracefully.
>>
>> Signed-off-by: Aditya Garg <gargaditya08@live.com>
>> ---
>> imap-send.c | 61 ++++++++++++++++++-----------------------------------
>> 1 file changed, 20 insertions(+), 41 deletions(-)
>
> This is a good thing to do, but I would have expected that it would
> come a lot earlier in the series, perhaps immediately after 01/10
> fixes the copy-and-paste bug. If this is moved earlier in the
> series, it would need to introduce the try_auth_method() helper at
> the same time. Since there is no new authentication methods
> introduced at that stage in the series yet, it would be quite
> straight-forward to read and understand the patch, and on top of
> such a solidified ground, the series can add OAuth2.0 and PLAIN
> support on top.
I understand what you said is the ideal way to do, but since the cram
md5 patch came up much later, I found it easier to place it at this place.
I usually try to avoid as much conflicts as possible while rebasing, since
I fear breaking something. But if I *have* to move it above, please let me
know.
Thanks
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v14 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-07 17:13 ` Aditya Garg
@ 2025-06-08 4:20 ` Junio C Hamano
2025-06-08 7:54 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-08 4:20 UTC (permalink / raw)
To: Aditya Garg
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> ... if I *have* to move it above, please let me
> know.
This is the second time after I told you what needs to be done you
told me to tell you to do it, isn't it?
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v14 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-08 4:20 ` Junio C Hamano
@ 2025-06-08 7:54 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 7:54 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
> On 8 Jun 2025, at 9:50 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>> ... if I *have* to move it above, please let me
>> know.
>
> This is the second time after I told you what needs to be done you
> told me to tell you to do it, isn't it?
What I understand every time is you suggest me rather than tell me.
Anyways, English is not my native language so I do misunderstand
quite often.
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (18 preceding siblings ...)
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-08 10:55 ` Aditya Garg
2025-06-08 10:55 ` [PATCH v15 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (10 more replies)
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
` (3 subsequent siblings)
23 siblings, 11 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 10:55 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch series does the following things:
Firstly it basically makes the imap-send command usable again since it
was broken because of not being able to correctly parse the config file.
Further it adds support for OAuth2.0 and PLAIN authentication to git
imap-send.
Lastly, it does some minor improvements including adding the ability to
specify the folder using the command line and ability to list the
available folders by adding a `--list` option.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
v8: - Drop the patch that enabled user to choose between libcurl and openssl using the config
- Add ability to list the available folders by adding a `--list` option
v9: - Encourage users to use OAuth2.0 for Gmail (similar change done for send-email docs).
v10: - Fix comment styles
- Fix failing tests
v11: - Use lower case letters for the first word of a sendtence in an error message
and avoid using full stops at the end of a sentence.
v12: - Gracefully exit PLAIN, CRAM-MD5, OAUTHBEARER and XOAUTH2 authentication methods
if OpenSSL support is not compiled in, but is requested by the user.
- Use backticks for string literals.
- Wrap documentation text to 75 columns.
- End the last member of enum CAPABILITY with a trailing comma.
v13: - Fix logic error which was using || instead of && when checking if
the authentication method is neither XOAUTH2 nor OAUTHBEARER.
v14: - Specify why we are not using CURLOPT_PASSWORD for OAuth2.0
methods using a comment.
- Add a function try_auth_method() to reduce code duplication
when trying to authenticate using a specific method.
v15: - Simply rearrange the patches to make the cram md5 patches come
before adding OAuth2.0 and PLAIN authentication methods. No
change has been done to the code itself.
Aditya Garg (10):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: gracefully fail if CRAM-MD5 authentication is requested
without OpenSSL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: enable specifying the folder using the command line
imap-send: fix minor mistakes in the logs
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
imap-send: add ability to list the available folders
Documentation/config/imap.adoc | 11 +-
Documentation/git-imap-send.adoc | 68 ++++-
imap-send.c | 412 ++++++++++++++++++++++++++-----
3 files changed, 414 insertions(+), 77 deletions(-)
Range-diff against v14:
-: ---------- > 1: 3e3ddf7077 imap-send: fix bug causing cfg->folder being set to NULL
3: 1510127888 = 2: 417b3b8e38 imap-send: fix memory leak in case auth_cram_md5 fails
4: 731fcbb602 ! 3: c4216528e7 imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
@@ Commit message
Signed-off-by: Aditya Garg <gargaditya08@live.com>
## imap-send.c ##
-@@ imap-send.c: static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
- return 0;
+@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const char *pass)
+ return (char *)response_64;
}
-+static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
-+{
-+ int ret;
-+ char *response;
-+
-+ response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
-+
-+ ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
-+ if (ret != strlen(response)) {
-+ free(response);
-+ return error("IMAP error: sending response failed");
-+ }
-+
-+ free(response);
-+
-+ return 0;
-+}
-+
- static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
- {
- int ret;
-@@ imap-send.c: static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
-
- #else
-
+-#else
+-
-static char *cram(const char *challenge_64 UNUSED,
- const char *user UNUSED,
- const char *pass UNUSED)
@@ imap-send.c: static int auth_xoauth2(struct imap_store *ctx, const char *prompt
- "you have to build git-imap-send with OpenSSL library.");
-}
-
- #define auth_plain NULL
-+#define auth_cram_md5 NULL
- #define auth_oauthbearer NULL
- #define auth_xoauth2 NULL
-
- #endif
-
--static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
--{
-- int ret;
-- char *response;
--
-- response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
--
-- ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
-- if (ret != strlen(response)) {
-- free(response);
-- return error("IMAP error: sending response failed");
-- }
--
-- free(response);
--
-- return 0;
--}
+-#endif
-
+ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+ {
+ int ret;
+@@ imap-send.c: static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+ return 0;
+ }
+
++#else
++
++#define auth_cram_md5 NULL
++
++#endif
++
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
+@@ imap-send.c: static void server_fill_credential(struct imap_server_conf *srvc, struct credent
+ srvc->pass = xstrdup(cred->password);
+ }
+
++static int try_auth_method(struct imap_server_conf *srvc,
++ struct imap_store *ctx,
++ struct imap *imap,
++ const char *auth_method,
++ enum CAPABILITY cap,
++ int (*fn)(struct imap_store *, const char *))
++{
++ struct imap_cmd_cb cb = {0};
++
++ if (!CAP(cap)) {
++ fprintf(stderr, "You specified "
++ "%s as authentication method, "
++ "but %s doesn't support it.\n",
++ auth_method, srvc->host);
++ return -1;
++ }
++ cb.cont = fn;
++
++ if (NOT_CONSTANT(!cb.cont)) {
++ fprintf(stderr, "If you want to use %s authentication mechanism, "
++ "you have to build git-imap-send with OpenSSL library.",
++ auth_method);
++ return -1;
++ }
++ if (imap_exec(ctx, &cb, "AUTHENTICATE %s", auth_method) != RESP_OK) {
++ fprintf(stderr, "IMAP error: AUTHENTICATE %s failed\n",
++ auth_method);
++ return -1;
++ }
++ return 0;
++}
++
+ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const char *folder)
+ {
+ struct credential cred = CREDENTIAL_INIT;
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
server_fill_credential(srvc, &cred);
if (srvc->auth_method) {
- struct imap_cmd_cb cb;
-
- if (!strcmp(srvc->auth_method, "PLAIN")) {
- if (try_auth_method(srvc, ctx, imap, "PLAIN", AUTH_PLAIN, auth_plain))
- goto bail;
- } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
- if (!CAP(AUTH_CRAM_MD5)) {
- fprintf(stderr, "You specified "
- "CRAM-MD5 as authentication method, "
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *
+ if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
- }
- } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
- if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
- goto bail;
+ } else {
+ fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ goto bail;
1: 34d56c3b57 ! 4: b38fca0e6a imap-send: add support for OAuth2.0 authentication
@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const
+ return b64;
+}
+
+ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+ {
+ int ret;
+@@ imap-send.c: static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+ return 0;
+ }
+
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const
+
#else
- static char *cram(const char *challenge_64 UNUSED,
-@@ imap-send.c: static char *cram(const char *challenge_64 UNUSED,
- "you have to build git-imap-send with OpenSSL library.");
- }
-
+ #define auth_cram_md5 NULL
+#define auth_oauthbearer NULL
+#define auth_xoauth2 NULL
-+
- #endif
- static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
-@@ imap-send.c: static void server_fill_credential(struct imap_server_conf *srvc, struct credent
- srvc->pass = xstrdup(cred->password);
- }
+ #endif
-+static int try_auth_method(struct imap_server_conf *srvc,
-+ struct imap_store *ctx,
-+ struct imap *imap,
-+ const char *auth_method,
-+ enum CAPABILITY cap,
-+ int (*fn)(struct imap_store *, const char *))
-+{
-+ struct imap_cmd_cb cb = {0};
-+
-+ if (!CAP(cap)) {
-+ fprintf(stderr, "You specified "
-+ "%s as authentication method, "
-+ "but %s doesn't support it.\n",
-+ auth_method, srvc->host);
-+ return -1;
-+ }
-+ cb.cont = fn;
-+
-+ if (NOT_CONSTANT(!cb.cont)) {
-+ fprintf(stderr, "If you want to use %s authentication mechanism, "
-+ "you have to build git-imap-send with OpenSSL library.",
-+ auth_method);
-+ return -1;
-+ }
-+ if (imap_exec(ctx, &cb, "AUTHENTICATE %s", auth_method) != RESP_OK) {
-+ fprintf(stderr, "IMAP error: AUTHENTICATE %s failed\n",
-+ auth_method);
-+ return -1;
-+ }
-+ return 0;
-+}
-+
- static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const char *folder)
- {
- struct credential cred = CREDENTIAL_INIT;
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
- fprintf(stderr, "IMAP error: AUTHENTICATE CRAM-MD5 failed\n");
+ if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
- }
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
+ goto bail;
2: 69fb8f63f1 ! 5: 86d3d2c54d imap-send: add PLAIN authentication method to OpenSSL
@@ imap-send.c: static char *xoauth2_base64(const char *user, const char *access_to
+ return 0;
+}
+
- static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
-@@ imap-send.c: static char *cram(const char *challenge_64 UNUSED,
- "you have to build git-imap-send with OpenSSL library.");
- }
+@@ imap-send.c: static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+
+ #else
+#define auth_plain NULL
+ #define auth_cram_md5 NULL
#define auth_oauthbearer NULL
#define auth_xoauth2 NULL
-
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
- if (srvc->auth_method) {
- struct imap_cmd_cb cb;
+ server_fill_credential(srvc, &cred);
+ if (srvc->auth_method) {
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (try_auth_method(srvc, ctx, imap, "PLAIN", AUTH_PLAIN, auth_plain))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
- if (!CAP(AUTH_CRAM_MD5)) {
- fprintf(stderr, "You specified "
- "CRAM-MD5 as authentication method, "
+ if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
5: 36154d3276 = 6: 7674e749c8 imap-send: enable specifying the folder using the command line
6: 85ce1205ca = 7: a67322ce06 imap-send: fix minor mistakes in the logs
7: 8dd19a4613 = 8: b2e7ef35ed imap-send: display port alongwith host when git credential is invoked
8: cc1398bb7c = 9: 668e62c0e0 imap-send: display the destination mailbox when sending a message
9: 0975df9fc0 = 10: 4d9a3b5661 imap-send: add ability to list the available folders
--
2.49.0
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v15 01/10] imap-send: fix bug causing cfg->folder being set to NULL
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-08 10:55 ` Aditya Garg
2025-06-08 10:55 ` [PATCH v15 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (9 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 10:55 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v15 02/10] imap-send: fix memory leak in case auth_cram_md5 fails
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-08 10:55 ` [PATCH v15 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-06-08 10:55 ` Aditya Garg
2025-06-08 10:55 ` [PATCH v15 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
` (8 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 10:55 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..1a582c8443 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -905,8 +905,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v15 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-08 10:55 ` [PATCH v15 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-08 10:55 ` [PATCH v15 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-06-08 10:55 ` Aditya Garg
2025-06-08 10:55 ` [PATCH v15 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (7 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 10:55 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Unlike PLAIN, XOAUTH2 and OAUTHBEARER, CRAM-MD5 authentication is not
supported by libcurl and requires OpenSSL. If the user tries to use
CRAM-MD5 authentication without OpenSSL, the previous behaviour was to
attempt to authenticate and fail with a die(error). Handle this in a
better way by first checking if OpenSSL is available and then attempting
to authenticate. If OpenSSL is not available, print an error message and
exit gracefully.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 66 +++++++++++++++++++++++++++++++----------------------
1 file changed, 39 insertions(+), 27 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 1a582c8443..f55399cd9e 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -885,18 +885,6 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
-#else
-
-static char *cram(const char *challenge_64 UNUSED,
- const char *user UNUSED,
- const char *pass UNUSED)
-{
- 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, const char *prompt)
{
int ret;
@@ -915,6 +903,12 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+#else
+
+#define auth_cram_md5 NULL
+
+#endif
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -934,6 +928,38 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
srvc->pass = xstrdup(cred->password);
}
+static int try_auth_method(struct imap_server_conf *srvc,
+ struct imap_store *ctx,
+ struct imap *imap,
+ const char *auth_method,
+ enum CAPABILITY cap,
+ int (*fn)(struct imap_store *, const char *))
+{
+ struct imap_cmd_cb cb = {0};
+
+ if (!CAP(cap)) {
+ fprintf(stderr, "You specified "
+ "%s as authentication method, "
+ "but %s doesn't support it.\n",
+ auth_method, srvc->host);
+ return -1;
+ }
+ cb.cont = fn;
+
+ if (NOT_CONSTANT(!cb.cont)) {
+ fprintf(stderr, "If you want to use %s authentication mechanism, "
+ "you have to build git-imap-send with OpenSSL library.",
+ auth_method);
+ return -1;
+ }
+ if (imap_exec(ctx, &cb, "AUTHENTICATE %s", auth_method) != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE %s failed\n",
+ auth_method);
+ return -1;
+ }
+ return 0;
+}
+
static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const char *folder)
{
struct credential cred = CREDENTIAL_INIT;
@@ -1089,23 +1115,9 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
server_fill_credential(srvc, &cred);
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");
+ if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
- }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v15 04/10] imap-send: add support for OAuth2.0 authentication
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-06-08 10:55 ` [PATCH v15 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
@ 2025-06-08 10:55 ` Aditya Garg
2025-06-08 10:55 ` [PATCH v15 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (6 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 10:55 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 47 +++++++++-
imap-send.c | 148 +++++++++++++++++++++++++++++--
3 files changed, 187 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..29b998d5ff 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
+ `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext `LOGIN` command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..8adf0e5aac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,18 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your regular password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you
+can generate an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create
+it. Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +122,35 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify
+`OAUTHBEARER` or `XOAUTH2` mechanism in your config. It is more secure
+than using app-specific passwords, and also does not enforce the need of
+having multi-factor authentication. You will have to use an OAuth2.0
+access token in place of your password when using this authentication.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +159,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index f55399cd9e..5373f18b94 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2,
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,68 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -903,9 +969,51 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
#else
#define auth_cram_md5 NULL
+#define auth_oauthbearer NULL
+#define auth_xoauth2 NULL
#endif
@@ -1118,6 +1226,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
+ goto bail;
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1419,7 +1533,16 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ /*
+ * Use CURLOPT_PASSWORD irrespective of whether there is
+ * an auth method specified or not, unless it's OAuth2.0,
+ * where we use CURLOPT_XOAUTH2_BEARER.
+ */
+ if (!srvc->auth_method ||
+ (strcmp(srvc->auth_method, "XOAUTH2") &&
+ strcmp(srvc->auth_method, "OAUTHBEARER")))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1437,11 +1560,22 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /*
+ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v15 05/10] imap-send: add PLAIN authentication method to OpenSSL
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-06-08 10:55 ` [PATCH v15 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-08 10:55 ` Aditya Garg
2025-06-08 10:55 ` [PATCH v15 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
` (5 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 10:55 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +--
imap-send.c | 63 +++++++++++++++++++++++++++++++++-
2 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 29b998d5ff..7c8b2dcce4 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
- `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are `PLAIN`, `CRAM-MD5`, `OAUTHBEARER`
+ and `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
plaintext `LOGIN` command.
diff --git a/imap-send.c b/imap-send.c
index 5373f18b94..c6e47ddc42 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2,
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,41 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +988,26 @@ static char *xoauth2_base64(const char *user, const char *access_token)
return b64;
}
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1011,6 +1068,7 @@ static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
#else
+#define auth_plain NULL
#define auth_cram_md5 NULL
#define auth_oauthbearer NULL
#define auth_xoauth2 NULL
@@ -1223,7 +1281,10 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
server_fill_credential(srvc, &cred);
if (srvc->auth_method) {
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (try_auth_method(srvc, ctx, imap, "PLAIN", AUTH_PLAIN, auth_plain))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
} else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v15 06/10] imap-send: enable specifying the folder using the command line
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-06-08 10:55 ` [PATCH v15 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-06-08 10:55 ` Aditya Garg
2025-06-08 10:55 ` [PATCH v15 07/10] imap-send: fix minor mistakes in the logs Aditya Garg
` (4 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 10:55 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 6 ++++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 7c8b2dcce4..4682a6bd03 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,9 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
+ `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
+ the value of this configuration variable is used as the fallback
+ default value when the `--folder` option is not given.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 8adf0e5aac..4a0487b66e 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields `From`, `Date`, and `Subject` in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index c6e47ddc42..a4cccb9110 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1729,6 +1731,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v15 07/10] imap-send: fix minor mistakes in the logs
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-06-08 10:55 ` [PATCH v15 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-06-08 10:55 ` Aditya Garg
2025-06-08 10:55 ` [PATCH v15 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
` (3 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 10:55 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some minor mistakes have been found in the logs. Most of them include
error messages starting with a capital letter, and ending with a period.
Also, abbreviations like "IMAP" and "OK" should be in uppercase. Fix them.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index a4cccb9110..a9dc6cfad6 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -205,7 +205,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in\n");
return -1;
}
@@ -1020,7 +1020,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1160,7 +1160,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1179,7 +1179,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1217,7 +1217,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1231,7 +1231,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1243,7 +1243,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1296,12 +1296,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
goto bail;
} else {
- fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ fprintf(stderr, "unknown authentication method:%s\n", srvc->host);
goto bail;
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "skipping account %s@%s, server forbids LOGIN\n",
srvc->user, srvc->host);
goto bail;
}
@@ -1557,7 +1557,7 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
@@ -1671,7 +1671,7 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
@@ -1755,13 +1755,13 @@ int cmd_main(int argc, const char **argv)
server.port = server.use_ssl ? 993 : 143;
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "no IMAP store specified\n");
ret = 1;
goto out;
}
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "no IMAP host specified\n");
ret = 1;
goto out;
}
@@ -1783,7 +1783,7 @@ int cmd_main(int argc, const char **argv)
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "no messages found to send\n");
ret = 1;
goto out;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v15 08/10] imap-send: display port alongwith host when git credential is invoked
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-06-08 10:55 ` [PATCH v15 07/10] imap-send: fix minor mistakes in the logs Aditya Garg
@ 2025-06-08 10:55 ` Aditya Garg
2025-06-08 10:55 ` [PATCH v15 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
` (2 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 10:55 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
FWIW, if no port is specified by the user, the default port, 993 for
IMAPS and 143 for IMAP is used by the code. So, the case of no port
defined for the helper is not possible, and therefore is not added.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index a9dc6cfad6..e3068ef1fe 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1083,7 +1083,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v15 09/10] imap-send: display the destination mailbox when sending a message
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-06-08 10:55 ` [PATCH v15 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-06-08 10:55 ` Aditya Garg
2025-06-08 10:55 ` [PATCH v15 10/10] imap-send: add ability to list the available folders Aditya Garg
2025-06-08 20:50 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Junio C Hamano
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 10:55 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
Sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index e3068ef1fe..9281112bea 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1557,7 +1557,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1671,7 +1672,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl = setup_curl(server, &cred);
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v15 10/10] imap-send: add ability to list the available folders
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (8 preceding siblings ...)
2025-06-08 10:55 ` [PATCH v15 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-06-08 10:55 ` Aditya Garg
2025-06-08 20:50 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Junio C Hamano
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 10:55 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Various IMAP servers have different ways to name common folders.
For example, the folder where all deleted messages are stored is often
named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
on Gmail it is named "[Gmail]/Drafts".
This commit adds a `--list` command to the `imap-send` tool that lists
the available folders on the IMAP server, allowing users to see
which folders are available and how they are named. A sample output
looks like this when run against a Gmail server:
Fetching the list of available folders...
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasChildren \Noselect) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
For OpenSSL, this is achived by running the 'IMAP LIST' command and
parsing the response. This command is specified in RFC6154:
https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
For libcurl, the example code published in the libcurl documentation
is used to implement this functionality:
https://curl.se/libcurl/c/imap-list.html
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/git-imap-send.adoc | 6 +-
imap-send.c | 98 ++++++++++++++++++++++++++------
2 files changed, 87 insertions(+), 17 deletions(-)
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 4a0487b66e..17147f93c3 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+'git imap-send' --list
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
+--list::
+ Run the IMAP LIST command to output a list of all the folders present.
CONFIGURATION
-------------
@@ -123,7 +126,8 @@ it. Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
-that the "Folder doesn't exist".
+that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
+list of available folders.
[NOTE]
If your Gmail account is set to another language than English, the name of the "Drafts"
diff --git a/imap-send.c b/imap-send.c
index 9281112bea..16c2e641ac 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -45,15 +45,21 @@
#endif
static int verbosity;
+static int list_folders = 0;
static int use_curl = USE_CURL_DEFAULT;
static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
+ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
+ "git imap-send --list",
+ NULL
+};
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
+ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
OPT_END()
};
@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
if (b->buf[b->offset + 1] == '\n') {
b->buf[b->offset] = 0; /* terminate the string */
b->offset += 2; /* next line */
- if (0 < verbosity)
+ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
puts(*s);
return 0;
}
@@ -1580,6 +1586,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
+static int list_imap_folders(struct imap_server_conf *server)
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
+ fprintf(stderr, "failed to connect to IMAP server\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
+ fprintf(stderr, "failed to list folders\n");
+ imap_close_store(ctx);
+ return 1;
+ }
+
+ imap_close_store(ctx);
+ return 0;
+}
+
#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
@@ -1613,11 +1639,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
- die("failed to encode server folder");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+ die("failed to encode server folder");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ -1648,10 +1676,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
-
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(curl);
@@ -1670,6 +1694,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
struct credential cred = CREDENTIAL_INIT;
curl = setup_curl(server, &cred);
+
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
fprintf(stderr, "Sending %d message%s to %s folder...\n",
@@ -1716,6 +1744,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
+
+static int curl_list_imap_folders(struct imap_server_conf *server)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct credential cred = CREDENTIAL_INIT;
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ curl = setup_curl(server, &cred);
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ if (cred.username) {
+ if (res == CURLE_OK)
+ credential_approve(the_repository, &cred);
+ else if (res == CURLE_LOGIN_DENIED)
+ credential_reject(the_repository, &cred);
+ }
+
+ credential_clear(&cred);
+
+ return res != CURLE_OK;
+}
#endif
int cmd_main(int argc, const char **argv)
@@ -1756,11 +1809,6 @@ int cmd_main(int argc, const char **argv)
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
- fprintf(stderr, "no IMAP store specified\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "no IMAP host specified\n");
@@ -1770,6 +1818,24 @@ int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
+ if (list_folders) {
+ if (server.tunnel)
+ ret = list_imap_folders(&server);
+#ifdef USE_CURL_FOR_IMAP_SEND
+ else if (use_curl)
+ ret = curl_list_imap_folders(&server);
+#endif
+ else
+ ret = list_imap_folders(&server);
+ goto out;
+ }
+
+ if (!server.folder) {
+ fprintf(stderr, "no IMAP store specified\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("could not read from stdin"));
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v14 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-07 15:32 ` Junio C Hamano
2025-06-07 17:13 ` Aditya Garg
@ 2025-06-08 10:56 ` Aditya Garg
1 sibling, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-08 10:56 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
> On 7 Jun 2025, at 9:02 PM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>> Unlike PLAIN, XOAUTH2 and OAUTHBEARER, CRAM-MD5 authentication is not
>> supported by libcurl and requires OpenSSL. If the user tries to use
>> CRAM-MD5 authentication without OpenSSL, the previous behaviour was to
>> attempt to authenticate and fail with a die(error). Handle this in a
>> better way by first checking if OpenSSL is available and then attempting
>> to authenticate. If OpenSSL is not available, print an error message and
>> exit gracefully.
>>
>> Signed-off-by: Aditya Garg <gargaditya08@live.com>
>> ---
>> imap-send.c | 61 ++++++++++++++++++-----------------------------------
>> 1 file changed, 20 insertions(+), 41 deletions(-)
>
> This is a good thing to do, but I would have expected that it would
> come a lot earlier in the series, perhaps immediately after 01/10
> fixes the copy-and-paste bug. If this is moved earlier in the
> series, it would need to introduce the try_auth_method() helper at
> the same time. Since there is no new authentication methods
> introduced at that stage in the series yet, it would be quite
> straight-forward to read and understand the patch, and on top of
> such a solidified ground, the series can add OAuth2.0 and PLAIN
> support on top.
Sent a v15 with the patches rearranged.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (9 preceding siblings ...)
2025-06-08 10:55 ` [PATCH v15 10/10] imap-send: add ability to list the available folders Aditya Garg
@ 2025-06-08 20:50 ` Junio C Hamano
2025-06-09 4:31 ` Aditya Garg
2025-06-09 7:23 ` Aditya Garg
10 siblings, 2 replies; 248+ messages in thread
From: Junio C Hamano @ 2025-06-08 20:50 UTC (permalink / raw)
To: Aditya Garg
Cc: git, Eric Sunshine, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> Aditya Garg (10):
> imap-send: fix bug causing cfg->folder being set to NULL
> imap-send: fix memory leak in case auth_cram_md5 fails
> imap-send: gracefully fail if CRAM-MD5 authentication is requested
> without OpenSSL
> imap-send: add support for OAuth2.0 authentication
> imap-send: add PLAIN authentication method to OpenSSL
> imap-send: enable specifying the folder using the command line
> imap-send: fix minor mistakes in the logs
> imap-send: display port alongwith host when git credential is invoked
> imap-send: display the destination mailbox when sending a message
> imap-send: add ability to list the available folders
Very nicely organized, starting from obvious fixes and usability
polishing at the beginning of the series, followed by feature
enhancements to add new variants to the existing framework, followed
by three new features. The "ok"->"OK" change seems somewhat out of
place (I would have done it early if the changes are not controversial,
or very late after the series if the changes looked merely subjective,
and not in the middle either case), but other than that the series is
now organized perfectly.
I think the "fixes" and "auth method enhancements" in the earlier
part are the same as before and I was happy with the resulting code.
I didn't seriously read the last three or four patches during the
previous round, so I would say they still need reviews, but the
early part of the series now looks very well.
Thanks.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-06-08 20:50 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Junio C Hamano
@ 2025-06-09 4:31 ` Aditya Garg
2025-06-09 7:23 ` Aditya Garg
1 sibling, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 4:31 UTC (permalink / raw)
To: Junio C Hamano
Cc: git, Eric Sunshine, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble, Phillip Wood
On 9 June 2025 2:20:51 am IST, Junio C Hamano <gitster@pobox.com> wrote:
>Aditya Garg <gargaditya08@live.com> writes:
>
>> Aditya Garg (10):
>> imap-send: fix bug causing cfg->folder being set to NULL
>> imap-send: fix memory leak in case auth_cram_md5 fails
>> imap-send: gracefully fail if CRAM-MD5 authentication is requested
>> without OpenSSL
>> imap-send: add support for OAuth2.0 authentication
>> imap-send: add PLAIN authentication method to OpenSSL
>> imap-send: enable specifying the folder using the command line
>> imap-send: fix minor mistakes in the logs
>> imap-send: display port alongwith host when git credential is invoked
>> imap-send: display the destination mailbox when sending a message
>> imap-send: add ability to list the available folders
>
>Very nicely organized, starting from obvious fixes and usability
>polishing at the beginning of the series, followed by feature
>enhancements to add new variants to the existing framework, followed
>by three new features. The "ok"->"OK" change seems somewhat out of
>place (I would have done it early if the changes are not controversial,
>or very late after the series if the changes looked merely subjective,
>and not in the middle either case), but other than that the series is
>now organized perfectly.
Let's shift it at the last then.
>
>I think the "fixes" and "auth method enhancements" in the earlier
>part are the same as before and I was happy with the resulting code.
>
>I didn't seriously read the last three or four patches during the
>previous round, so I would say they still need reviews, but the
>early part of the series now looks very well.
>
>Thanks.
>
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v16 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (19 preceding siblings ...)
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-09 7:20 ` Aditya Garg
2025-06-09 7:20 ` [PATCH v16 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (9 more replies)
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 subsequent siblings)
23 siblings, 10 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 7:20 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch series does the following things:
Firstly it basically makes the imap-send command usable again since it
was broken because of not being able to correctly parse the config file.
Further it adds support for OAuth2.0 and PLAIN authentication to git
imap-send.
Lastly, it does some minor improvements including adding the ability to
specify the folder using the command line and ability to list the
available folders by adding a `--list` option.
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
v8: - Drop the patch that enabled user to choose between libcurl and openssl using the config
- Add ability to list the available folders by adding a `--list` option
v9: - Encourage users to use OAuth2.0 for Gmail (similar change done for send-email docs).
v10: - Fix comment styles
- Fix failing tests
v11: - Use lower case letters for the first word of a sendtence in an error message
and avoid using full stops at the end of a sentence.
v12: - Gracefully exit PLAIN, CRAM-MD5, OAUTHBEARER and XOAUTH2 authentication methods
if OpenSSL support is not compiled in, but is requested by the user.
- Use backticks for string literals.
- Wrap documentation text to 75 columns.
- End the last member of enum CAPABILITY with a trailing comma.
v13: - Fix logic error which was using || instead of && when checking if
the authentication method is neither XOAUTH2 nor OAUTHBEARER.
v14: - Specify why we are not using CURLOPT_PASSWORD for OAuth2.0
methods using a comment.
- Add a function try_auth_method() to reduce code duplication
when trying to authenticate using a specific method.
v15: - Simply rearrange the patches to make the cram md5 patches come
before adding OAuth2.0 and PLAIN authentication methods. No
change has been done to the code itself.
v16: - Rearrage some more patches so that the two new features, i.e.,
--folder and --list come just after the new authentication
methods. Then the two patches with minor improvements of displaying
the destination mailbox and displaying port alongwith host have
been added. The patch fixing other minor mistakes in the logs has
been moved to the end. Just like v15, no change has been done
to the code itself.
Aditya Garg (10):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: gracefully fail if CRAM-MD5 authentication is requested
without OpenSSL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: enable specifying the folder using the command line
imap-send: add ability to list the available folders
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
imap-send: fix minor mistakes in the logs
Documentation/config/imap.adoc | 11 +-
Documentation/git-imap-send.adoc | 68 ++++-
imap-send.c | 412 ++++++++++++++++++++++++++-----
3 files changed, 414 insertions(+), 77 deletions(-)
Range-diff against v15:
-: ---------- > 1: 3e3ddf7077 imap-send: fix bug causing cfg->folder being set to NULL
-: ---------- > 2: 417b3b8e38 imap-send: fix memory leak in case auth_cram_md5 fails
-: ---------- > 3: c4216528e7 imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
3: 668e62c0e0 ! 4: b38fca0e6a imap-send: display the destination mailbox when sending a message
@@ Metadata
Author: Aditya Garg <gargaditya08@live.com>
## Commit message ##
- imap-send: display the destination mailbox when sending a message
+ imap-send: add support for OAuth2.0 authentication
- Whenever we sent a message using the `imap-send` command, it would
- display a log showing the number of messages which are to be sent.
- For example:
+ OAuth2.0 is a new way of authentication supported by various email providers
+ these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
+ for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
+ XOAUTH2 is Google's proprietary mechanism (See [3]).
- Sending 1 message
- 100% (1/1) done
+ [1]: https://datatracker.ietf.org/doc/html/rfc5801
+ [2]: https://datatracker.ietf.org/doc/html/rfc7628
+ [3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
- This had been made more informative by adding the name of the destination
- folder as well:
+ Signed-off-by: Aditya Garg <gargaditya08@live.com>
- Sending 1 message to Drafts folder...
- 100% (1/1) done
+ ## Documentation/config/imap.adoc ##
+@@ Documentation/config/imap.adoc: imap.authMethod::
+ Specify the authentication method for authenticating with the IMAP server.
+ If Git was built with the NO_CURL option, or if your curl version is older
+ than 7.34.0, or if you're running git-imap-send with the `--no-curl`
+- option, the only supported method is 'CRAM-MD5'. If this is not set
+- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
++ option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
++ `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
++ plaintext `LOGIN` command.
- Signed-off-by: Aditya Garg <gargaditya08@live.com>
+ ## Documentation/git-imap-send.adoc ##
+@@ Documentation/git-imap-send.adoc: Using Gmail's IMAP interface:
+
+ ---------
+ [imap]
+- folder = "[Gmail]/Drafts"
+- host = imaps://imap.gmail.com
+- user = user@gmail.com
+- port = 993
++ folder = "[Gmail]/Drafts"
++ host = imaps://imap.gmail.com
++ user = user@gmail.com
++ port = 993
+ ---------
+
++Gmail does not allow using your regular password for `git imap-send`.
++If you have multi-factor authentication set up on your Gmail account, you
++can generate an app-specific password for use with `git imap-send`.
++Visit https://security.google.com/settings/security/apppasswords to create
++it. Alternatively, use OAuth2.0 authentication as described below.
++
+ [NOTE]
+ You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
+ that the "Folder doesn't exist".
+@@ Documentation/git-imap-send.adoc: that the "Folder doesn't exist".
+ If your Gmail account is set to another language than English, the name of the "Drafts"
+ folder will be localized.
+
++If you want to use OAuth2.0 based authentication, you can specify
++`OAUTHBEARER` or `XOAUTH2` mechanism in your config. It is more secure
++than using app-specific passwords, and also does not enforce the need of
++having multi-factor authentication. You will have to use an OAuth2.0
++access token in place of your password when using this authentication.
++
++---------
++[imap]
++ folder = "[Gmail]/Drafts"
++ host = imaps://imap.gmail.com
++ user = user@gmail.com
++ port = 993
++ authmethod = OAUTHBEARER
++---------
++
++Using Outlook's IMAP interface:
++
++Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
++supports only `XOAUTH2` as the mechanism.
++
++---------
++[imap]
++ folder = "Drafts"
++ host = imaps://outlook.office365.com
++ user = user@outlook.com
++ port = 993
++ authmethod = XOAUTH2
++---------
++
+ Once the commits are ready to be sent, run the following command:
+
+ $ git format-patch --cover-letter -M --stdout origin/master | git imap-send
+@@ Documentation/git-imap-send.adoc: Just make sure to disable line wrapping in the email client (Gmail's web
+ interface will wrap lines no matter what, so you need to use a real
+ IMAP client).
+
++In case you are using OAuth2.0 authentication, it is easier to use credential
++helpers to generate tokens. Credential helpers suggested in
++linkgit:git-send-email[1] can be used for `git imap-send` as well.
++
+ CAUTION
+ -------
+ It is still your responsibility to make sure that the email message
## imap-send.c ##
-@@ imap-send.c: static int append_msgs_to_imap(struct imap_server_conf *server,
+@@ imap-send.c: enum CAPABILITY {
+ LITERALPLUS,
+ NAMESPACE,
+ STARTTLS,
+- AUTH_CRAM_MD5
++ AUTH_CRAM_MD5,
++ AUTH_OAUTHBEARER,
++ AUTH_XOAUTH2,
+ };
+
+ static const char *cap_list[] = {
+@@ imap-send.c: static const char *cap_list[] = {
+ "NAMESPACE",
+ "STARTTLS",
+ "AUTH=CRAM-MD5",
++ "AUTH=OAUTHBEARER",
++ "AUTH=XOAUTH2",
+ };
+
+ #define RESP_OK 0
+@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const char *pass)
+ return (char *)response_64;
+ }
+
++static char *oauthbearer_base64(const char *user, const char *access_token)
++{
++ int raw_len, b64_len;
++ char *raw, *b64;
++
++ /*
++ * Compose the OAUTHBEARER string
++ *
++ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
++ *
++ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
++ * * gs2-cb-flag `n` -> client does not support CB
++ * * gs2-authzid `a=" {User} "`
++ *
++ * The second part are key value pairs containing host, port and auth as
++ * described in RFC7628.
++ *
++ * https://datatracker.ietf.org/doc/html/rfc5801
++ * https://datatracker.ietf.org/doc/html/rfc7628
++ */
++ raw_len = strlen(user) + strlen(access_token) + 20;
++ raw = xmallocz(raw_len + 1);
++ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
++
++ /* Base64 encode */
++ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
++ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
++ free(raw);
++
++ if (b64_len < 0) {
++ free(b64);
++ return NULL;
++ }
++ return b64;
++}
++
++static char *xoauth2_base64(const char *user, const char *access_token)
++{
++ int raw_len, b64_len;
++ char *raw, *b64;
++
++ /*
++ * Compose the XOAUTH2 string
++ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
++ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
++ */
++ raw_len = strlen(user) + strlen(access_token) + 20;
++ raw = xmallocz(raw_len + 1);
++ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
++
++ /* Base64 encode */
++ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
++ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
++ free(raw);
++
++ if (b64_len < 0) {
++ free(b64);
++ return NULL;
++ }
++ return b64;
++}
++
+ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+ {
+ int ret;
+@@ imap-send.c: static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+ return 0;
+ }
+
++static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
++{
++ int ret;
++ char *b64;
++
++ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
++ if (!b64)
++ return error("OAUTHBEARER: base64 encoding failed");
++
++ /* Send the base64-encoded response */
++ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
++ if (ret != (int)strlen(b64)) {
++ free(b64);
++ return error("IMAP error: sending OAUTHBEARER response failed");
++ }
++
++ free(b64);
++ return 0;
++}
++
++static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
++{
++ int ret;
++ char *b64;
++
++ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
++ if (!b64)
++ return error("XOAUTH2: base64 encoding failed");
++
++ /* Send the base64-encoded response */
++ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
++ if (ret != (int)strlen(b64)) {
++ free(b64);
++ return error("IMAP error: sending XOAUTH2 response failed");
++ }
++
++ free(b64);
++ return 0;
++}
++
+ #else
+
+ #define auth_cram_md5 NULL
++#define auth_oauthbearer NULL
++#define auth_xoauth2 NULL
+
+ #endif
+
+@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
+ if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
+ goto bail;
++ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
++ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
++ goto bail;
++ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
++ if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
++ goto bail;
+ } else {
+ fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ goto bail;
+@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
+
+ server_fill_credential(srvc, cred);
+ curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
+- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
++
++ /*
++ * Use CURLOPT_PASSWORD irrespective of whether there is
++ * an auth method specified or not, unless it's OAuth2.0,
++ * where we use CURLOPT_XOAUTH2_BEARER.
++ */
++ if (!srvc->auth_method ||
++ (strcmp(srvc->auth_method, "XOAUTH2") &&
++ strcmp(srvc->auth_method, "OAUTHBEARER")))
++ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
+ strbuf_addstr(&path, srvc->host);
+@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
+ curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
+
+ if (srvc->auth_method) {
+- struct strbuf auth = STRBUF_INIT;
+- strbuf_addstr(&auth, "AUTH=");
+- strbuf_addstr(&auth, srvc->auth_method);
+- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+- strbuf_release(&auth);
++ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
++ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
++
++ /*
++ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
++ * upon debugging, it has been found that it is capable of detecting
++ * the best option out of OAUTHBEARER and XOAUTH2.
++ */
++ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
++ } else {
++ struct strbuf auth = STRBUF_INIT;
++ strbuf_addstr(&auth, "AUTH=");
++ strbuf_addstr(&auth, srvc->auth_method);
++ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
++ strbuf_release(&auth);
++ }
}
- ctx->name = server->folder;
-
-- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
-+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
-+ total, (total != 1) ? "s" : "", server->folder);
- while (1) {
- unsigned percent = n * 100 / total;
-
-@@ imap-send.c: static int curl_append_msgs_to_imap(struct imap_server_conf *server,
- curl = setup_curl(server, &cred);
- curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
-
-- fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
-+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
-+ total, (total != 1) ? "s" : "", server->folder);
- while (1) {
- unsigned percent = n * 100 / total;
- int prev_len;
+
+ if (!srvc->use_ssl)
-: ---------- > 5: 86d3d2c54d imap-send: add PLAIN authentication method to OpenSSL
-: ---------- > 6: 7674e749c8 imap-send: enable specifying the folder using the command line
4: 4d9a3b5661 ! 7: 90ce3a63f3 imap-send: add ability to list the available folders
@@ imap-send.c: static int curl_append_msgs_to_imap(struct imap_server_conf *server
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
@@ imap-send.c: static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
@@ imap-send.c: int cmd_main(int argc, const char **argv)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
-- fprintf(stderr, "no IMAP store specified\n");
+- fprintf(stderr, "no imap store specified\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no IMAP host specified\n");
+ fprintf(stderr, "no imap host specified\n");
@@ imap-send.c: int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
@@ imap-send.c: int cmd_main(int argc, const char **argv)
+ }
+
+ if (!server.folder) {
-+ fprintf(stderr, "no IMAP store specified\n");
++ fprintf(stderr, "no imap store specified\n");
+ ret = 1;
+ goto out;
+ }
2: b2e7ef35ed = 8: 1bdd054908 imap-send: display port alongwith host when git credential is invoked
-: ---------- > 9: e381120ab5 imap-send: display the destination mailbox when sending a message
1: a67322ce06 ! 10: 6561d45bee imap-send: fix minor mistakes in the logs
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *
srvc->user, srvc->host);
goto bail;
}
-@@ imap-send.c: static int append_msgs_to_imap(struct imap_server_conf *server,
- }
- ctx->name = server->folder;
-
-- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
-+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
- while (1) {
- unsigned percent = n * 100 / total;
-
-@@ imap-send.c: static int curl_append_msgs_to_imap(struct imap_server_conf *server,
- curl = setup_curl(server, &cred);
- curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
-
-- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
-+ fprintf(stderr, "Sending %d message%s\n", total, (total != 1) ? "s" : "");
- while (1) {
- unsigned percent = n * 100 / total;
- int prev_len;
@@ imap-send.c: int cmd_main(int argc, const char **argv)
- server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
-- fprintf(stderr, "no imap store specified\n");
-+ fprintf(stderr, "no IMAP store specified\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
@@ imap-send.c: int cmd_main(int argc, const char **argv)
goto out;
}
@@ imap-send.c: int cmd_main(int argc, const char **argv)
+ }
+
+ if (!server.folder) {
+- fprintf(stderr, "no imap store specified\n");
++ fprintf(stderr, "no IMAP store specified\n");
+ ret = 1;
+ goto out;
+ }
+@@ imap-send.c: int cmd_main(int argc, const char **argv)
total = count_messages(&all_msgs);
if (!total) {
--
2.49.0
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v16 01/10] imap-send: fix bug causing cfg->folder being set to NULL
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
@ 2025-06-09 7:20 ` Aditya Garg
2025-06-09 7:20 ` [PATCH v16 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (8 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 7:20 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 27dc033c7f..37f94a37e8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v16 02/10] imap-send: fix memory leak in case auth_cram_md5 fails
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
2025-06-09 7:20 ` [PATCH v16 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-06-09 7:20 ` Aditya Garg
2025-06-09 7:20 ` [PATCH v16 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
` (7 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 7:20 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 37f94a37e8..1a582c8443 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -905,8 +905,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v16 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
2025-06-09 7:20 ` [PATCH v16 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-09 7:20 ` [PATCH v16 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-06-09 7:20 ` Aditya Garg
2025-06-09 19:15 ` Junio C Hamano
2025-06-09 7:20 ` [PATCH v16 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (6 subsequent siblings)
9 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 7:20 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Unlike PLAIN, XOAUTH2 and OAUTHBEARER, CRAM-MD5 authentication is not
supported by libcurl and requires OpenSSL. If the user tries to use
CRAM-MD5 authentication without OpenSSL, the previous behaviour was to
attempt to authenticate and fail with a die(error). Handle this in a
better way by first checking if OpenSSL is available and then attempting
to authenticate. If OpenSSL is not available, print an error message and
exit gracefully.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 66 +++++++++++++++++++++++++++++++----------------------
1 file changed, 39 insertions(+), 27 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 1a582c8443..f55399cd9e 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -885,18 +885,6 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
-#else
-
-static char *cram(const char *challenge_64 UNUSED,
- const char *user UNUSED,
- const char *pass UNUSED)
-{
- 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, const char *prompt)
{
int ret;
@@ -915,6 +903,12 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+#else
+
+#define auth_cram_md5 NULL
+
+#endif
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -934,6 +928,38 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
srvc->pass = xstrdup(cred->password);
}
+static int try_auth_method(struct imap_server_conf *srvc,
+ struct imap_store *ctx,
+ struct imap *imap,
+ const char *auth_method,
+ enum CAPABILITY cap,
+ int (*fn)(struct imap_store *, const char *))
+{
+ struct imap_cmd_cb cb = {0};
+
+ if (!CAP(cap)) {
+ fprintf(stderr, "You specified "
+ "%s as authentication method, "
+ "but %s doesn't support it.\n",
+ auth_method, srvc->host);
+ return -1;
+ }
+ cb.cont = fn;
+
+ if (NOT_CONSTANT(!cb.cont)) {
+ fprintf(stderr, "If you want to use %s authentication mechanism, "
+ "you have to build git-imap-send with OpenSSL library.",
+ auth_method);
+ return -1;
+ }
+ if (imap_exec(ctx, &cb, "AUTHENTICATE %s", auth_method) != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE %s failed\n",
+ auth_method);
+ return -1;
+ }
+ return 0;
+}
+
static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const char *folder)
{
struct credential cred = CREDENTIAL_INIT;
@@ -1089,23 +1115,9 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
server_fill_credential(srvc, &cred);
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");
+ if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
- }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v16 04/10] imap-send: add support for OAuth2.0 authentication
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
` (2 preceding siblings ...)
2025-06-09 7:20 ` [PATCH v16 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
@ 2025-06-09 7:20 ` Aditya Garg
2025-06-09 7:20 ` [PATCH v16 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (5 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 7:20 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 47 +++++++++-
imap-send.c | 148 +++++++++++++++++++++++++++++--
3 files changed, 187 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..29b998d5ff 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
+ `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext `LOGIN` command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..8adf0e5aac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,18 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your regular password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you
+can generate an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create
+it. Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +122,35 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify
+`OAUTHBEARER` or `XOAUTH2` mechanism in your config. It is more secure
+than using app-specific passwords, and also does not enforce the need of
+having multi-factor authentication. You will have to use an OAuth2.0
+access token in place of your password when using this authentication.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +159,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index f55399cd9e..5373f18b94 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2,
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,68 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -903,9 +969,51 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
#else
#define auth_cram_md5 NULL
+#define auth_oauthbearer NULL
+#define auth_xoauth2 NULL
#endif
@@ -1118,6 +1226,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
+ goto bail;
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1419,7 +1533,16 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ /*
+ * Use CURLOPT_PASSWORD irrespective of whether there is
+ * an auth method specified or not, unless it's OAuth2.0,
+ * where we use CURLOPT_XOAUTH2_BEARER.
+ */
+ if (!srvc->auth_method ||
+ (strcmp(srvc->auth_method, "XOAUTH2") &&
+ strcmp(srvc->auth_method, "OAUTHBEARER")))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1437,11 +1560,22 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /*
+ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v16 05/10] imap-send: add PLAIN authentication method to OpenSSL
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
` (3 preceding siblings ...)
2025-06-09 7:20 ` [PATCH v16 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-09 7:20 ` Aditya Garg
2025-06-09 7:20 ` [PATCH v16 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
` (4 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 7:20 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +--
imap-send.c | 63 +++++++++++++++++++++++++++++++++-
2 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 29b998d5ff..7c8b2dcce4 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
- `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are `PLAIN`, `CRAM-MD5`, `OAUTHBEARER`
+ and `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
plaintext `LOGIN` command.
diff --git a/imap-send.c b/imap-send.c
index 5373f18b94..c6e47ddc42 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2,
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,41 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +988,26 @@ static char *xoauth2_base64(const char *user, const char *access_token)
return b64;
}
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1011,6 +1068,7 @@ static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
#else
+#define auth_plain NULL
#define auth_cram_md5 NULL
#define auth_oauthbearer NULL
#define auth_xoauth2 NULL
@@ -1223,7 +1281,10 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
server_fill_credential(srvc, &cred);
if (srvc->auth_method) {
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (try_auth_method(srvc, ctx, imap, "PLAIN", AUTH_PLAIN, auth_plain))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
} else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v16 06/10] imap-send: enable specifying the folder using the command line
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
` (4 preceding siblings ...)
2025-06-09 7:20 ` [PATCH v16 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-06-09 7:20 ` Aditya Garg
2025-06-09 18:33 ` Junio C Hamano
2025-06-09 7:20 ` [PATCH v16 07/10] imap-send: add ability to list the available folders Aditya Garg
` (3 subsequent siblings)
9 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 7:20 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 6 ++++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 7c8b2dcce4..4682a6bd03 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,9 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
+ `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
+ the value of this configuration variable is used as the fallback
+ default value when the `--folder` option is not given.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 8adf0e5aac..4a0487b66e 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields `From`, `Date`, and `Subject` in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index c6e47ddc42..a4cccb9110 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1729,6 +1731,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v16 07/10] imap-send: add ability to list the available folders
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
` (5 preceding siblings ...)
2025-06-09 7:20 ` [PATCH v16 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-06-09 7:20 ` Aditya Garg
2025-06-09 18:42 ` Junio C Hamano
2025-06-09 7:20 ` [PATCH v16 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
` (2 subsequent siblings)
9 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 7:20 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Various IMAP servers have different ways to name common folders.
For example, the folder where all deleted messages are stored is often
named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
on Gmail it is named "[Gmail]/Drafts".
This commit adds a `--list` command to the `imap-send` tool that lists
the available folders on the IMAP server, allowing users to see
which folders are available and how they are named. A sample output
looks like this when run against a Gmail server:
Fetching the list of available folders...
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasChildren \Noselect) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
For OpenSSL, this is achived by running the 'IMAP LIST' command and
parsing the response. This command is specified in RFC6154:
https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
For libcurl, the example code published in the libcurl documentation
is used to implement this functionality:
https://curl.se/libcurl/c/imap-list.html
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/git-imap-send.adoc | 6 +-
imap-send.c | 98 ++++++++++++++++++++++++++------
2 files changed, 87 insertions(+), 17 deletions(-)
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 4a0487b66e..17147f93c3 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+'git imap-send' --list
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
+--list::
+ Run the IMAP LIST command to output a list of all the folders present.
CONFIGURATION
-------------
@@ -123,7 +126,8 @@ it. Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
-that the "Folder doesn't exist".
+that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
+list of available folders.
[NOTE]
If your Gmail account is set to another language than English, the name of the "Drafts"
diff --git a/imap-send.c b/imap-send.c
index a4cccb9110..f03a92a2fb 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -45,15 +45,21 @@
#endif
static int verbosity;
+static int list_folders = 0;
static int use_curl = USE_CURL_DEFAULT;
static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
+ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
+ "git imap-send --list",
+ NULL
+};
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
+ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
OPT_END()
};
@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
if (b->buf[b->offset + 1] == '\n') {
b->buf[b->offset] = 0; /* terminate the string */
b->offset += 2; /* next line */
- if (0 < verbosity)
+ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
puts(*s);
return 0;
}
@@ -1579,6 +1585,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
+static int list_imap_folders(struct imap_server_conf *server)
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
+ fprintf(stderr, "failed to connect to IMAP server\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
+ fprintf(stderr, "failed to list folders\n");
+ imap_close_store(ctx);
+ return 1;
+ }
+
+ imap_close_store(ctx);
+ return 0;
+}
+
#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
@@ -1612,11 +1638,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
- die("failed to encode server folder");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+ die("failed to encode server folder");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ -1647,10 +1675,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
-
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(curl);
@@ -1669,6 +1693,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
struct credential cred = CREDENTIAL_INIT;
curl = setup_curl(server, &cred);
+
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
@@ -1714,6 +1742,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
+
+static int curl_list_imap_folders(struct imap_server_conf *server)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct credential cred = CREDENTIAL_INIT;
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ curl = setup_curl(server, &cred);
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ if (cred.username) {
+ if (res == CURLE_OK)
+ credential_approve(the_repository, &cred);
+ else if (res == CURLE_LOGIN_DENIED)
+ credential_reject(the_repository, &cred);
+ }
+
+ credential_clear(&cred);
+
+ return res != CURLE_OK;
+}
#endif
int cmd_main(int argc, const char **argv)
@@ -1754,11 +1807,6 @@ int cmd_main(int argc, const char **argv)
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "no imap host specified\n");
@@ -1768,6 +1816,24 @@ int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
+ if (list_folders) {
+ if (server.tunnel)
+ ret = list_imap_folders(&server);
+#ifdef USE_CURL_FOR_IMAP_SEND
+ else if (use_curl)
+ ret = curl_list_imap_folders(&server);
+#endif
+ else
+ ret = list_imap_folders(&server);
+ goto out;
+ }
+
+ if (!server.folder) {
+ fprintf(stderr, "no imap store specified\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("could not read from stdin"));
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v16 08/10] imap-send: display port alongwith host when git credential is invoked
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
` (6 preceding siblings ...)
2025-06-09 7:20 ` [PATCH v16 07/10] imap-send: add ability to list the available folders Aditya Garg
@ 2025-06-09 7:20 ` Aditya Garg
2025-06-09 18:55 ` Junio C Hamano
2025-06-09 7:20 ` [PATCH v16 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-09 7:20 ` [PATCH v16 10/10] imap-send: fix minor mistakes in the logs Aditya Garg
9 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 7:20 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
FWIW, if no port is specified by the user, the default port, 993 for
IMAPS and 143 for IMAP is used by the code. So, the case of no port
defined for the helper is not possible, and therefore is not added.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index f03a92a2fb..9807012169 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1089,7 +1089,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v16 09/10] imap-send: display the destination mailbox when sending a message
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
` (7 preceding siblings ...)
2025-06-09 7:20 ` [PATCH v16 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-06-09 7:20 ` Aditya Garg
2025-06-09 18:57 ` Junio C Hamano
2025-06-09 7:20 ` [PATCH v16 10/10] imap-send: fix minor mistakes in the logs Aditya Garg
9 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 7:20 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 9807012169..3d6bcd7e88 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1563,7 +1563,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1699,7 +1700,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v16 10/10] imap-send: fix minor mistakes in the logs
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
` (8 preceding siblings ...)
2025-06-09 7:20 ` [PATCH v16 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-06-09 7:20 ` Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 7:20 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some minor mistakes have been found in the logs. Most of them include
error messages starting with a capital letter, and ending with a period.
Also, abbreviations like "IMAP" and "OK" should be in uppercase. Fix them.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 3d6bcd7e88..16c2e641ac 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -211,7 +211,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in\n");
return -1;
}
@@ -1026,7 +1026,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1166,7 +1166,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1185,7 +1185,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1223,7 +1223,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1237,7 +1237,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1249,7 +1249,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1302,12 +1302,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
goto bail;
} else {
- fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ fprintf(stderr, "unknown authentication method:%s\n", srvc->host);
goto bail;
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "skipping account %s@%s, server forbids LOGIN\n",
srvc->user, srvc->host);
goto bail;
}
@@ -1811,7 +1811,7 @@ int cmd_main(int argc, const char **argv)
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "no IMAP host specified\n");
ret = 1;
goto out;
}
@@ -1831,7 +1831,7 @@ int cmd_main(int argc, const char **argv)
}
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "no IMAP store specified\n");
ret = 1;
goto out;
}
@@ -1851,7 +1851,7 @@ int cmd_main(int argc, const char **argv)
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "no messages found to send\n");
ret = 1;
goto out;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-06-08 20:50 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Junio C Hamano
2025-06-09 4:31 ` Aditya Garg
@ 2025-06-09 7:23 ` Aditya Garg
1 sibling, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 7:23 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
> On 9 Jun 2025, at 2:21 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>> Aditya Garg (10):
>> imap-send: fix bug causing cfg->folder being set to NULL
>> imap-send: fix memory leak in case auth_cram_md5 fails
>> imap-send: gracefully fail if CRAM-MD5 authentication is requested
>> without OpenSSL
>> imap-send: add support for OAuth2.0 authentication
>> imap-send: add PLAIN authentication method to OpenSSL
>> imap-send: enable specifying the folder using the command line
>> imap-send: fix minor mistakes in the logs
>> imap-send: display port alongwith host when git credential is invoked
>> imap-send: display the destination mailbox when sending a message
>> imap-send: add ability to list the available folders
>
> Very nicely organized, starting from obvious fixes and usability
> polishing at the beginning of the series, followed by feature
> enhancements to add new variants to the existing framework, followed
> by three new features. The "ok"->"OK" change seems somewhat out of
> place (I would have done it early if the changes are not controversial,
> or very late after the series if the changes looked merely subjective,
> and not in the middle either case), but other than that the series is
> now organized perfectly.
>
Fixed in v16
> I think the "fixes" and "auth method enhancements" in the earlier
> part are the same as before and I was happy with the resulting code.
>
> I didn't seriously read the last three or four patches during the
> previous round, so I would say they still need reviews, but the
> early part of the series now looks very well.
>
> Thanks.
>
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (20 preceding siblings ...)
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
@ 2025-06-09 15:41 ` Aditya Garg
2025-06-09 15:41 ` [PATCH v17 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (9 more replies)
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
23 siblings, 10 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 15:41 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
v8: - Drop the patch that enabled user to choose between libcurl and openssl using the config
- Add ability to list the available folders by adding a `--list` option
v9: - Encourage users to use OAuth2.0 for Gmail (similar change done for send-email docs).
v10: - Fix comment styles
- Fix failing tests
v11: - Use lower case letters for the first word of a sendtence in an error message
and avoid using full stops at the end of a sentence.
v12: - Gracefully exit PLAIN, CRAM-MD5, OAUTHBEARER and XOAUTH2 authentication methods
if OpenSSL support is not compiled in, but is requested by the user.
- Use backticks for string literals.
- Wrap documentation text to 75 columns.
- End the last member of enum CAPABILITY with a trailing comma.
v13: - Fix logic error which was using || instead of && when checking if
the authentication method is neither XOAUTH2 nor OAUTHBEARER.
v14: - Specify why we are not using CURLOPT_PASSWORD for OAuth2.0
methods using a comment.
- Add a function try_auth_method() to reduce code duplication
when trying to authenticate using a specific method.
v15: - Simply rearrange the patches to make the cram md5 patches come
before adding OAuth2.0 and PLAIN authentication methods. No
change has been done to the code itself.
v16: - Rearrage some more patches so that the two new features, i.e.,
--folder and --list come just after the new authentication
methods. Then the two patches with minor improvements of displaying
the destination mailbox and displaying port alongwith host have
been added. The patch fixing other minor mistakes in the logs has
been moved to the end. Just like v15, no change has been done
to the code itself.
v17: - Rebase on top of master where 30325e2 was causing a conflict.
(Sorry for the bad range diff, but I think its easy to understand)
Aditya Garg (10):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: gracefully fail if CRAM-MD5 authentication is requested
without OpenSSL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: enable specifying the folder using the command line
imap-send: add ability to list the available folders
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
imap-send: fix minor mistakes in the logs
Documentation/config/imap.adoc | 11 +-
Documentation/git-imap-send.adoc | 68 ++++-
imap-send.c | 412 ++++++++++++++++++++++++++-----
3 files changed, 414 insertions(+), 77 deletions(-)
Range-diff against v16:
1: 194d108e15 < -: ---------- builtin/am: fix memory leak in `split_mail_stgit_series`
2: 798369e8ce < -: ---------- t1001: replace 'test -f' with 'test_path_is_file'
3: dce2b90fb1 < -: ---------- oidmap: rename oidmap_free() to oidmap_clear()
4: 9369c83cce < -: ---------- oidmap: add size function
5: f0a73c8578 < -: ---------- raw_object_store: drop extra pointer to replace_map
6: c4b2850438 < -: ---------- reftable/writer: fix memory leak when `padded_write()` fails
7: a795acc6ed < -: ---------- reftable/writer: fix memory leak when `writer_index_hash()` fails
8: 8fdb6df271 < -: ---------- reftable: fix perf regression when reading blocks of unwanted type
9: ce0b8c96b9 < -: ---------- The sixteenth batch
10: 3e3ddf7077 = 1: 4accbe6ecf imap-send: fix bug causing cfg->folder being set to NULL
11: 417b3b8e38 = 2: 1cfd66ccea imap-send: fix memory leak in case auth_cram_md5 fails
12: c4216528e7 = 3: 12ff5135be imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
13: b38fca0e6a ! 4: 43b18dbfb0 imap-send: add support for OAuth2.0 authentication
@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct crede
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
- curl_easy_setopt(curl, CURLOPT_PORT, srvc->port);
+ curl_easy_setopt(curl, CURLOPT_PORT, (long)srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
14: 86d3d2c54d = 5: 1ebf9f935f imap-send: add PLAIN authentication method to OpenSSL
15: 7674e749c8 = 6: 0c6283407c imap-send: enable specifying the folder using the command line
16: 90ce3a63f3 ! 7: f59cb1dca1 imap-send: add ability to list the available folders
@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct crede
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (long)srvc->ssl_verify);
+ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (long)srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
17: 1bdd054908 = 8: 1247afbe78 imap-send: display port alongwith host when git credential is invoked
18: e381120ab5 = 9: c30ecbf508 imap-send: display the destination mailbox when sending a message
19: 6561d45bee = 10: eaff4db692 imap-send: fix minor mistakes in the logs
--
2.49.0.824.geaff4db692
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v17 01/10] imap-send: fix bug causing cfg->folder being set to NULL
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-09 15:41 ` Aditya Garg
2025-06-09 15:41 ` [PATCH v17 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (8 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 15:41 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 2e812f5a6e..3eed2360fd 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0.824.geaff4db692
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v17 02/10] imap-send: fix memory leak in case auth_cram_md5 fails
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-09 15:41 ` [PATCH v17 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-06-09 15:41 ` Aditya Garg
2025-06-09 15:41 ` [PATCH v17 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
` (7 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 15:41 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 3eed2360fd..cee8f5690d 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -905,8 +905,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0.824.geaff4db692
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v17 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-09 15:41 ` [PATCH v17 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-09 15:41 ` [PATCH v17 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-06-09 15:41 ` Aditya Garg
2025-06-09 15:41 ` [PATCH v17 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (6 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 15:41 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Unlike PLAIN, XOAUTH2 and OAUTHBEARER, CRAM-MD5 authentication is not
supported by libcurl and requires OpenSSL. If the user tries to use
CRAM-MD5 authentication without OpenSSL, the previous behaviour was to
attempt to authenticate and fail with a die(error). Handle this in a
better way by first checking if OpenSSL is available and then attempting
to authenticate. If OpenSSL is not available, print an error message and
exit gracefully.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 66 +++++++++++++++++++++++++++++++----------------------
1 file changed, 39 insertions(+), 27 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index cee8f5690d..39013330a7 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -885,18 +885,6 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
-#else
-
-static char *cram(const char *challenge_64 UNUSED,
- const char *user UNUSED,
- const char *pass UNUSED)
-{
- 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, const char *prompt)
{
int ret;
@@ -915,6 +903,12 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+#else
+
+#define auth_cram_md5 NULL
+
+#endif
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -934,6 +928,38 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
srvc->pass = xstrdup(cred->password);
}
+static int try_auth_method(struct imap_server_conf *srvc,
+ struct imap_store *ctx,
+ struct imap *imap,
+ const char *auth_method,
+ enum CAPABILITY cap,
+ int (*fn)(struct imap_store *, const char *))
+{
+ struct imap_cmd_cb cb = {0};
+
+ if (!CAP(cap)) {
+ fprintf(stderr, "You specified "
+ "%s as authentication method, "
+ "but %s doesn't support it.\n",
+ auth_method, srvc->host);
+ return -1;
+ }
+ cb.cont = fn;
+
+ if (NOT_CONSTANT(!cb.cont)) {
+ fprintf(stderr, "If you want to use %s authentication mechanism, "
+ "you have to build git-imap-send with OpenSSL library.",
+ auth_method);
+ return -1;
+ }
+ if (imap_exec(ctx, &cb, "AUTHENTICATE %s", auth_method) != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE %s failed\n",
+ auth_method);
+ return -1;
+ }
+ return 0;
+}
+
static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const char *folder)
{
struct credential cred = CREDENTIAL_INIT;
@@ -1089,23 +1115,9 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
server_fill_credential(srvc, &cred);
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");
+ if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
- }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
--
2.49.0.824.geaff4db692
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v17 04/10] imap-send: add support for OAuth2.0 authentication
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-06-09 15:41 ` [PATCH v17 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
@ 2025-06-09 15:41 ` Aditya Garg
2025-06-09 15:41 ` [PATCH v17 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (5 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 15:41 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 47 +++++++++-
imap-send.c | 148 +++++++++++++++++++++++++++++--
3 files changed, 187 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..29b998d5ff 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
+ `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext `LOGIN` command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..8adf0e5aac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,18 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your regular password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you
+can generate an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create
+it. Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +122,35 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify
+`OAUTHBEARER` or `XOAUTH2` mechanism in your config. It is more secure
+than using app-specific passwords, and also does not enforce the need of
+having multi-factor authentication. You will have to use an OAuth2.0
+access token in place of your password when using this authentication.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +159,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 39013330a7..24eab86a1a 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2,
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,68 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -903,9 +969,51 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
#else
#define auth_cram_md5 NULL
+#define auth_oauthbearer NULL
+#define auth_xoauth2 NULL
#endif
@@ -1118,6 +1226,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
+ goto bail;
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1419,7 +1533,16 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ /*
+ * Use CURLOPT_PASSWORD irrespective of whether there is
+ * an auth method specified or not, unless it's OAuth2.0,
+ * where we use CURLOPT_XOAUTH2_BEARER.
+ */
+ if (!srvc->auth_method ||
+ (strcmp(srvc->auth_method, "XOAUTH2") &&
+ strcmp(srvc->auth_method, "OAUTHBEARER")))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1437,11 +1560,22 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, (long)srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /*
+ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0.824.geaff4db692
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v17 05/10] imap-send: add PLAIN authentication method to OpenSSL
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-06-09 15:41 ` [PATCH v17 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-09 15:41 ` Aditya Garg
2025-06-09 15:41 ` [PATCH v17 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
` (4 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 15:41 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +--
imap-send.c | 63 +++++++++++++++++++++++++++++++++-
2 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 29b998d5ff..7c8b2dcce4 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
- `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are `PLAIN`, `CRAM-MD5`, `OAUTHBEARER`
+ and `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
plaintext `LOGIN` command.
diff --git a/imap-send.c b/imap-send.c
index 24eab86a1a..64f66ec67d 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2,
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,41 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +988,26 @@ static char *xoauth2_base64(const char *user, const char *access_token)
return b64;
}
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1011,6 +1068,7 @@ static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
#else
+#define auth_plain NULL
#define auth_cram_md5 NULL
#define auth_oauthbearer NULL
#define auth_xoauth2 NULL
@@ -1223,7 +1281,10 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
server_fill_credential(srvc, &cred);
if (srvc->auth_method) {
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (try_auth_method(srvc, ctx, imap, "PLAIN", AUTH_PLAIN, auth_plain))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
} else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
--
2.49.0.824.geaff4db692
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v17 06/10] imap-send: enable specifying the folder using the command line
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-06-09 15:41 ` [PATCH v17 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-06-09 15:41 ` Aditya Garg
2025-06-09 15:41 ` [PATCH v17 07/10] imap-send: add ability to list the available folders Aditya Garg
` (3 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 15:41 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 6 ++++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 7c8b2dcce4..4682a6bd03 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,9 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
+ `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
+ the value of this configuration variable is used as the fallback
+ default value when the `--folder` option is not given.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 8adf0e5aac..4a0487b66e 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields `From`, `Date`, and `Subject` in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index 64f66ec67d..522d01c88e 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1729,6 +1731,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0.824.geaff4db692
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v17 07/10] imap-send: add ability to list the available folders
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-06-09 15:41 ` [PATCH v17 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-06-09 15:41 ` Aditya Garg
2025-06-09 15:41 ` [PATCH v17 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
` (2 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 15:41 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Various IMAP servers have different ways to name common folders.
For example, the folder where all deleted messages are stored is often
named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
on Gmail it is named "[Gmail]/Drafts".
This commit adds a `--list` command to the `imap-send` tool that lists
the available folders on the IMAP server, allowing users to see
which folders are available and how they are named. A sample output
looks like this when run against a Gmail server:
Fetching the list of available folders...
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasChildren \Noselect) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
For OpenSSL, this is achived by running the 'IMAP LIST' command and
parsing the response. This command is specified in RFC6154:
https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
For libcurl, the example code published in the libcurl documentation
is used to implement this functionality:
https://curl.se/libcurl/c/imap-list.html
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/git-imap-send.adoc | 6 +-
imap-send.c | 98 ++++++++++++++++++++++++++------
2 files changed, 87 insertions(+), 17 deletions(-)
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 4a0487b66e..17147f93c3 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+'git imap-send' --list
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
+--list::
+ Run the IMAP LIST command to output a list of all the folders present.
CONFIGURATION
-------------
@@ -123,7 +126,8 @@ it. Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
-that the "Folder doesn't exist".
+that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
+list of available folders.
[NOTE]
If your Gmail account is set to another language than English, the name of the "Drafts"
diff --git a/imap-send.c b/imap-send.c
index 522d01c88e..4ac0ba606c 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -45,15 +45,21 @@
#endif
static int verbosity;
+static int list_folders = 0;
static int use_curl = USE_CURL_DEFAULT;
static char *opt_folder = NULL;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
+ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
+ "git imap-send --list",
+ NULL
+};
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
+ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
OPT_END()
};
@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
if (b->buf[b->offset + 1] == '\n') {
b->buf[b->offset] = 0; /* terminate the string */
b->offset += 2; /* next line */
- if (0 < verbosity)
+ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
puts(*s);
return 0;
}
@@ -1579,6 +1585,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
+static int list_imap_folders(struct imap_server_conf *server)
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
+ fprintf(stderr, "failed to connect to IMAP server\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
+ fprintf(stderr, "failed to list folders\n");
+ imap_close_store(ctx);
+ return 1;
+ }
+
+ imap_close_store(ctx);
+ return 0;
+}
+
#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
@@ -1612,11 +1638,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
- die("failed to encode server folder");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+ die("failed to encode server folder");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ -1647,10 +1675,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (long)srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (long)srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
-
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(curl);
@@ -1669,6 +1693,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
struct credential cred = CREDENTIAL_INIT;
curl = setup_curl(server, &cred);
+
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
@@ -1714,6 +1742,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
+
+static int curl_list_imap_folders(struct imap_server_conf *server)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct credential cred = CREDENTIAL_INIT;
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ curl = setup_curl(server, &cred);
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ if (cred.username) {
+ if (res == CURLE_OK)
+ credential_approve(the_repository, &cred);
+ else if (res == CURLE_LOGIN_DENIED)
+ credential_reject(the_repository, &cred);
+ }
+
+ credential_clear(&cred);
+
+ return res != CURLE_OK;
+}
#endif
int cmd_main(int argc, const char **argv)
@@ -1754,11 +1807,6 @@ int cmd_main(int argc, const char **argv)
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "no imap host specified\n");
@@ -1768,6 +1816,24 @@ int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
+ if (list_folders) {
+ if (server.tunnel)
+ ret = list_imap_folders(&server);
+#ifdef USE_CURL_FOR_IMAP_SEND
+ else if (use_curl)
+ ret = curl_list_imap_folders(&server);
+#endif
+ else
+ ret = list_imap_folders(&server);
+ goto out;
+ }
+
+ if (!server.folder) {
+ fprintf(stderr, "no imap store specified\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("could not read from stdin"));
--
2.49.0.824.geaff4db692
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v17 08/10] imap-send: display port alongwith host when git credential is invoked
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-06-09 15:41 ` [PATCH v17 07/10] imap-send: add ability to list the available folders Aditya Garg
@ 2025-06-09 15:41 ` Aditya Garg
2025-06-09 15:41 ` [PATCH v17 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-09 15:41 ` [PATCH v17 10/10] imap-send: fix minor mistakes in the logs Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 15:41 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
FWIW, if no port is specified by the user, the default port, 993 for
IMAPS and 143 for IMAP is used by the code. So, the case of no port
defined for the helper is not possible, and therefore is not added.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 4ac0ba606c..da85a6ee9e 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1089,7 +1089,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0.824.geaff4db692
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v17 09/10] imap-send: display the destination mailbox when sending a message
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-06-09 15:41 ` [PATCH v17 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-06-09 15:41 ` Aditya Garg
2025-06-09 15:41 ` [PATCH v17 10/10] imap-send: fix minor mistakes in the logs Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 15:41 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index da85a6ee9e..7d5df3d049 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1563,7 +1563,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1699,7 +1700,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0.824.geaff4db692
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v17 10/10] imap-send: fix minor mistakes in the logs
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (8 preceding siblings ...)
2025-06-09 15:41 ` [PATCH v17 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-06-09 15:41 ` Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 15:41 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some minor mistakes have been found in the logs. Most of them include
error messages starting with a capital letter, and ending with a period.
Also, abbreviations like "IMAP" and "OK" should be in uppercase. Fix them.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 7d5df3d049..f465a51213 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -211,7 +211,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in\n");
return -1;
}
@@ -1026,7 +1026,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1166,7 +1166,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1185,7 +1185,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1223,7 +1223,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1237,7 +1237,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1249,7 +1249,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1302,12 +1302,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
goto bail;
} else {
- fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ fprintf(stderr, "unknown authentication method:%s\n", srvc->host);
goto bail;
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "skipping account %s@%s, server forbids LOGIN\n",
srvc->user, srvc->host);
goto bail;
}
@@ -1811,7 +1811,7 @@ int cmd_main(int argc, const char **argv)
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "no IMAP host specified\n");
ret = 1;
goto out;
}
@@ -1831,7 +1831,7 @@ int cmd_main(int argc, const char **argv)
}
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "no IMAP store specified\n");
ret = 1;
goto out;
}
@@ -1851,7 +1851,7 @@ int cmd_main(int argc, const char **argv)
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "no messages found to send\n");
ret = 1;
goto out;
}
--
2.49.0.824.geaff4db692
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v16 06/10] imap-send: enable specifying the folder using the command line
2025-06-09 7:20 ` [PATCH v16 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-06-09 18:33 ` Junio C Hamano
0 siblings, 0 replies; 248+ messages in thread
From: Junio C Hamano @ 2025-06-09 18:33 UTC (permalink / raw)
To: Aditya Garg
Cc: git, Eric Sunshine, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> Some users may very often want to imap-send messages to a folder
> other than the default set in the config. Add a command line
> argument for the same.
>
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
> ---
> Documentation/config/imap.adoc | 6 ++++--
> Documentation/git-imap-send.adoc | 15 +++++++++++----
> imap-send.c | 9 ++++++++-
> 3 files changed, 23 insertions(+), 7 deletions(-)
>
> diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
> index 7c8b2dcce4..4682a6bd03 100644
> --- a/Documentation/config/imap.adoc
> +++ b/Documentation/config/imap.adoc
> @@ -1,7 +1,9 @@
> imap.folder::
> The folder to drop the mails into, which is typically the Drafts
> - folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
> - "[Gmail]/Drafts". Required.
> + folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
> + `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
> + the value of this configuration variable is used as the fallback
> + default value when the `--folder` option is not given.
>
> imap.tunnel::
> Command used to set up a tunnel to the IMAP server through which
> diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
> index 8adf0e5aac..4a0487b66e 100644
> --- a/Documentation/git-imap-send.adoc
> +++ b/Documentation/git-imap-send.adoc
> @@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
> SYNOPSIS
> --------
> [verse]
> -'git imap-send' [-v] [-q] [--[no-]curl]
> +'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
This matches the _usage[] string. Excellent.
> DESCRIPTION
> -----------
> -This command uploads a mailbox generated with 'git format-patch'
> +This command uploads a mailbox generated with `git format-patch`
> into an IMAP drafts folder. This allows patches to be sent as
> other email is when using mail clients that cannot read mailbox
> files directly. The command also works with any general mailbox
> -in which emails have the fields "From", "Date", and "Subject" in
> +in which emails have the fields `From`, `Date`, and `Subject` in
> that order.
>
> Typical usage is something like:
>
> -git format-patch --signoff --stdout --attach origin | git imap-send
> +------
> +$ git format-patch --signoff --stdout --attach origin | git imap-send
> +------
The above is small enough that it is OK to make the change
while-at-it, but it deserves a brief mention in the proposed log
message (e.g. "While at it, fix minor mark-up inconsistencies in the
existing documentation text").
> @@ -37,6 +39,11 @@ OPTIONS
> --quiet::
> Be quiet.
>
> +-f <folder>::
> +--folder=<folder>::
> + Specify the folder in which the emails have to saved.
> + For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
Good.
> diff --git a/imap-send.c b/imap-send.c
> index c6e47ddc42..a4cccb9110 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -46,12 +46,14 @@
>
> static int verbosity;
> static int use_curl = USE_CURL_DEFAULT;
> +static char *opt_folder = NULL;
Let's lose "= NULL" here.
Do not explicitly initialize globals to 0 or NULL; let BSS take care
of the zero initialization, instead.
> -static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
> +static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
>
> static struct option imap_send_options[] = {
> OPT__VERBOSITY(&verbosity),
> OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
> + OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
> OPT_END()
> };
>
> @@ -1729,6 +1731,11 @@ int cmd_main(int argc, const char **argv)
>
> argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
>
> + if (opt_folder) {
> + free(server.folder);
> + server.folder = xstrdup(opt_folder);
> + }
Good. This matches the same care taken on the configuration side
that avoids leaking the value previously given.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v16 07/10] imap-send: add ability to list the available folders
2025-06-09 7:20 ` [PATCH v16 07/10] imap-send: add ability to list the available folders Aditya Garg
@ 2025-06-09 18:42 ` Junio C Hamano
0 siblings, 0 replies; 248+ messages in thread
From: Junio C Hamano @ 2025-06-09 18:42 UTC (permalink / raw)
To: Aditya Garg
Cc: git, Eric Sunshine, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> diff --git a/imap-send.c b/imap-send.c
> index a4cccb9110..f03a92a2fb 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -45,15 +45,21 @@
> #endif
>
> static int verbosity;
> +static int list_folders = 0;
Let's lose " = 0" here.
Do not explicitly initialize globals to 0 or NULL; let BSS take care
of the zero initialization, instead.
> -static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
> +static char const * const imap_send_usage[] = {
> + N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
> + "git imap-send --list",
> + NULL
> +};
Good.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v16 08/10] imap-send: display port alongwith host when git credential is invoked
2025-06-09 7:20 ` [PATCH v16 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-06-09 18:55 ` Junio C Hamano
2025-06-09 19:02 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-09 18:55 UTC (permalink / raw)
To: Aditya Garg
Cc: git, Eric Sunshine, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> FWIW, if no port is specified by the user, the default port, 993 for
> IMAPS and 143 for IMAP is used by the code. So, the case of no port
> defined for the helper is not possible, and therefore is not added.
Shouldn't we do a bit better than being so pessimistic?
If the user left the port unspecified, or if the more knowledgeable
user redundantly specified the default port explicitly, showing to
such a user :993 for imaps at the end adds no useful information.
> cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
> - cred->host = xstrdup(srvc->host);
Perhaps something like
if ((srvc->use_ssl ? 993 : 143) == srvc->port)
cred->host = xstrdup(srvc->host);
else
here?
> + cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
>
> cred->username = xstrdup_or_null(srvc->user);
> cred->password = xstrdup_or_null(srvc->pass);
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v16 09/10] imap-send: display the destination mailbox when sending a message
2025-06-09 7:20 ` [PATCH v16 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-06-09 18:57 ` Junio C Hamano
2025-06-09 19:05 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-09 18:57 UTC (permalink / raw)
To: Aditya Garg
Cc: git, Eric Sunshine, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> Whenever we sent a message using the `imap-send` command, it would
> display a log showing the number of messages which are to be sent.
> For example:
>
> sending 1 message
> 100% (1/1) done
>
> This had been made more informative by adding the name of the destination
> folder as well:
>
> Sending 1 message to Drafts folder...
> 100% (1/1) done
>
> Signed-off-by: Aditya Garg <gargaditya08@live.com>
> ---
> imap-send.c | 6 ++++--
> 1 file changed, 4 insertions(+), 2 deletions(-)
Hmph, I have to wonder how much value this adds. It is not like we
extended imap-send to allow it to stuff messages to multiple imap
folders during the same session (in which case, "sending ... to A"
followed by "sending ... to B" may give a good feel of progress).
But that is minor, not an objection strong enough to shoot down a
piece of code that has already been written. Capitalizing "Sending"
certainly is a vast cosmetic improvement ;-).
> diff --git a/imap-send.c b/imap-send.c
> index 9807012169..3d6bcd7e88 100644
> --- a/imap-send.c
> +++ b/imap-send.c
> @@ -1563,7 +1563,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
> }
> ctx->name = server->folder;
>
> - fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
> + fprintf(stderr, "Sending %d message%s to %s folder...\n",
> + total, (total != 1) ? "s" : "", server->folder);
Totally outside the topic, but as #leftoverbits we may want to i18n/l10n
the messages from this program after the dust settles from this series.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v16 08/10] imap-send: display port alongwith host when git credential is invoked
2025-06-09 18:55 ` Junio C Hamano
@ 2025-06-09 19:02 ` Aditya Garg
2025-06-09 20:14 ` Junio C Hamano
0 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 19:02 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
> On 10 Jun 2025, at 12:26 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>> FWIW, if no port is specified by the user, the default port, 993 for
>> IMAPS and 143 for IMAP is used by the code. So, the case of no port
>> defined for the helper is not possible, and therefore is not added.
>
> Shouldn't we do a bit better than being so pessimistic?
>
> If the user left the port unspecified, or if the more knowledgeable
> user redundantly specified the default port explicitly, showing to
> such a user :993 for imaps at the end adds no useful information.
Maybe you misunderstood me? I want to show the port explicitly
just like send-email. I think the FWIW line could be excluded, since
it's more confusing the useful.
>
>> cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
>> - cred->host = xstrdup(srvc->host);
>
> Perhaps something like
>
> if ((srvc->use_ssl ? 993 : 143) == srvc->port)
> cred->host = xstrdup(srvc->host);
> else
>
> here?
That will not show the port if we specify the port as 993 as well then.
>
>> + cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
>
>
>>
>> cred->username = xstrdup_or_null(srvc->user);
>> cred->password = xstrdup_or_null(srvc->pass);
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v16 09/10] imap-send: display the destination mailbox when sending a message
2025-06-09 18:57 ` Junio C Hamano
@ 2025-06-09 19:05 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 19:05 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
> On 10 Jun 2025, at 12:27 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>> Whenever we sent a message using the `imap-send` command, it would
>> display a log showing the number of messages which are to be sent.
>> For example:
>>
>> sending 1 message
>> 100% (1/1) done
>>
>> This had been made more informative by adding the name of the destination
>> folder as well:
>>
>> Sending 1 message to Drafts folder...
>> 100% (1/1) done
>>
>> Signed-off-by: Aditya Garg <gargaditya08@live.com>
>> ---
>> imap-send.c | 6 ++++--
>> 1 file changed, 4 insertions(+), 2 deletions(-)
>
> Hmph, I have to wonder how much value this adds. It is not like we
> extended imap-send to allow it to stuff messages to multiple imap
> folders during the same session (in which case, "sending ... to A"
> followed by "sending ... to B" may give a good feel of progress).
But does give the information in cases with and without the -f argument.
>
> But that is minor, not an objection strong enough to shoot down a
> piece of code that has already been written. Capitalizing "Sending"
> certainly is a vast cosmetic improvement ;-).
Phew ;)
>
>> diff --git a/imap-send.c b/imap-send.c
>> index 9807012169..3d6bcd7e88 100644
>> --- a/imap-send.c
>> +++ b/imap-send.c
>> @@ -1563,7 +1563,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
>> }
>> ctx->name = server->folder;
>>
>> - fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
>> + fprintf(stderr, "Sending %d message%s to %s folder...\n",
>> + total, (total != 1) ? "s" : "", server->folder);
>
> Totally outside the topic, but as #leftoverbits we may want to i18n/l10n
> the messages from this program after the dust settles from this series.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v16 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-09 7:20 ` [PATCH v16 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
@ 2025-06-09 19:15 ` Junio C Hamano
2025-06-09 19:20 ` Aditya Garg
0 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-09 19:15 UTC (permalink / raw)
To: Aditya Garg
Cc: git, Eric Sunshine, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> + if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
> goto bail;
> - }
> } else {
> fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
> goto bail;
Outside the theme of this step (read: I am only leaving a mental
note as potential #leftoverbits; I do not want to see this fixed as
part of this step) and probably outside the theme of this series,
but srvc->host is probably copy-and-paste-bug for srvc->auth_method
I would think.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v16 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-09 19:15 ` Junio C Hamano
@ 2025-06-09 19:20 ` Aditya Garg
0 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 19:20 UTC (permalink / raw)
To: Junio C Hamano
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
> On 10 Jun 2025, at 12:46 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
> Aditya Garg <gargaditya08@live.com> writes:
>
>> + if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
>> goto bail;
>> - }
>> } else {
>> fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
>> goto bail;
>
> Outside the theme of this step (read: I am only leaving a mental
> note as potential #leftoverbits; I do not want to see this fixed as
> part of this step) and probably outside the theme of this series,
> but srvc->host is probably copy-and-paste-bug for srvc->auth_method
> I would think.
Good catch. I can make this minor change a part of v18.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v16 08/10] imap-send: display port alongwith host when git credential is invoked
2025-06-09 19:02 ` Aditya Garg
@ 2025-06-09 20:14 ` Junio C Hamano
0 siblings, 0 replies; 248+ messages in thread
From: Junio C Hamano @ 2025-06-09 20:14 UTC (permalink / raw)
To: Aditya Garg
Cc: git@vger.kernel.org, Eric Sunshine, Zi Yao, brian m carlson,
Jeff King, Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
>> On 10 Jun 2025, at 12:26 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>
>> Aditya Garg <gargaditya08@live.com> writes:
>>
>>> FWIW, if no port is specified by the user, the default port, 993 for
>>> IMAPS and 143 for IMAP is used by the code. So, the case of no port
>>> defined for the helper is not possible, and therefore is not added.
>>
>> Shouldn't we do a bit better than being so pessimistic?
>>
>> If the user left the port unspecified, or if the more knowledgeable
>> user redundantly specified the default port explicitly, showing to
>> such a user :993 for imaps at the end adds no useful information.
>
> Maybe you misunderstood me? I want to show the port explicitly
> just like send-email. I think the FWIW line could be excluded, since
> it's more confusing the useful.
Yeah, I read your FWIW line to be saying "even if we wanted to tell
cases where the user left it unspecified and the user set it to a
value that happens to be the same as the default, we have no way to
tell (unless we add some code to record one more bit, that is), so
we punt and show port regardless."
>> Perhaps something like
>>
>> if ((srvc->use_ssl ? 993 : 143) == srvc->port)
>> cred->host = xstrdup(srvc->host);
>> else
>>
>> here?
>
> That will not show the port if we specify the port as 993 as well then.
Yes, that is exactly what I was saying---if the user set it to the
port that is the default anyway, or more importantly, if the user
did not set, it is unnecessary and/or confusing to start showing the
port number.
If the ISP uses something non-standard and the user explicitly sets
it to that port, it may make sense to show it that the user is using
something non-standard.
Having said that, I do not care too much either way---if we prefer
to always show port, that's fine, but then the FWIW part definitely
needs to be rephrased to explain why it makes sense to show even the
default port.
Thanks.
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (21 preceding siblings ...)
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-09 20:22 ` Aditya Garg
2025-06-09 20:22 ` [PATCH v18 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (9 more replies)
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
23 siblings, 10 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 20:22 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
v8: - Drop the patch that enabled user to choose between libcurl and openssl using the config
- Add ability to list the available folders by adding a `--list` option
v9: - Encourage users to use OAuth2.0 for Gmail (similar change done for send-email docs).
v10: - Fix comment styles
- Fix failing tests
v11: - Use lower case letters for the first word of a sendtence in an error message
and avoid using full stops at the end of a sentence.
v12: - Gracefully exit PLAIN, CRAM-MD5, OAUTHBEARER and XOAUTH2 authentication methods
if OpenSSL support is not compiled in, but is requested by the user.
- Use backticks for string literals.
- Wrap documentation text to 75 columns.
- End the last member of enum CAPABILITY with a trailing comma.
v13: - Fix logic error which was using || instead of && when checking if
the authentication method is neither XOAUTH2 nor OAUTHBEARER.
v14: - Specify why we are not using CURLOPT_PASSWORD for OAuth2.0
methods using a comment.
- Add a function try_auth_method() to reduce code duplication
when trying to authenticate using a specific method.
v15: - Simply rearrange the patches to make the cram md5 patches come
before adding OAuth2.0 and PLAIN authentication methods. No
change has been done to the code itself.
v16: - Rearrage some more patches so that the two new features, i.e.,
--folder and --list come just after the new authentication
methods. Then the two patches with minor improvements of displaying
the destination mailbox and displaying port alongwith host have
been added. The patch fixing other minor mistakes in the logs has
been moved to the end. Just like v15, no change has been done
to the code itself.
v17: - Rebase on top of master where 30325e2 was causing a conflict.
(Sorry for the bad range diff, but I think its easy to understand)
v18: - Avoid initialising variables with 0 or NULL. Let them remain
uninitialised
- Add a white at it note to the commit message of the patch that
adds support to specify the folder.
- Add another minor fix to the log that displays the unknown auth
mechanism used. It was displaying the host rather than the mechanism.
- Remove unecessary and pessimistic lines from the patch that enabled
showing the host alongwith the port.
Aditya Garg (10):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: gracefully fail if CRAM-MD5 authentication is requested
without OpenSSL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: enable specifying the folder using the command line
imap-send: add ability to list the available folders
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
imap-send: fix minor mistakes in the logs
Documentation/config/imap.adoc | 11 +-
Documentation/git-imap-send.adoc | 68 ++++-
imap-send.c | 412 ++++++++++++++++++++++++++-----
3 files changed, 414 insertions(+), 77 deletions(-)
Range-diff against v17:
-: ---------- > 1: 4accbe6ecf imap-send: fix bug causing cfg->folder being set to NULL
-: ---------- > 2: 1cfd66ccea imap-send: fix memory leak in case auth_cram_md5 fails
-: ---------- > 3: 12ff5135be imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
1: 0c6283407c ! 4: 43b18dbfb0 imap-send: enable specifying the folder using the command line
@@ Metadata
Author: Aditya Garg <gargaditya08@live.com>
## Commit message ##
- imap-send: enable specifying the folder using the command line
+ imap-send: add support for OAuth2.0 authentication
- Some users may very often want to imap-send messages to a folder
- other than the default set in the config. Add a command line
- argument for the same.
+ OAuth2.0 is a new way of authentication supported by various email providers
+ these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
+ for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
+ XOAUTH2 is Google's proprietary mechanism (See [3]).
+
+ [1]: https://datatracker.ietf.org/doc/html/rfc5801
+ [2]: https://datatracker.ietf.org/doc/html/rfc7628
+ [3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
## Documentation/config/imap.adoc ##
-@@
- imap.folder::
- The folder to drop the mails into, which is typically the Drafts
-- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
-- "[Gmail]/Drafts". Required.
-+ folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
-+ `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
-+ the value of this configuration variable is used as the fallback
-+ default value when the `--folder` option is not given.
-
- imap.tunnel::
- Command used to set up a tunnel to the IMAP server through which
+@@ Documentation/config/imap.adoc: imap.authMethod::
+ Specify the authentication method for authenticating with the IMAP server.
+ If Git was built with the NO_CURL option, or if your curl version is older
+ than 7.34.0, or if you're running git-imap-send with the `--no-curl`
+- option, the only supported method is 'CRAM-MD5'. If this is not set
+- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
++ option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
++ `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
++ plaintext `LOGIN` command.
## Documentation/git-imap-send.adoc ##
-@@ Documentation/git-imap-send.adoc: git-imap-send - Send a collection of patches from stdin to an IMAP folder
- SYNOPSIS
- --------
- [verse]
--'git imap-send' [-v] [-q] [--[no-]curl]
-+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
-
-
- DESCRIPTION
- -----------
--This command uploads a mailbox generated with 'git format-patch'
-+This command uploads a mailbox generated with `git format-patch`
- into an IMAP drafts folder. This allows patches to be sent as
- other email is when using mail clients that cannot read mailbox
- files directly. The command also works with any general mailbox
--in which emails have the fields "From", "Date", and "Subject" in
-+in which emails have the fields `From`, `Date`, and `Subject` in
- that order.
-
- Typical usage is something like:
-
--git format-patch --signoff --stdout --attach origin | git imap-send
-+------
-+$ git format-patch --signoff --stdout --attach origin | git imap-send
-+------
-
-
- OPTIONS
-@@ Documentation/git-imap-send.adoc: OPTIONS
- --quiet::
- Be quiet.
-
-+-f <folder>::
-+--folder=<folder>::
-+ Specify the folder in which the emails have to saved.
-+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
-+
- --curl::
- Use libcurl to communicate with the IMAP server, unless tunneling
- into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
-
- ## imap-send.c ##
-@@
+@@ Documentation/git-imap-send.adoc: Using Gmail's IMAP interface:
+
+ ---------
+ [imap]
+- folder = "[Gmail]/Drafts"
+- host = imaps://imap.gmail.com
+- user = user@gmail.com
+- port = 993
++ folder = "[Gmail]/Drafts"
++ host = imaps://imap.gmail.com
++ user = user@gmail.com
++ port = 993
+ ---------
+
++Gmail does not allow using your regular password for `git imap-send`.
++If you have multi-factor authentication set up on your Gmail account, you
++can generate an app-specific password for use with `git imap-send`.
++Visit https://security.google.com/settings/security/apppasswords to create
++it. Alternatively, use OAuth2.0 authentication as described below.
++
+ [NOTE]
+ You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
+ that the "Folder doesn't exist".
+@@ Documentation/git-imap-send.adoc: that the "Folder doesn't exist".
+ If your Gmail account is set to another language than English, the name of the "Drafts"
+ folder will be localized.
- static int verbosity;
- static int use_curl = USE_CURL_DEFAULT;
-+static char *opt_folder = NULL;
++If you want to use OAuth2.0 based authentication, you can specify
++`OAUTHBEARER` or `XOAUTH2` mechanism in your config. It is more secure
++than using app-specific passwords, and also does not enforce the need of
++having multi-factor authentication. You will have to use an OAuth2.0
++access token in place of your password when using this authentication.
++
++---------
++[imap]
++ folder = "[Gmail]/Drafts"
++ host = imaps://imap.gmail.com
++ user = user@gmail.com
++ port = 993
++ authmethod = OAUTHBEARER
++---------
++
++Using Outlook's IMAP interface:
++
++Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
++supports only `XOAUTH2` as the mechanism.
++
++---------
++[imap]
++ folder = "Drafts"
++ host = imaps://outlook.office365.com
++ user = user@outlook.com
++ port = 993
++ authmethod = XOAUTH2
++---------
++
+ Once the commits are ready to be sent, run the following command:
--static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
-+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+ $ git format-patch --cover-letter -M --stdout origin/master | git imap-send
+@@ Documentation/git-imap-send.adoc: Just make sure to disable line wrapping in the email client (Gmail's web
+ interface will wrap lines no matter what, so you need to use a real
+ IMAP client).
- static struct option imap_send_options[] = {
- OPT__VERBOSITY(&verbosity),
- OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
-+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
- OPT_END()
++In case you are using OAuth2.0 authentication, it is easier to use credential
++helpers to generate tokens. Credential helpers suggested in
++linkgit:git-send-email[1] can be used for `git imap-send` as well.
++
+ CAUTION
+ -------
+ It is still your responsibility to make sure that the email message
+
+ ## imap-send.c ##
+@@ imap-send.c: enum CAPABILITY {
+ LITERALPLUS,
+ NAMESPACE,
+ STARTTLS,
+- AUTH_CRAM_MD5
++ AUTH_CRAM_MD5,
++ AUTH_OAUTHBEARER,
++ AUTH_XOAUTH2,
+ };
+
+ static const char *cap_list[] = {
+@@ imap-send.c: static const char *cap_list[] = {
+ "NAMESPACE",
+ "STARTTLS",
+ "AUTH=CRAM-MD5",
++ "AUTH=OAUTHBEARER",
++ "AUTH=XOAUTH2",
};
-@@ imap-send.c: int cmd_main(int argc, const char **argv)
+ #define RESP_OK 0
+@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const char *pass)
+ return (char *)response_64;
+ }
- argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
++static char *oauthbearer_base64(const char *user, const char *access_token)
++{
++ int raw_len, b64_len;
++ char *raw, *b64;
++
++ /*
++ * Compose the OAUTHBEARER string
++ *
++ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
++ *
++ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
++ * * gs2-cb-flag `n` -> client does not support CB
++ * * gs2-authzid `a=" {User} "`
++ *
++ * The second part are key value pairs containing host, port and auth as
++ * described in RFC7628.
++ *
++ * https://datatracker.ietf.org/doc/html/rfc5801
++ * https://datatracker.ietf.org/doc/html/rfc7628
++ */
++ raw_len = strlen(user) + strlen(access_token) + 20;
++ raw = xmallocz(raw_len + 1);
++ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
++
++ /* Base64 encode */
++ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
++ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
++ free(raw);
++
++ if (b64_len < 0) {
++ free(b64);
++ return NULL;
++ }
++ return b64;
++}
++
++static char *xoauth2_base64(const char *user, const char *access_token)
++{
++ int raw_len, b64_len;
++ char *raw, *b64;
++
++ /*
++ * Compose the XOAUTH2 string
++ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
++ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
++ */
++ raw_len = strlen(user) + strlen(access_token) + 20;
++ raw = xmallocz(raw_len + 1);
++ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
++
++ /* Base64 encode */
++ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
++ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
++ free(raw);
++
++ if (b64_len < 0) {
++ free(b64);
++ return NULL;
++ }
++ return b64;
++}
++
+ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+ {
+ int ret;
+@@ imap-send.c: static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
+ return 0;
+ }
-+ if (opt_folder) {
-+ free(server.folder);
-+ server.folder = xstrdup(opt_folder);
++static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
++{
++ int ret;
++ char *b64;
++
++ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
++ if (!b64)
++ return error("OAUTHBEARER: base64 encoding failed");
++
++ /* Send the base64-encoded response */
++ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
++ if (ret != (int)strlen(b64)) {
++ free(b64);
++ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
- if (argc)
- usage_with_options(imap_send_usage, imap_send_options);
++ free(b64);
++ return 0;
++}
++
++static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
++{
++ int ret;
++ char *b64;
++
++ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
++ if (!b64)
++ return error("XOAUTH2: base64 encoding failed");
++
++ /* Send the base64-encoded response */
++ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
++ if (ret != (int)strlen(b64)) {
++ free(b64);
++ return error("IMAP error: sending XOAUTH2 response failed");
++ }
++
++ free(b64);
++ return 0;
++}
++
+ #else
+
+ #define auth_cram_md5 NULL
++#define auth_oauthbearer NULL
++#define auth_xoauth2 NULL
+
+ #endif
+
+@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
+ if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
+ goto bail;
++ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
++ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
++ goto bail;
++ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
++ if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
++ goto bail;
+ } else {
+ fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ goto bail;
+@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
+
+ server_fill_credential(srvc, cred);
+ curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
+- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
++
++ /*
++ * Use CURLOPT_PASSWORD irrespective of whether there is
++ * an auth method specified or not, unless it's OAuth2.0,
++ * where we use CURLOPT_XOAUTH2_BEARER.
++ */
++ if (!srvc->auth_method ||
++ (strcmp(srvc->auth_method, "XOAUTH2") &&
++ strcmp(srvc->auth_method, "OAUTHBEARER")))
++ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
+ strbuf_addstr(&path, srvc->host);
+@@ imap-send.c: static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
+ curl_easy_setopt(curl, CURLOPT_PORT, (long)srvc->port);
+
+ if (srvc->auth_method) {
+- struct strbuf auth = STRBUF_INIT;
+- strbuf_addstr(&auth, "AUTH=");
+- strbuf_addstr(&auth, srvc->auth_method);
+- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+- strbuf_release(&auth);
++ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
++ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
++
++ /*
++ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
++ * upon debugging, it has been found that it is capable of detecting
++ * the best option out of OAUTHBEARER and XOAUTH2.
++ */
++ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
++ } else {
++ struct strbuf auth = STRBUF_INIT;
++ strbuf_addstr(&auth, "AUTH=");
++ strbuf_addstr(&auth, srvc->auth_method);
++ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
++ strbuf_release(&auth);
++ }
+ }
+ if (!srvc->use_ssl)
-: ---------- > 5: 1ebf9f935f imap-send: add PLAIN authentication method to OpenSSL
-: ---------- > 6: ce2cfa34cf imap-send: enable specifying the folder using the command line
2: f59cb1dca1 ! 7: 5c36e68493 imap-send: add ability to list the available folders
@@ imap-send.c
#endif
static int verbosity;
-+static int list_folders = 0;
++static int list_folders;
static int use_curl = USE_CURL_DEFAULT;
- static char *opt_folder = NULL;
+ static char *opt_folder;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
3: 1247afbe78 ! 8: cc4f88791f imap-send: display port alongwith host when git credential is invoked
@@ Commit message
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
- FWIW, if no port is specified by the user, the default port, 993 for
- IMAPS and 143 for IMAP is used by the code. So, the case of no port
- defined for the helper is not possible, and therefore is not added.
-
Signed-off-by: Aditya Garg <gargaditya08@live.com>
## imap-send.c ##
4: c30ecbf508 = 9: 82432c7b21 imap-send: display the destination mailbox when sending a message
5: eaff4db692 ! 10: d780afc026 imap-send: fix minor mistakes in the logs
@@ Commit message
Some minor mistakes have been found in the logs. Most of them include
error messages starting with a capital letter, and ending with a period.
- Also, abbreviations like "IMAP" and "OK" should be in uppercase. Fix them.
+ Abbreviations like "IMAP" and "OK" should also be in uppercase. Another
+ mistake was that the error message showing unknown authentication
+ mechanism used was displaying the host rather than the mechanism in the
+ logs. Fix them.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
@@ imap-send.c: static struct imap_store *imap_open_store(struct imap_server_conf *
goto bail;
} else {
- fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
-+ fprintf(stderr, "unknown authentication method:%s\n", srvc->host);
++ fprintf(stderr, "unknown authentication mechanism: %s\n", srvc->auth_method);
goto bail;
}
} else {
--
2.49.0
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v18 01/10] imap-send: fix bug causing cfg->folder being set to NULL
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-09 20:22 ` Aditya Garg
2025-06-09 20:22 ` [PATCH v18 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (8 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 20:22 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 2e812f5a6e..3eed2360fd 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v18 02/10] imap-send: fix memory leak in case auth_cram_md5 fails
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-09 20:22 ` [PATCH v18 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-06-09 20:22 ` Aditya Garg
2025-06-09 20:22 ` [PATCH v18 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
` (7 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 20:22 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 3eed2360fd..cee8f5690d 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -905,8 +905,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v18 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-09 20:22 ` [PATCH v18 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-09 20:22 ` [PATCH v18 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-06-09 20:22 ` Aditya Garg
2025-06-09 20:22 ` [PATCH v18 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (6 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 20:22 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Unlike PLAIN, XOAUTH2 and OAUTHBEARER, CRAM-MD5 authentication is not
supported by libcurl and requires OpenSSL. If the user tries to use
CRAM-MD5 authentication without OpenSSL, the previous behaviour was to
attempt to authenticate and fail with a die(error). Handle this in a
better way by first checking if OpenSSL is available and then attempting
to authenticate. If OpenSSL is not available, print an error message and
exit gracefully.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 66 +++++++++++++++++++++++++++++++----------------------
1 file changed, 39 insertions(+), 27 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index cee8f5690d..39013330a7 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -885,18 +885,6 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
-#else
-
-static char *cram(const char *challenge_64 UNUSED,
- const char *user UNUSED,
- const char *pass UNUSED)
-{
- 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, const char *prompt)
{
int ret;
@@ -915,6 +903,12 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+#else
+
+#define auth_cram_md5 NULL
+
+#endif
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -934,6 +928,38 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
srvc->pass = xstrdup(cred->password);
}
+static int try_auth_method(struct imap_server_conf *srvc,
+ struct imap_store *ctx,
+ struct imap *imap,
+ const char *auth_method,
+ enum CAPABILITY cap,
+ int (*fn)(struct imap_store *, const char *))
+{
+ struct imap_cmd_cb cb = {0};
+
+ if (!CAP(cap)) {
+ fprintf(stderr, "You specified "
+ "%s as authentication method, "
+ "but %s doesn't support it.\n",
+ auth_method, srvc->host);
+ return -1;
+ }
+ cb.cont = fn;
+
+ if (NOT_CONSTANT(!cb.cont)) {
+ fprintf(stderr, "If you want to use %s authentication mechanism, "
+ "you have to build git-imap-send with OpenSSL library.",
+ auth_method);
+ return -1;
+ }
+ if (imap_exec(ctx, &cb, "AUTHENTICATE %s", auth_method) != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE %s failed\n",
+ auth_method);
+ return -1;
+ }
+ return 0;
+}
+
static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const char *folder)
{
struct credential cred = CREDENTIAL_INIT;
@@ -1089,23 +1115,9 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
server_fill_credential(srvc, &cred);
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");
+ if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
- }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v18 04/10] imap-send: add support for OAuth2.0 authentication
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-06-09 20:22 ` [PATCH v18 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
@ 2025-06-09 20:22 ` Aditya Garg
2025-06-17 10:27 ` Phillip Wood
2025-06-09 20:22 ` [PATCH v18 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (5 subsequent siblings)
9 siblings, 1 reply; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 20:22 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 47 +++++++++-
imap-send.c | 148 +++++++++++++++++++++++++++++--
3 files changed, 187 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..29b998d5ff 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
+ `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext `LOGIN` command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..8adf0e5aac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,18 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your regular password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you
+can generate an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create
+it. Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +122,35 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify
+`OAUTHBEARER` or `XOAUTH2` mechanism in your config. It is more secure
+than using app-specific passwords, and also does not enforce the need of
+having multi-factor authentication. You will have to use an OAuth2.0
+access token in place of your password when using this authentication.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +159,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 39013330a7..24eab86a1a 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2,
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,68 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int raw_len, b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw_len = strlen(user) + strlen(access_token) + 20;
+ raw = xmallocz(raw_len + 1);
+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -903,9 +969,51 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
#else
#define auth_cram_md5 NULL
+#define auth_oauthbearer NULL
+#define auth_xoauth2 NULL
#endif
@@ -1118,6 +1226,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
+ goto bail;
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1419,7 +1533,16 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ /*
+ * Use CURLOPT_PASSWORD irrespective of whether there is
+ * an auth method specified or not, unless it's OAuth2.0,
+ * where we use CURLOPT_XOAUTH2_BEARER.
+ */
+ if (!srvc->auth_method ||
+ (strcmp(srvc->auth_method, "XOAUTH2") &&
+ strcmp(srvc->auth_method, "OAUTHBEARER")))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1437,11 +1560,22 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, (long)srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /*
+ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v18 05/10] imap-send: add PLAIN authentication method to OpenSSL
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-06-09 20:22 ` [PATCH v18 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-09 20:22 ` Aditya Garg
2025-06-09 20:22 ` [PATCH v18 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
` (4 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 20:22 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +--
imap-send.c | 63 +++++++++++++++++++++++++++++++++-
2 files changed, 64 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 29b998d5ff..7c8b2dcce4 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
- `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are `PLAIN`, `CRAM-MD5`, `OAUTHBEARER`
+ and `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
plaintext `LOGIN` command.
diff --git a/imap-send.c b/imap-send.c
index 24eab86a1a..64f66ec67d 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2,
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,41 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ int user_len = strlen(user);
+ int pass_len = strlen(pass);
+ int raw_len = 1 + user_len + 1 + pass_len;
+ int b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ raw = xmallocz(raw_len);
+ raw[0] = '\0';
+ memcpy(raw + 1, user, user_len);
+ raw[1 + user_len] = '\0';
+ memcpy(raw + 2 + user_len, pass, pass_len);
+
+ b64 = xmallocz(ENCODED_SIZE(raw_len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -951,6 +988,26 @@ static char *xoauth2_base64(const char *user, const char *access_token)
return b64;
}
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1011,6 +1068,7 @@ static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
#else
+#define auth_plain NULL
#define auth_cram_md5 NULL
#define auth_oauthbearer NULL
#define auth_xoauth2 NULL
@@ -1223,7 +1281,10 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
server_fill_credential(srvc, &cred);
if (srvc->auth_method) {
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (try_auth_method(srvc, ctx, imap, "PLAIN", AUTH_PLAIN, auth_plain))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
} else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v18 06/10] imap-send: enable specifying the folder using the command line
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-06-09 20:22 ` [PATCH v18 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-06-09 20:22 ` Aditya Garg
2025-06-09 20:22 ` [PATCH v18 07/10] imap-send: add ability to list the available folders Aditya Garg
` (3 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 20:22 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
While at it, fix minor mark-up inconsistencies in the existing
documentation text.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 6 ++++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 7c8b2dcce4..4682a6bd03 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,9 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
+ `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
+ the value of this configuration variable is used as the fallback
+ default value when the `--folder` option is not given.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 8adf0e5aac..4a0487b66e 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields `From`, `Date`, and `Subject` in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index 64f66ec67d..be6412fe2d 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1729,6 +1731,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v18 07/10] imap-send: add ability to list the available folders
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-06-09 20:22 ` [PATCH v18 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-06-09 20:22 ` Aditya Garg
2025-06-09 20:22 ` [PATCH v18 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
` (2 subsequent siblings)
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 20:22 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Various IMAP servers have different ways to name common folders.
For example, the folder where all deleted messages are stored is often
named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
on Gmail it is named "[Gmail]/Drafts".
This commit adds a `--list` command to the `imap-send` tool that lists
the available folders on the IMAP server, allowing users to see
which folders are available and how they are named. A sample output
looks like this when run against a Gmail server:
Fetching the list of available folders...
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasChildren \Noselect) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
For OpenSSL, this is achived by running the 'IMAP LIST' command and
parsing the response. This command is specified in RFC6154:
https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
For libcurl, the example code published in the libcurl documentation
is used to implement this functionality:
https://curl.se/libcurl/c/imap-list.html
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/git-imap-send.adoc | 6 +-
imap-send.c | 98 ++++++++++++++++++++++++++------
2 files changed, 87 insertions(+), 17 deletions(-)
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 4a0487b66e..17147f93c3 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+'git imap-send' --list
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
+--list::
+ Run the IMAP LIST command to output a list of all the folders present.
CONFIGURATION
-------------
@@ -123,7 +126,8 @@ it. Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
-that the "Folder doesn't exist".
+that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
+list of available folders.
[NOTE]
If your Gmail account is set to another language than English, the name of the "Drafts"
diff --git a/imap-send.c b/imap-send.c
index be6412fe2d..77cf2b3da2 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -45,15 +45,21 @@
#endif
static int verbosity;
+static int list_folders;
static int use_curl = USE_CURL_DEFAULT;
static char *opt_folder;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
+ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
+ "git imap-send --list",
+ NULL
+};
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
+ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
OPT_END()
};
@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
if (b->buf[b->offset + 1] == '\n') {
b->buf[b->offset] = 0; /* terminate the string */
b->offset += 2; /* next line */
- if (0 < verbosity)
+ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
puts(*s);
return 0;
}
@@ -1579,6 +1585,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
+static int list_imap_folders(struct imap_server_conf *server)
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
+ fprintf(stderr, "failed to connect to IMAP server\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
+ fprintf(stderr, "failed to list folders\n");
+ imap_close_store(ctx);
+ return 1;
+ }
+
+ imap_close_store(ctx);
+ return 0;
+}
+
#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
@@ -1612,11 +1638,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
- die("failed to encode server folder");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+ die("failed to encode server folder");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ -1647,10 +1675,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (long)srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (long)srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
-
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(curl);
@@ -1669,6 +1693,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
struct credential cred = CREDENTIAL_INIT;
curl = setup_curl(server, &cred);
+
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
@@ -1714,6 +1742,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
+
+static int curl_list_imap_folders(struct imap_server_conf *server)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct credential cred = CREDENTIAL_INIT;
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ curl = setup_curl(server, &cred);
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ if (cred.username) {
+ if (res == CURLE_OK)
+ credential_approve(the_repository, &cred);
+ else if (res == CURLE_LOGIN_DENIED)
+ credential_reject(the_repository, &cred);
+ }
+
+ credential_clear(&cred);
+
+ return res != CURLE_OK;
+}
#endif
int cmd_main(int argc, const char **argv)
@@ -1754,11 +1807,6 @@ int cmd_main(int argc, const char **argv)
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "no imap host specified\n");
@@ -1768,6 +1816,24 @@ int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
+ if (list_folders) {
+ if (server.tunnel)
+ ret = list_imap_folders(&server);
+#ifdef USE_CURL_FOR_IMAP_SEND
+ else if (use_curl)
+ ret = curl_list_imap_folders(&server);
+#endif
+ else
+ ret = list_imap_folders(&server);
+ goto out;
+ }
+
+ if (!server.folder) {
+ fprintf(stderr, "no imap store specified\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("could not read from stdin"));
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v18 08/10] imap-send: display port alongwith host when git credential is invoked
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-06-09 20:22 ` [PATCH v18 07/10] imap-send: add ability to list the available folders Aditya Garg
@ 2025-06-09 20:22 ` Aditya Garg
2025-06-09 20:22 ` [PATCH v18 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-09 20:22 ` [PATCH v18 10/10] imap-send: fix minor mistakes in the logs Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 20:22 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 77cf2b3da2..a79e7c7da7 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1089,7 +1089,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v18 09/10] imap-send: display the destination mailbox when sending a message
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-06-09 20:22 ` [PATCH v18 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-06-09 20:22 ` Aditya Garg
2025-06-09 20:22 ` [PATCH v18 10/10] imap-send: fix minor mistakes in the logs Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 20:22 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index a79e7c7da7..fe4e2fbeb8 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1563,7 +1563,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1699,7 +1700,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v18 10/10] imap-send: fix minor mistakes in the logs
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (8 preceding siblings ...)
2025-06-09 20:22 ` [PATCH v18 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-06-09 20:22 ` Aditya Garg
9 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-09 20:22 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some minor mistakes have been found in the logs. Most of them include
error messages starting with a capital letter, and ending with a period.
Abbreviations like "IMAP" and "OK" should also be in uppercase. Another
mistake was that the error message showing unknown authentication
mechanism used was displaying the host rather than the mechanism in the
logs. Fix them.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index fe4e2fbeb8..ed4c34dadd 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -211,7 +211,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in\n");
return -1;
}
@@ -1026,7 +1026,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1166,7 +1166,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1185,7 +1185,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1223,7 +1223,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1237,7 +1237,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1249,7 +1249,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1302,12 +1302,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
goto bail;
} else {
- fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ fprintf(stderr, "unknown authentication mechanism: %s\n", srvc->auth_method);
goto bail;
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "skipping account %s@%s, server forbids LOGIN\n",
srvc->user, srvc->host);
goto bail;
}
@@ -1811,7 +1811,7 @@ int cmd_main(int argc, const char **argv)
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "no IMAP host specified\n");
ret = 1;
goto out;
}
@@ -1831,7 +1831,7 @@ int cmd_main(int argc, const char **argv)
}
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "no IMAP store specified\n");
ret = 1;
goto out;
}
@@ -1851,7 +1851,7 @@ int cmd_main(int argc, const char **argv)
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "no messages found to send\n");
ret = 1;
goto out;
}
--
2.49.0
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v18 04/10] imap-send: add support for OAuth2.0 authentication
2025-06-09 20:22 ` [PATCH v18 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-17 10:27 ` Phillip Wood
2025-06-20 5:16 ` Aditya Garg
2025-06-20 7:00 ` Aditya Garg
0 siblings, 2 replies; 248+ messages in thread
From: Phillip Wood @ 2025-06-17 10:27 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
I'm not really on the list at the moment but I saw this was slated for next in what's cooking. Apologies if the formatting is off, I'm in my phone.
On 9 June 2025 21:22:49 BST, Aditya Garg <gargaditya08@live.com> wrote:
>
>+static char *oauthbearer_base64(const char *user, const char *access_token)
>+{
>+ int raw_len, b64_len;
>+ char *raw, *b64;
>+
>+ /*
>+ * Compose the OAUTHBEARER string
>+ *
>+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
>+ *
>+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
>+ * * gs2-cb-flag `n` -> client does not support CB
>+ * * gs2-authzid `a=" {User} "`
>+ *
>+ * The second part are key value pairs containing host, port and auth as
>+ * described in RFC7628.
>+ *
>+ * https://datatracker.ietf.org/doc/html/rfc5801
>+ * https://datatracker.ietf.org/doc/html/rfc7628
>+ */
>+ raw_len = strlen(user) + strlen(access_token) + 20;
>+ raw = xmallocz(raw_len + 1);
>+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
This looks very fragile. It would be safer to use an strbuf or if there are no embedded nul bytes xstrfmt() and strlen(). This applies to the next patch as well and any others that are building strings with snprintf() or memcpy().
Also the comment above mentions the host and port but I don't see them here.
Thanks
Phillip
>+
>+ /* Base64 encode */
>+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
>+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
>+ free(raw);
>+
>+ if (b64_len < 0) {
>+ free(b64);
>+ return NULL;
>+ }
>+ return b64;
>+}
>+
>+static char *xoauth2_base64(const char *user, const char *access_token)
>+{
>+ int raw_len, b64_len;
>+ char *raw, *b64;
>+
>+ /*
>+ * Compose the XOAUTH2 string
>+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
>+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
>+ */
>+ raw_len = strlen(user) + strlen(access_token) + 20;
>+ raw = xmallocz(raw_len + 1);
>+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
>+
>+ /* Base64 encode */
>+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
>+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
>+ free(raw);
>+
>+ if (b64_len < 0) {
>+ free(b64);
>+ return NULL;
>+ }
>+ return b64;
>+}
>+
> static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
> {
> int ret;
>@@ -903,9 +969,51 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
> return 0;
> }
>
>+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
>+{
>+ int ret;
>+ char *b64;
>+
>+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
>+ if (!b64)
>+ return error("OAUTHBEARER: base64 encoding failed");
>+
>+ /* Send the base64-encoded response */
>+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
>+ if (ret != (int)strlen(b64)) {
>+ free(b64);
>+ return error("IMAP error: sending OAUTHBEARER response failed");
>+ }
>+
>+ free(b64);
>+ return 0;
>+}
>+
>+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
>+{
>+ int ret;
>+ char *b64;
>+
>+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
>+ if (!b64)
>+ return error("XOAUTH2: base64 encoding failed");
>+
>+ /* Send the base64-encoded response */
>+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
>+ if (ret != (int)strlen(b64)) {
>+ free(b64);
>+ return error("IMAP error: sending XOAUTH2 response failed");
>+ }
>+
>+ free(b64);
>+ return 0;
>+}
>+
> #else
>
> #define auth_cram_md5 NULL
>+#define auth_oauthbearer NULL
>+#define auth_xoauth2 NULL
>
> #endif
>
>@@ -1118,6 +1226,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
> if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
> if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
> goto bail;
>+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
>+ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
>+ goto bail;
>+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
>+ if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
>+ goto bail;
> } else {
> fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
> goto bail;
>@@ -1419,7 +1533,16 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
>
> server_fill_credential(srvc, cred);
> curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
>- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>+
>+ /*
>+ * Use CURLOPT_PASSWORD irrespective of whether there is
>+ * an auth method specified or not, unless it's OAuth2.0,
>+ * where we use CURLOPT_XOAUTH2_BEARER.
>+ */
>+ if (!srvc->auth_method ||
>+ (strcmp(srvc->auth_method, "XOAUTH2") &&
>+ strcmp(srvc->auth_method, "OAUTHBEARER")))
>+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
>
> strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
> strbuf_addstr(&path, srvc->host);
>@@ -1437,11 +1560,22 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
> curl_easy_setopt(curl, CURLOPT_PORT, (long)srvc->port);
>
> if (srvc->auth_method) {
>- struct strbuf auth = STRBUF_INIT;
>- strbuf_addstr(&auth, "AUTH=");
>- strbuf_addstr(&auth, srvc->auth_method);
>- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
>- strbuf_release(&auth);
>+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
>+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
>+
>+ /*
>+ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
>+ * upon debugging, it has been found that it is capable of detecting
>+ * the best option out of OAUTHBEARER and XOAUTH2.
>+ */
>+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
>+ } else {
>+ struct strbuf auth = STRBUF_INIT;
>+ strbuf_addstr(&auth, "AUTH=");
>+ strbuf_addstr(&auth, srvc->auth_method);
>+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
>+ strbuf_release(&auth);
>+ }
> }
>
> if (!srvc->use_ssl)
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v18 04/10] imap-send: add support for OAuth2.0 authentication
2025-06-17 10:27 ` Phillip Wood
@ 2025-06-20 5:16 ` Aditya Garg
2025-06-20 7:00 ` Aditya Garg
1 sibling, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 5:16 UTC (permalink / raw)
To: Phillip Wood, Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble
On 17-06-2025 03:57 pm, Phillip Wood wrote:
> I'm not really on the list at the moment but I saw this was slated for next in what's cooking. Apologies if the formatting is off, I'm in my phone.
>
> On 9 June 2025 21:22:49 BST, Aditya Garg <gargaditya08@live.com> wrote:
>>
>> +static char *oauthbearer_base64(const char *user, const char *access_token)
>> +{
>> + int raw_len, b64_len;
>> + char *raw, *b64;
>> +
>> + /*
>> + * Compose the OAUTHBEARER string
>> + *
>> + * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
>> + *
>> + * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
>> + * * gs2-cb-flag `n` -> client does not support CB
>> + * * gs2-authzid `a=" {User} "`
>> + *
>> + * The second part are key value pairs containing host, port and auth as
>> + * described in RFC7628.
>> + *
>> + * https://datatracker.ietf.org/doc/html/rfc5801
>> + * https://datatracker.ietf.org/doc/html/rfc7628
>> + */
>> + raw_len = strlen(user) + strlen(access_token) + 20;
>> + raw = xmallocz(raw_len + 1);
>> + snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
>
> This looks very fragile. It would be safer to use an strbuf or if there are no embedded nul bytes xstrfmt() and strlen(). This applies to the next patch as well and any others that are building strings with snprintf() or memcpy().
Ok
>
> Also the comment above mentions the host and port but I don't see them here.
Host and port are optional. See section 3.1 here:
https://datatracker.ietf.org/doc/html/rfc7628#section-3.1
Also, please add me to the Cc list. I do not read the mailing list quite often, and saw this in what's cooking.
Thanks
Aditya
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (22 preceding siblings ...)
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-20 6:40 ` Aditya Garg
2025-06-20 6:40 ` [PATCH v19 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
` (10 more replies)
23 siblings, 11 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 6:40 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
v2: - Added support for OAuth2.0 with curl.
- Fixed the memory leak in case auth_cram_md5 fails.
v3: - Improve wording in first patch
- Change misleading message if OAuth2.0 is used without OpenSSL
v4: - Add PLAIN authentication mechanism for OpenSSL
- Improved wording in the first patch a bit more
v5: - Add ability to specify destination folder using the command line
- Add ability to set a default between curl and openssl using the config
v6: - Fix minor mistakes in --folder documentation
v7: - Fix spelling and grammar mistakes in logs shown to the user when running imap-send
- Display port alongwith host when git credential is invoked and asks for a password
- Display the destination mailbox when sending a message
v8: - Drop the patch that enabled user to choose between libcurl and openssl using the config
- Add ability to list the available folders by adding a `--list` option
v9: - Encourage users to use OAuth2.0 for Gmail (similar change done for send-email docs).
v10: - Fix comment styles
- Fix failing tests
v11: - Use lower case letters for the first word of a sendtence in an error message
and avoid using full stops at the end of a sentence.
v12: - Gracefully exit PLAIN, CRAM-MD5, OAUTHBEARER and XOAUTH2 authentication methods
if OpenSSL support is not compiled in, but is requested by the user.
- Use backticks for string literals.
- Wrap documentation text to 75 columns.
- End the last member of enum CAPABILITY with a trailing comma.
v13: - Fix logic error which was using || instead of && when checking if
the authentication method is neither XOAUTH2 nor OAUTHBEARER.
v14: - Specify why we are not using CURLOPT_PASSWORD for OAuth2.0
methods using a comment.
- Add a function try_auth_method() to reduce code duplication
when trying to authenticate using a specific method.
v15: - Simply rearrange the patches to make the cram md5 patches come
before adding OAuth2.0 and PLAIN authentication methods. No
change has been done to the code itself.
v16: - Rearrage some more patches so that the two new features, i.e.,
--folder and --list come just after the new authentication
methods. Then the two patches with minor improvements of displaying
the destination mailbox and displaying port alongwith host have
been added. The patch fixing other minor mistakes in the logs has
been moved to the end. Just like v15, no change has been done
to the code itself.
v17: - Rebase on top of master where 30325e2 was causing a conflict.
(Sorry for the bad range diff, but I think its easy to understand)
v18: - Avoid initialising variables with 0 or NULL. Let them remain
uninitialised
- Add a white at it note to the commit message of the patch that
adds support to specify the folder.
- Add another minor fix to the log that displays the unknown auth
mechanism used. It was displaying the host rather than the mechanism.
- Remove unecessary and pessimistic lines from the patch that enabled
showing the host alongwith the port.
v19: - Use xstrfmt() for OAuth2 strings and strbuf for PLAIN.
Aditya Garg (10):
imap-send: fix bug causing cfg->folder being set to NULL
imap-send: fix memory leak in case auth_cram_md5 fails
imap-send: gracefully fail if CRAM-MD5 authentication is requested
without OpenSSL
imap-send: add support for OAuth2.0 authentication
imap-send: add PLAIN authentication method to OpenSSL
imap-send: enable specifying the folder using the command line
imap-send: add ability to list the available folders
imap-send: display port alongwith host when git credential is invoked
imap-send: display the destination mailbox when sending a message
imap-send: fix minor mistakes in the logs
Documentation/config/imap.adoc | 11 +-
Documentation/git-imap-send.adoc | 68 +++++-
imap-send.c | 405 ++++++++++++++++++++++++++-----
3 files changed, 407 insertions(+), 77 deletions(-)
Range-diff against v18:
-: ---------- > 1: 4accbe6ecf imap-send: fix bug causing cfg->folder being set to NULL
-: ---------- > 2: 1cfd66ccea imap-send: fix memory leak in case auth_cram_md5 fails
-: ---------- > 3: 12ff5135be imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
1: 43b18dbfb0 ! 4: 6461607abc imap-send: add support for OAuth2.0 authentication
@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
-+ int raw_len, b64_len;
++ int b64_len;
+ char *raw, *b64;
+
+ /*
@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
-+ raw_len = strlen(user) + strlen(access_token) + 20;
-+ raw = xmallocz(raw_len + 1);
-+ snprintf(raw, raw_len + 1, "n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
++ raw = xstrfmt("n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
-+ int raw_len, b64_len;
++ int b64_len;
+ char *raw, *b64;
+
+ /*
@@ imap-send.c: static char *cram(const char *challenge_64, const char *user, const
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
-+ raw_len = strlen(user) + strlen(access_token) + 20;
-+ raw = xmallocz(raw_len + 1);
-+ snprintf(raw, raw_len + 1, "user=%s\001auth=Bearer %s\001\001", user, access_token);
++ raw = xstrfmt("user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
2: 1ebf9f935f ! 5: 76745861e8 imap-send: add PLAIN authentication method to OpenSSL
@@ imap-send.c: static char hexchar(unsigned int b)
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
-+ int user_len = strlen(user);
-+ int pass_len = strlen(pass);
-+ int raw_len = 1 + user_len + 1 + pass_len;
++ struct strbuf raw = STRBUF_INIT;
+ int b64_len;
-+ char *raw, *b64;
++ char *b64;
+
+ /*
+ * Compose the PLAIN string
@@ imap-send.c: static char hexchar(unsigned int b)
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
-+ raw = xmallocz(raw_len);
-+ raw[0] = '\0';
-+ memcpy(raw + 1, user, user_len);
-+ raw[1 + user_len] = '\0';
-+ memcpy(raw + 2 + user_len, pass, pass_len);
++ strbuf_addch(&raw, '\0');
++ strbuf_addstr(&raw, user);
++ strbuf_addch(&raw, '\0');
++ strbuf_addstr(&raw, pass);
+
-+ b64 = xmallocz(ENCODED_SIZE(raw_len));
-+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, raw_len);
-+ free(raw);
++ b64 = xmallocz(ENCODED_SIZE(raw.len));
++ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw.buf, raw.len);
++ strbuf_release(&raw);
+
+ if (b64_len < 0) {
+ free(b64);
3: ce2cfa34cf = 6: cb0857e36e imap-send: enable specifying the folder using the command line
4: 5c36e68493 = 7: 360aa72808 imap-send: add ability to list the available folders
5: cc4f88791f = 8: 422db5f0f0 imap-send: display port alongwith host when git credential is invoked
6: 82432c7b21 = 9: eaef39e6f1 imap-send: display the destination mailbox when sending a message
7: d780afc026 = 10: cc76007b2f imap-send: fix minor mistakes in the logs
--
2.49.0.824.gcc76007b2f
^ permalink raw reply [flat|nested] 248+ messages in thread
* [PATCH v19 01/10] imap-send: fix bug causing cfg->folder being set to NULL
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
@ 2025-06-20 6:40 ` Aditya Garg
2025-06-20 6:40 ` [PATCH v19 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
` (9 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 6:40 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
6d1f198f34 (imap-send: fix leaking memory in `imap_server_conf`, 2024-06-07)
resulted a change in static int git_imap_config which resulted in cfg->folder
being incorrectly set to NULL in case imap.user, imap.pass, imap.tunnel and
imap.authmethod were defined. Because of this, since Git 2.46.0,
git-imap-send is not usable at all. The bug seems to have been unnoticed for
a long time, likely due to better options like git-send-email.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 2e812f5a6e..3eed2360fd 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1316,16 +1316,16 @@ static int git_imap_config(const char *var, const char *val,
FREE_AND_NULL(cfg->folder);
return git_config_string(&cfg->folder, var, val);
} else if (!strcmp("imap.user", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->user);
return git_config_string(&cfg->user, var, val);
} else if (!strcmp("imap.pass", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->pass);
return git_config_string(&cfg->pass, var, val);
} else if (!strcmp("imap.tunnel", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->tunnel);
return git_config_string(&cfg->tunnel, var, val);
} else if (!strcmp("imap.authmethod", var)) {
- FREE_AND_NULL(cfg->folder);
+ FREE_AND_NULL(cfg->auth_method);
return git_config_string(&cfg->auth_method, var, val);
} else if (!strcmp("imap.port", var)) {
cfg->port = git_config_int(var, val, ctx->kvi);
--
2.49.0.824.gcc76007b2f
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v19 02/10] imap-send: fix memory leak in case auth_cram_md5 fails
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-20 6:40 ` [PATCH v19 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
@ 2025-06-20 6:40 ` Aditya Garg
2025-06-20 6:40 ` [PATCH v19 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
` (8 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 6:40 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
This patch fixes a memory leak by running free(response) in case
auth_cram_md5 fails.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index 3eed2360fd..cee8f5690d 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -905,8 +905,10 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
response = cram(prompt, ctx->cfg->user, ctx->cfg->pass);
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
- if (ret != strlen(response))
+ if (ret != strlen(response)) {
+ free(response);
return error("IMAP error: sending response failed");
+ }
free(response);
--
2.49.0.824.gcc76007b2f
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v19 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-20 6:40 ` [PATCH v19 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-20 6:40 ` [PATCH v19 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
@ 2025-06-20 6:40 ` Aditya Garg
2025-06-20 6:40 ` [PATCH v19 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
` (7 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 6:40 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Unlike PLAIN, XOAUTH2 and OAUTHBEARER, CRAM-MD5 authentication is not
supported by libcurl and requires OpenSSL. If the user tries to use
CRAM-MD5 authentication without OpenSSL, the previous behaviour was to
attempt to authenticate and fail with a die(error). Handle this in a
better way by first checking if OpenSSL is available and then attempting
to authenticate. If OpenSSL is not available, print an error message and
exit gracefully.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 66 +++++++++++++++++++++++++++++++----------------------
1 file changed, 39 insertions(+), 27 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index cee8f5690d..39013330a7 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -885,18 +885,6 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
-#else
-
-static char *cram(const char *challenge_64 UNUSED,
- const char *user UNUSED,
- const char *pass UNUSED)
-{
- 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, const char *prompt)
{
int ret;
@@ -915,6 +903,12 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+#else
+
+#define auth_cram_md5 NULL
+
+#endif
+
static void server_fill_credential(struct imap_server_conf *srvc, struct credential *cred)
{
if (srvc->user && srvc->pass)
@@ -934,6 +928,38 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
srvc->pass = xstrdup(cred->password);
}
+static int try_auth_method(struct imap_server_conf *srvc,
+ struct imap_store *ctx,
+ struct imap *imap,
+ const char *auth_method,
+ enum CAPABILITY cap,
+ int (*fn)(struct imap_store *, const char *))
+{
+ struct imap_cmd_cb cb = {0};
+
+ if (!CAP(cap)) {
+ fprintf(stderr, "You specified "
+ "%s as authentication method, "
+ "but %s doesn't support it.\n",
+ auth_method, srvc->host);
+ return -1;
+ }
+ cb.cont = fn;
+
+ if (NOT_CONSTANT(!cb.cont)) {
+ fprintf(stderr, "If you want to use %s authentication mechanism, "
+ "you have to build git-imap-send with OpenSSL library.",
+ auth_method);
+ return -1;
+ }
+ if (imap_exec(ctx, &cb, "AUTHENTICATE %s", auth_method) != RESP_OK) {
+ fprintf(stderr, "IMAP error: AUTHENTICATE %s failed\n",
+ auth_method);
+ return -1;
+ }
+ return 0;
+}
+
static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const char *folder)
{
struct credential cred = CREDENTIAL_INIT;
@@ -1089,23 +1115,9 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
server_fill_credential(srvc, &cred);
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");
+ if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
- }
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
--
2.49.0.824.gcc76007b2f
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v19 04/10] imap-send: add support for OAuth2.0 authentication
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (2 preceding siblings ...)
2025-06-20 6:40 ` [PATCH v19 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
@ 2025-06-20 6:40 ` Aditya Garg
2025-06-20 6:40 ` [PATCH v19 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
` (6 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 6:40 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
OAuth2.0 is a new way of authentication supported by various email providers
these days. OAUTHBEARER and XOAUTH2 are the two most common mechanisms used
for OAuth2.0. OAUTHBEARER is described in RFC5801[1] and RFC7628[2], whereas
XOAUTH2 is Google's proprietary mechanism (See [3]).
[1]: https://datatracker.ietf.org/doc/html/rfc5801
[2]: https://datatracker.ietf.org/doc/html/rfc7628
[3]: https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 5 +-
Documentation/git-imap-send.adoc | 47 +++++++++-
imap-send.c | 144 +++++++++++++++++++++++++++++--
3 files changed, 183 insertions(+), 13 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 3d28f72643..29b998d5ff 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,5 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported method is 'CRAM-MD5'. If this is not set
- then 'git imap-send' uses the basic IMAP plaintext LOGIN command.
+ option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
+ `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ plaintext `LOGIN` command.
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 26ccf4e433..8adf0e5aac 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -102,12 +102,18 @@ Using Gmail's IMAP interface:
---------
[imap]
- folder = "[Gmail]/Drafts"
- host = imaps://imap.gmail.com
- user = user@gmail.com
- port = 993
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
---------
+Gmail does not allow using your regular password for `git imap-send`.
+If you have multi-factor authentication set up on your Gmail account, you
+can generate an app-specific password for use with `git imap-send`.
+Visit https://security.google.com/settings/security/apppasswords to create
+it. Alternatively, use OAuth2.0 authentication as described below.
+
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
that the "Folder doesn't exist".
@@ -116,6 +122,35 @@ that the "Folder doesn't exist".
If your Gmail account is set to another language than English, the name of the "Drafts"
folder will be localized.
+If you want to use OAuth2.0 based authentication, you can specify
+`OAUTHBEARER` or `XOAUTH2` mechanism in your config. It is more secure
+than using app-specific passwords, and also does not enforce the need of
+having multi-factor authentication. You will have to use an OAuth2.0
+access token in place of your password when using this authentication.
+
+---------
+[imap]
+ folder = "[Gmail]/Drafts"
+ host = imaps://imap.gmail.com
+ user = user@gmail.com
+ port = 993
+ authmethod = OAUTHBEARER
+---------
+
+Using Outlook's IMAP interface:
+
+Unlike Gmail, Outlook only supports OAuth2.0 based authentication. Also, it
+supports only `XOAUTH2` as the mechanism.
+
+---------
+[imap]
+ folder = "Drafts"
+ host = imaps://outlook.office365.com
+ user = user@outlook.com
+ port = 993
+ authmethod = XOAUTH2
+---------
+
Once the commits are ready to be sent, run the following command:
$ git format-patch --cover-letter -M --stdout origin/master | git imap-send
@@ -124,6 +159,10 @@ Just make sure to disable line wrapping in the email client (Gmail's web
interface will wrap lines no matter what, so you need to use a real
IMAP client).
+In case you are using OAuth2.0 authentication, it is easier to use credential
+helpers to generate tokens. Credential helpers suggested in
+linkgit:git-send-email[1] can be used for `git imap-send` as well.
+
CAUTION
-------
It is still your responsibility to make sure that the email message
diff --git a/imap-send.c b/imap-send.c
index 39013330a7..5a83ea80e1 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,7 +139,9 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
- AUTH_CRAM_MD5
+ AUTH_CRAM_MD5,
+ AUTH_OAUTHBEARER,
+ AUTH_XOAUTH2,
};
static const char *cap_list[] = {
@@ -149,6 +151,8 @@ static const char *cap_list[] = {
"NAMESPACE",
"STARTTLS",
"AUTH=CRAM-MD5",
+ "AUTH=OAUTHBEARER",
+ "AUTH=XOAUTH2",
};
#define RESP_OK 0
@@ -885,6 +889,64 @@ static char *cram(const char *challenge_64, const char *user, const char *pass)
return (char *)response_64;
}
+static char *oauthbearer_base64(const char *user, const char *access_token)
+{
+ int b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the OAUTHBEARER string
+ *
+ * "n,a=" {User} ",^Ahost=" {Host} "^Aport=" {Port} "^Aauth=Bearer " {Access Token} "^A^A
+ *
+ * The first part `n,a=" {User} ",` is the gs2 header described in RFC5801.
+ * * gs2-cb-flag `n` -> client does not support CB
+ * * gs2-authzid `a=" {User} "`
+ *
+ * The second part are key value pairs containing host, port and auth as
+ * described in RFC7628.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc5801
+ * https://datatracker.ietf.org/doc/html/rfc7628
+ */
+ raw = xstrfmt("n,a=%s,\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
+static char *xoauth2_base64(const char *user, const char *access_token)
+{
+ int b64_len;
+ char *raw, *b64;
+
+ /*
+ * Compose the XOAUTH2 string
+ * "user=" {User} "^Aauth=Bearer " {Access Token} "^A^A"
+ * https://developers.google.com/workspace/gmail/imap/xoauth2-protocol#initial_client_response
+ */
+ raw = xstrfmt("user=%s\001auth=Bearer %s\001\001", user, access_token);
+
+ /* Base64 encode */
+ b64 = xmallocz(ENCODED_SIZE(strlen(raw)));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw, strlen(raw));
+ free(raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -903,9 +965,51 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
return 0;
}
+static int auth_oauthbearer(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = oauthbearer_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("OAUTHBEARER: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending OAUTHBEARER response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
+static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = xoauth2_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("XOAUTH2: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending XOAUTH2 response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
#else
#define auth_cram_md5 NULL
+#define auth_oauthbearer NULL
+#define auth_xoauth2 NULL
#endif
@@ -1118,6 +1222,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
+ } else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
+ if (try_auth_method(srvc, ctx, imap, "OAUTHBEARER", AUTH_OAUTHBEARER, auth_oauthbearer))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "XOAUTH2")) {
+ if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
+ goto bail;
} else {
fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
goto bail;
@@ -1419,7 +1529,16 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
server_fill_credential(srvc, cred);
curl_easy_setopt(curl, CURLOPT_USERNAME, srvc->user);
- curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
+
+ /*
+ * Use CURLOPT_PASSWORD irrespective of whether there is
+ * an auth method specified or not, unless it's OAuth2.0,
+ * where we use CURLOPT_XOAUTH2_BEARER.
+ */
+ if (!srvc->auth_method ||
+ (strcmp(srvc->auth_method, "XOAUTH2") &&
+ strcmp(srvc->auth_method, "OAUTHBEARER")))
+ curl_easy_setopt(curl, CURLOPT_PASSWORD, srvc->pass);
strbuf_addstr(&path, srvc->use_ssl ? "imaps://" : "imap://");
strbuf_addstr(&path, srvc->host);
@@ -1437,11 +1556,22 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_PORT, (long)srvc->port);
if (srvc->auth_method) {
- struct strbuf auth = STRBUF_INIT;
- strbuf_addstr(&auth, "AUTH=");
- strbuf_addstr(&auth, srvc->auth_method);
- curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
- strbuf_release(&auth);
+ if (!strcmp(srvc->auth_method, "XOAUTH2") ||
+ !strcmp(srvc->auth_method, "OAUTHBEARER")) {
+
+ /*
+ * While CURLOPT_XOAUTH2_BEARER looks as if it only supports XOAUTH2,
+ * upon debugging, it has been found that it is capable of detecting
+ * the best option out of OAUTHBEARER and XOAUTH2.
+ */
+ curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, srvc->pass);
+ } else {
+ struct strbuf auth = STRBUF_INIT;
+ strbuf_addstr(&auth, "AUTH=");
+ strbuf_addstr(&auth, srvc->auth_method);
+ curl_easy_setopt(curl, CURLOPT_LOGIN_OPTIONS, auth.buf);
+ strbuf_release(&auth);
+ }
}
if (!srvc->use_ssl)
--
2.49.0.824.gcc76007b2f
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v19 05/10] imap-send: add PLAIN authentication method to OpenSSL
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (3 preceding siblings ...)
2025-06-20 6:40 ` [PATCH v19 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
@ 2025-06-20 6:40 ` Aditya Garg
2025-06-20 6:40 ` [PATCH v19 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
` (5 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 6:40 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
The current implementation for PLAIN in imap-send works just fine
if using curl, but if attempted to use for OpenSSL, it is treated
as an invalid mechanism. The default implementation for OpenSSL is
IMAP LOGIN command rather than AUTH PLAIN. Since AUTH PLAIN is
still used today by many email providers in form of app passwords,
lets add an implementation that can use AUTH PLAIN if specified.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 4 +--
imap-send.c | 60 +++++++++++++++++++++++++++++++++-
2 files changed, 61 insertions(+), 3 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 29b998d5ff..7c8b2dcce4 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -40,6 +40,6 @@ imap.authMethod::
Specify the authentication method for authenticating with the IMAP server.
If Git was built with the NO_CURL option, or if your curl version is older
than 7.34.0, or if you're running git-imap-send with the `--no-curl`
- option, the only supported methods are `CRAM-MD5`, `OAUTHBEARER` and
- `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
+ option, the only supported methods are `PLAIN`, `CRAM-MD5`, `OAUTHBEARER`
+ and `XOAUTH2`. If this is not set then `git imap-send` uses the basic IMAP
plaintext `LOGIN` command.
diff --git a/imap-send.c b/imap-send.c
index 5a83ea80e1..f3ba5eeb5b 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -139,6 +139,7 @@ enum CAPABILITY {
LITERALPLUS,
NAMESPACE,
STARTTLS,
+ AUTH_PLAIN,
AUTH_CRAM_MD5,
AUTH_OAUTHBEARER,
AUTH_XOAUTH2,
@@ -150,6 +151,7 @@ static const char *cap_list[] = {
"LITERAL+",
"NAMESPACE",
"STARTTLS",
+ "AUTH=PLAIN",
"AUTH=CRAM-MD5",
"AUTH=OAUTHBEARER",
"AUTH=XOAUTH2",
@@ -851,6 +853,38 @@ static char hexchar(unsigned int b)
}
#define ENCODED_SIZE(n) (4 * DIV_ROUND_UP((n), 3))
+static char *plain_base64(const char *user, const char *pass)
+{
+ struct strbuf raw = STRBUF_INIT;
+ int b64_len;
+ char *b64;
+
+ /*
+ * Compose the PLAIN string
+ *
+ * The username and password are combined to one string and base64 encoded.
+ * "\0user\0pass"
+ *
+ * The method has been described in RFC4616.
+ *
+ * https://datatracker.ietf.org/doc/html/rfc4616
+ */
+ strbuf_addch(&raw, '\0');
+ strbuf_addstr(&raw, user);
+ strbuf_addch(&raw, '\0');
+ strbuf_addstr(&raw, pass);
+
+ b64 = xmallocz(ENCODED_SIZE(raw.len));
+ b64_len = EVP_EncodeBlock((unsigned char *)b64, (unsigned char *)raw.buf, raw.len);
+ strbuf_release(&raw);
+
+ if (b64_len < 0) {
+ free(b64);
+ return NULL;
+ }
+ return b64;
+}
+
static char *cram(const char *challenge_64, const char *user, const char *pass)
{
int i, resp_len, encoded_len, decoded_len;
@@ -947,6 +981,26 @@ static char *xoauth2_base64(const char *user, const char *access_token)
return b64;
}
+static int auth_plain(struct imap_store *ctx, const char *prompt UNUSED)
+{
+ int ret;
+ char *b64;
+
+ b64 = plain_base64(ctx->cfg->user, ctx->cfg->pass);
+ if (!b64)
+ return error("PLAIN: base64 encoding failed");
+
+ /* Send the base64-encoded response */
+ ret = socket_write(&ctx->imap->buf.sock, b64, strlen(b64));
+ if (ret != (int)strlen(b64)) {
+ free(b64);
+ return error("IMAP error: sending PLAIN response failed");
+ }
+
+ free(b64);
+ return 0;
+}
+
static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
{
int ret;
@@ -1007,6 +1061,7 @@ static int auth_xoauth2(struct imap_store *ctx, const char *prompt UNUSED)
#else
+#define auth_plain NULL
#define auth_cram_md5 NULL
#define auth_oauthbearer NULL
#define auth_xoauth2 NULL
@@ -1219,7 +1274,10 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
server_fill_credential(srvc, &cred);
if (srvc->auth_method) {
- if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
+ if (!strcmp(srvc->auth_method, "PLAIN")) {
+ if (try_auth_method(srvc, ctx, imap, "PLAIN", AUTH_PLAIN, auth_plain))
+ goto bail;
+ } else if (!strcmp(srvc->auth_method, "CRAM-MD5")) {
if (try_auth_method(srvc, ctx, imap, "CRAM-MD5", AUTH_CRAM_MD5, auth_cram_md5))
goto bail;
} else if (!strcmp(srvc->auth_method, "OAUTHBEARER")) {
--
2.49.0.824.gcc76007b2f
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v19 06/10] imap-send: enable specifying the folder using the command line
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (4 preceding siblings ...)
2025-06-20 6:40 ` [PATCH v19 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
@ 2025-06-20 6:40 ` Aditya Garg
2025-06-20 6:40 ` [PATCH v19 07/10] imap-send: add ability to list the available folders Aditya Garg
` (4 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 6:40 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some users may very often want to imap-send messages to a folder
other than the default set in the config. Add a command line
argument for the same.
While at it, fix minor mark-up inconsistencies in the existing
documentation text.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/config/imap.adoc | 6 ++++--
Documentation/git-imap-send.adoc | 15 +++++++++++----
imap-send.c | 9 ++++++++-
3 files changed, 23 insertions(+), 7 deletions(-)
diff --git a/Documentation/config/imap.adoc b/Documentation/config/imap.adoc
index 7c8b2dcce4..4682a6bd03 100644
--- a/Documentation/config/imap.adoc
+++ b/Documentation/config/imap.adoc
@@ -1,7 +1,9 @@
imap.folder::
The folder to drop the mails into, which is typically the Drafts
- folder. For example: "INBOX.Drafts", "INBOX/Drafts" or
- "[Gmail]/Drafts". Required.
+ folder. For example: `INBOX.Drafts`, `INBOX/Drafts` or
+ `[Gmail]/Drafts`. The IMAP folder to interact with MUST be specified;
+ the value of this configuration variable is used as the fallback
+ default value when the `--folder` option is not given.
imap.tunnel::
Command used to set up a tunnel to the IMAP server through which
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 8adf0e5aac..4a0487b66e 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -9,21 +9,23 @@ git-imap-send - Send a collection of patches from stdin to an IMAP folder
SYNOPSIS
--------
[verse]
-'git imap-send' [-v] [-q] [--[no-]curl]
+'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
DESCRIPTION
-----------
-This command uploads a mailbox generated with 'git format-patch'
+This command uploads a mailbox generated with `git format-patch`
into an IMAP drafts folder. This allows patches to be sent as
other email is when using mail clients that cannot read mailbox
files directly. The command also works with any general mailbox
-in which emails have the fields "From", "Date", and "Subject" in
+in which emails have the fields `From`, `Date`, and `Subject` in
that order.
Typical usage is something like:
-git format-patch --signoff --stdout --attach origin | git imap-send
+------
+$ git format-patch --signoff --stdout --attach origin | git imap-send
+------
OPTIONS
@@ -37,6 +39,11 @@ OPTIONS
--quiet::
Be quiet.
+-f <folder>::
+--folder=<folder>::
+ Specify the folder in which the emails have to saved.
+ For example: `--folder=[Gmail]/Drafts` or `-f INBOX/Drafts`.
+
--curl::
Use libcurl to communicate with the IMAP server, unless tunneling
into it. Ignored if Git was built without the USE_CURL_FOR_IMAP_SEND
diff --git a/imap-send.c b/imap-send.c
index f3ba5eeb5b..7e021c8392 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -46,12 +46,14 @@
static int verbosity;
static int use_curl = USE_CURL_DEFAULT;
+static char *opt_folder;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] < <mbox>", NULL };
+static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
+ OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
OPT_END()
};
@@ -1722,6 +1724,11 @@ int cmd_main(int argc, const char **argv)
argc = parse_options(argc, (const char **)argv, "", imap_send_options, imap_send_usage, 0);
+ if (opt_folder) {
+ free(server.folder);
+ server.folder = xstrdup(opt_folder);
+ }
+
if (argc)
usage_with_options(imap_send_usage, imap_send_options);
--
2.49.0.824.gcc76007b2f
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v19 07/10] imap-send: add ability to list the available folders
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (5 preceding siblings ...)
2025-06-20 6:40 ` [PATCH v19 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
@ 2025-06-20 6:40 ` Aditya Garg
2025-06-20 6:40 ` [PATCH v19 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
` (3 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 6:40 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Various IMAP servers have different ways to name common folders.
For example, the folder where all deleted messages are stored is often
named "[Gmail]/Trash" on Gmail servers, and "Deleted" on Outlook.
Similarly, the Drafts folder is simply named "Drafts" on Outlook, but
on Gmail it is named "[Gmail]/Drafts".
This commit adds a `--list` command to the `imap-send` tool that lists
the available folders on the IMAP server, allowing users to see
which folders are available and how they are named. A sample output
looks like this when run against a Gmail server:
Fetching the list of available folders...
* LIST (\HasNoChildren) "/" "INBOX"
* LIST (\HasChildren \Noselect) "/" "[Gmail]"
* LIST (\All \HasNoChildren) "/" "[Gmail]/All Mail"
* LIST (\Drafts \HasNoChildren) "/" "[Gmail]/Drafts"
* LIST (\HasNoChildren \Important) "/" "[Gmail]/Important"
* LIST (\HasNoChildren \Sent) "/" "[Gmail]/Sent Mail"
* LIST (\HasNoChildren \Junk) "/" "[Gmail]/Spam"
* LIST (\Flagged \HasNoChildren) "/" "[Gmail]/Starred"
* LIST (\HasNoChildren \Trash) "/" "[Gmail]/Trash"
For OpenSSL, this is achived by running the 'IMAP LIST' command and
parsing the response. This command is specified in RFC6154:
https://datatracker.ietf.org/doc/html/rfc6154#section-5.1
For libcurl, the example code published in the libcurl documentation
is used to implement this functionality:
https://curl.se/libcurl/c/imap-list.html
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
Documentation/git-imap-send.adoc | 6 +-
imap-send.c | 98 ++++++++++++++++++++++++++------
2 files changed, 87 insertions(+), 17 deletions(-)
diff --git a/Documentation/git-imap-send.adoc b/Documentation/git-imap-send.adoc
index 4a0487b66e..17147f93c3 100644
--- a/Documentation/git-imap-send.adoc
+++ b/Documentation/git-imap-send.adoc
@@ -10,6 +10,7 @@ SYNOPSIS
--------
[verse]
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
+'git imap-send' --list
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
using libcurl. Ignored if Git was built with the NO_OPENSSL option
set.
+--list::
+ Run the IMAP LIST command to output a list of all the folders present.
CONFIGURATION
-------------
@@ -123,7 +126,8 @@ it. Alternatively, use OAuth2.0 authentication as described below.
[NOTE]
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
-that the "Folder doesn't exist".
+that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
+list of available folders.
[NOTE]
If your Gmail account is set to another language than English, the name of the "Drafts"
diff --git a/imap-send.c b/imap-send.c
index 7e021c8392..b1dddaff3e 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -45,15 +45,21 @@
#endif
static int verbosity;
+static int list_folders;
static int use_curl = USE_CURL_DEFAULT;
static char *opt_folder;
-static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
+static char const * const imap_send_usage[] = {
+ N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
+ "git imap-send --list",
+ NULL
+};
static struct option imap_send_options[] = {
OPT__VERBOSITY(&verbosity),
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
+ OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
OPT_END()
};
@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
if (b->buf[b->offset + 1] == '\n') {
b->buf[b->offset] = 0; /* terminate the string */
b->offset += 2; /* next line */
- if (0 < verbosity)
+ if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
puts(*s);
return 0;
}
@@ -1572,6 +1578,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
return 0;
}
+static int list_imap_folders(struct imap_server_conf *server)
+{
+ struct imap_store *ctx = imap_open_store(server, "INBOX");
+ if (!ctx) {
+ fprintf(stderr, "failed to connect to IMAP server\n");
+ return 1;
+ }
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ /* Issue the LIST command and print the results */
+ if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
+ fprintf(stderr, "failed to list folders\n");
+ imap_close_store(ctx);
+ return 1;
+ }
+
+ imap_close_store(ctx);
+ return 0;
+}
+
#ifdef USE_CURL_FOR_IMAP_SEND
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
{
@@ -1605,11 +1631,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
if (!path.len || path.buf[path.len - 1] != '/')
strbuf_addch(&path, '/');
- uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
- if (!uri_encoded_folder)
- die("failed to encode server folder");
- strbuf_addstr(&path, uri_encoded_folder);
- curl_free(uri_encoded_folder);
+ if (!list_folders) {
+ uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
+ if (!uri_encoded_folder)
+ die("failed to encode server folder");
+ strbuf_addstr(&path, uri_encoded_folder);
+ curl_free(uri_encoded_folder);
+ }
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
strbuf_release(&path);
@@ -1640,10 +1668,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, (long)srvc->ssl_verify);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, (long)srvc->ssl_verify);
- curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
-
- curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
-
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
http_trace_curl_no_data();
setup_curl_trace(curl);
@@ -1662,6 +1686,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
struct credential cred = CREDENTIAL_INIT;
curl = setup_curl(server, &cred);
+
+ curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
+ curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
+
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
@@ -1707,6 +1735,31 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
return res != CURLE_OK;
}
+
+static int curl_list_imap_folders(struct imap_server_conf *server)
+{
+ CURL *curl;
+ CURLcode res = CURLE_OK;
+ struct credential cred = CREDENTIAL_INIT;
+
+ fprintf(stderr, "Fetching the list of available folders...\n");
+ curl = setup_curl(server, &cred);
+ res = curl_easy_perform(curl);
+
+ curl_easy_cleanup(curl);
+ curl_global_cleanup();
+
+ if (cred.username) {
+ if (res == CURLE_OK)
+ credential_approve(the_repository, &cred);
+ else if (res == CURLE_LOGIN_DENIED)
+ credential_reject(the_repository, &cred);
+ }
+
+ credential_clear(&cred);
+
+ return res != CURLE_OK;
+}
#endif
int cmd_main(int argc, const char **argv)
@@ -1747,11 +1800,6 @@ int cmd_main(int argc, const char **argv)
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
- if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
- ret = 1;
- goto out;
- }
if (!server.host) {
if (!server.tunnel) {
fprintf(stderr, "no imap host specified\n");
@@ -1761,6 +1809,24 @@ int cmd_main(int argc, const char **argv)
server.host = xstrdup("tunnel");
}
+ if (list_folders) {
+ if (server.tunnel)
+ ret = list_imap_folders(&server);
+#ifdef USE_CURL_FOR_IMAP_SEND
+ else if (use_curl)
+ ret = curl_list_imap_folders(&server);
+#endif
+ else
+ ret = list_imap_folders(&server);
+ goto out;
+ }
+
+ if (!server.folder) {
+ fprintf(stderr, "no imap store specified\n");
+ ret = 1;
+ goto out;
+ }
+
/* read the messages */
if (strbuf_read(&all_msgs, 0, 0) < 0) {
error_errno(_("could not read from stdin"));
--
2.49.0.824.gcc76007b2f
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v19 08/10] imap-send: display port alongwith host when git credential is invoked
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (6 preceding siblings ...)
2025-06-20 6:40 ` [PATCH v19 07/10] imap-send: add ability to list the available folders Aditya Garg
@ 2025-06-20 6:40 ` Aditya Garg
2025-06-20 6:40 ` [PATCH v19 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
` (2 subsequent siblings)
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 6:40 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
When requesting for passsword, git credential helper used to display
only the host name. For example:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com':
Now, it will display the port along with the host name:
Password for 'imaps://gargaditya08%40live.com@outlook.office365.com:993':
This has been done to make credential helpers more specific for ports.
Also, this behaviour will also mimic git send-email, which displays
the port along with the host name when requesting for a password.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/imap-send.c b/imap-send.c
index b1dddaff3e..ef5cf0a395 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1082,7 +1082,7 @@ static void server_fill_credential(struct imap_server_conf *srvc, struct credent
return;
cred->protocol = xstrdup(srvc->use_ssl ? "imaps" : "imap");
- cred->host = xstrdup(srvc->host);
+ cred->host = xstrfmt("%s:%d", srvc->host, srvc->port);
cred->username = xstrdup_or_null(srvc->user);
cred->password = xstrdup_or_null(srvc->pass);
--
2.49.0.824.gcc76007b2f
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v19 09/10] imap-send: display the destination mailbox when sending a message
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (7 preceding siblings ...)
2025-06-20 6:40 ` [PATCH v19 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
@ 2025-06-20 6:40 ` Aditya Garg
2025-06-20 6:40 ` [PATCH v19 10/10] imap-send: fix minor mistakes in the logs Aditya Garg
2025-06-20 15:50 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Junio C Hamano
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 6:40 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Whenever we sent a message using the `imap-send` command, it would
display a log showing the number of messages which are to be sent.
For example:
sending 1 message
100% (1/1) done
This had been made more informative by adding the name of the destination
folder as well:
Sending 1 message to Drafts folder...
100% (1/1) done
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index ef5cf0a395..11a19ffeec 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -1556,7 +1556,8 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
}
ctx->name = server->folder;
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
@@ -1692,7 +1693,8 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
- fprintf(stderr, "sending %d message%s\n", total, (total != 1) ? "s" : "");
+ fprintf(stderr, "Sending %d message%s to %s folder...\n",
+ total, (total != 1) ? "s" : "", server->folder);
while (1) {
unsigned percent = n * 100 / total;
int prev_len;
--
2.49.0.824.gcc76007b2f
^ permalink raw reply related [flat|nested] 248+ messages in thread
* [PATCH v19 10/10] imap-send: fix minor mistakes in the logs
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (8 preceding siblings ...)
2025-06-20 6:40 ` [PATCH v19 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
@ 2025-06-20 6:40 ` Aditya Garg
2025-06-20 15:50 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Junio C Hamano
10 siblings, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 6:40 UTC (permalink / raw)
To: Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble,
Phillip Wood
Some minor mistakes have been found in the logs. Most of them include
error messages starting with a capital letter, and ending with a period.
Abbreviations like "IMAP" and "OK" should also be in uppercase. Another
mistake was that the error message showing unknown authentication
mechanism used was displaying the host rather than the mechanism in the
logs. Fix them.
Signed-off-by: Aditya Garg <gargaditya08@live.com>
---
imap-send.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/imap-send.c b/imap-send.c
index 11a19ffeec..603e3d6fbc 100644
--- a/imap-send.c
+++ b/imap-send.c
@@ -211,7 +211,7 @@ static int ssl_socket_connect(struct imap_socket *sock UNUSED,
const struct imap_server_conf *cfg UNUSED,
int use_tls_only UNUSED)
{
- fprintf(stderr, "SSL requested but SSL support not compiled in\n");
+ fprintf(stderr, "SSL requested, but SSL support is not compiled in\n");
return -1;
}
@@ -1019,7 +1019,7 @@ static int auth_cram_md5(struct imap_store *ctx, const char *prompt)
ret = socket_write(&ctx->imap->buf.sock, response, strlen(response));
if (ret != strlen(response)) {
free(response);
- return error("IMAP error: sending response failed");
+ return error("IMAP error: sending CRAM-MD5 response failed");
}
free(response);
@@ -1159,7 +1159,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
imap->buf.sock.fd[0] = tunnel.out;
imap->buf.sock.fd[1] = tunnel.in;
- imap_info("ok\n");
+ imap_info("OK\n");
} else {
#ifndef NO_IPV6
struct addrinfo hints, *ai0, *ai;
@@ -1178,7 +1178,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(gai));
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
for (ai0 = ai; ai; ai = ai->ai_next) {
char addr[NI_MAXHOST];
@@ -1216,7 +1216,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
perror("gethostbyname");
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
addr.sin_addr.s_addr = *((int *) he->h_addr_list[0]);
@@ -1230,7 +1230,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
}
#endif
if (s < 0) {
- fputs("Error: unable to connect to server.\n", stderr);
+ fputs("error: unable to connect to server\n", stderr);
goto bail;
}
@@ -1242,7 +1242,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
close(s);
goto bail;
}
- imap_info("ok\n");
+ imap_info("OK\n");
}
/* read the greeting string */
@@ -1295,12 +1295,12 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, const c
if (try_auth_method(srvc, ctx, imap, "XOAUTH2", AUTH_XOAUTH2, auth_xoauth2))
goto bail;
} else {
- fprintf(stderr, "Unknown authentication method:%s\n", srvc->host);
+ fprintf(stderr, "unknown authentication mechanism: %s\n", srvc->auth_method);
goto bail;
}
} else {
if (CAP(NOLOGIN)) {
- fprintf(stderr, "Skipping account %s@%s, server forbids LOGIN\n",
+ fprintf(stderr, "skipping account %s@%s, server forbids LOGIN\n",
srvc->user, srvc->host);
goto bail;
}
@@ -1804,7 +1804,7 @@ int cmd_main(int argc, const char **argv)
if (!server.host) {
if (!server.tunnel) {
- fprintf(stderr, "no imap host specified\n");
+ fprintf(stderr, "no IMAP host specified\n");
ret = 1;
goto out;
}
@@ -1824,7 +1824,7 @@ int cmd_main(int argc, const char **argv)
}
if (!server.folder) {
- fprintf(stderr, "no imap store specified\n");
+ fprintf(stderr, "no IMAP store specified\n");
ret = 1;
goto out;
}
@@ -1844,7 +1844,7 @@ int cmd_main(int argc, const char **argv)
total = count_messages(&all_msgs);
if (!total) {
- fprintf(stderr, "no messages to send\n");
+ fprintf(stderr, "no messages found to send\n");
ret = 1;
goto out;
}
--
2.49.0.824.gcc76007b2f
^ permalink raw reply related [flat|nested] 248+ messages in thread
* Re: [PATCH v18 04/10] imap-send: add support for OAuth2.0 authentication
2025-06-17 10:27 ` Phillip Wood
2025-06-20 5:16 ` Aditya Garg
@ 2025-06-20 7:00 ` Aditya Garg
1 sibling, 0 replies; 248+ messages in thread
From: Aditya Garg @ 2025-06-20 7:00 UTC (permalink / raw)
To: Phillip Wood, Junio C Hamano, git
Cc: Eric Sunshine, Zi Yao, brian m . carlson, Jeff King, Ben Knoble
> This looks very fragile. It would be safer to use an strbuf or if there are no embedded nul bytes xstrfmt() and strlen(). This applies to the next patch as well and any others that are building strings with snprintf() or memcpy().
>
v19 should have these fixed.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
` (9 preceding siblings ...)
2025-06-20 6:40 ` [PATCH v19 10/10] imap-send: fix minor mistakes in the logs Aditya Garg
@ 2025-06-20 15:50 ` Junio C Hamano
2025-06-23 9:09 ` Phillip Wood
10 siblings, 1 reply; 248+ messages in thread
From: Junio C Hamano @ 2025-06-20 15:50 UTC (permalink / raw)
To: Aditya Garg
Cc: git, Eric Sunshine, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble, Phillip Wood
Aditya Garg <gargaditya08@live.com> writes:
> v19: - Use xstrfmt() for OAuth2 strings and strbuf for PLAIN.
>
> Aditya Garg (10):
> imap-send: fix bug causing cfg->folder being set to NULL
> imap-send: fix memory leak in case auth_cram_md5 fails
> imap-send: gracefully fail if CRAM-MD5 authentication is requested
> without OpenSSL
> imap-send: add support for OAuth2.0 authentication
> imap-send: add PLAIN authentication method to OpenSSL
> imap-send: enable specifying the folder using the command line
> imap-send: add ability to list the available folders
> imap-send: display port alongwith host when git credential is invoked
> imap-send: display the destination mailbox when sending a message
> imap-send: fix minor mistakes in the logs
>
> Documentation/config/imap.adoc | 11 +-
> Documentation/git-imap-send.adoc | 68 +++++-
> imap-send.c | 405 ++++++++++++++++++++++++++-----
> 3 files changed, 407 insertions(+), 77 deletions(-)
Looking good. Will replace.
Should we declare victory and mark the topic for 'next' now?
Thanks.
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-06-20 15:50 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Junio C Hamano
@ 2025-06-23 9:09 ` Phillip Wood
2025-06-23 16:27 ` Junio C Hamano
0 siblings, 1 reply; 248+ messages in thread
From: Phillip Wood @ 2025-06-23 9:09 UTC (permalink / raw)
To: Junio C Hamano, Aditya Garg
Cc: git, Eric Sunshine, Zi Yao, brian m . carlson, Jeff King,
Ben Knoble
On 20/06/2025 16:50, Junio C Hamano wrote:
> Aditya Garg <gargaditya08@live.com> writes:
>
>> v19: - Use xstrfmt() for OAuth2 strings and strbuf for PLAIN.
>>
>> Aditya Garg (10):
>> imap-send: fix bug causing cfg->folder being set to NULL
>> imap-send: fix memory leak in case auth_cram_md5 fails
>> imap-send: gracefully fail if CRAM-MD5 authentication is requested
>> without OpenSSL
>> imap-send: add support for OAuth2.0 authentication
>> imap-send: add PLAIN authentication method to OpenSSL
>> imap-send: enable specifying the folder using the command line
>> imap-send: add ability to list the available folders
>> imap-send: display port alongwith host when git credential is invoked
>> imap-send: display the destination mailbox when sending a message
>> imap-send: fix minor mistakes in the logs
>>
>> Documentation/config/imap.adoc | 11 +-
>> Documentation/git-imap-send.adoc | 68 +++++-
>> imap-send.c | 405 ++++++++++++++++++++++++++-----
>> 3 files changed, 407 insertions(+), 77 deletions(-)
>
> Looking good. Will replace.
>
> Should we declare victory and mark the topic for 'next' now?
I think so, the range diff looks good. I've not reviewed each patch but
I just had a quick scan of
git diff origin/master origin/seen imap-send.c
and it looked reasonable.
Best Wishes
Phillip
^ permalink raw reply [flat|nested] 248+ messages in thread
* Re: [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support
2025-06-23 9:09 ` Phillip Wood
@ 2025-06-23 16:27 ` Junio C Hamano
0 siblings, 0 replies; 248+ messages in thread
From: Junio C Hamano @ 2025-06-23 16:27 UTC (permalink / raw)
To: Phillip Wood
Cc: Aditya Garg, git, Eric Sunshine, Zi Yao, brian m . carlson,
Jeff King, Ben Knoble
Phillip Wood <phillip.wood123@gmail.com> writes:
> On 20/06/2025 16:50, Junio C Hamano wrote:
>> Aditya Garg <gargaditya08@live.com> writes:
>>
>>> v19: - Use xstrfmt() for OAuth2 strings and strbuf for PLAIN.
>>>
>>> Aditya Garg (10):
>>> imap-send: fix bug causing cfg->folder being set to NULL
>>> imap-send: fix memory leak in case auth_cram_md5 fails
>>> imap-send: gracefully fail if CRAM-MD5 authentication is requested
>>> without OpenSSL
>>> imap-send: add support for OAuth2.0 authentication
>>> imap-send: add PLAIN authentication method to OpenSSL
>>> imap-send: enable specifying the folder using the command line
>>> imap-send: add ability to list the available folders
>>> imap-send: display port alongwith host when git credential is invoked
>>> imap-send: display the destination mailbox when sending a message
>>> imap-send: fix minor mistakes in the logs
>>>
>>> Documentation/config/imap.adoc | 11 +-
>>> Documentation/git-imap-send.adoc | 68 +++++-
>>> imap-send.c | 405 ++++++++++++++++++++++++++-----
>>> 3 files changed, 407 insertions(+), 77 deletions(-)
>> Looking good. Will replace.
>> Should we declare victory and mark the topic for 'next' now?
>
> I think so, the range diff looks good. I've not reviewed each patch
> but I just had a quick scan of
>
> git diff origin/master origin/seen imap-send.c
>
> and it looked reasonable.
>
> Best Wishes
>
> Phillip
Thanks.
^ permalink raw reply [flat|nested] 248+ messages in thread
end of thread, other threads:[~2025-06-23 16:27 UTC | newest]
Thread overview: 248+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-22 17:27 [PATCH 0/2] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-22 17:27 ` [PATCH 1/2] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-22 18:00 ` Eric Sunshine
2025-05-22 18:04 ` Aditya Garg
2025-05-22 18:21 ` Aditya Garg
2025-05-22 18:25 ` Eric Sunshine
2025-05-22 18:28 ` Aditya Garg
2025-05-22 18:31 ` Jeff King
2025-05-22 18:33 ` Eric Sunshine
2025-05-24 16:28 ` Ben Knoble
2025-05-24 16:30 ` Aditya Garg
2025-05-24 16:32 ` Ben Knoble
2025-05-22 19:30 ` Aditya Garg
2025-05-22 19:32 ` Eric Sunshine
2025-05-22 19:40 ` Aditya Garg
2025-05-22 19:02 ` Junio C Hamano
2025-05-22 19:04 ` Aditya Garg
2025-05-22 18:29 ` Jeff King
2025-05-22 18:31 ` Aditya Garg
2025-05-22 17:27 ` [PATCH 2/2] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-05-22 19:45 ` brian m. carlson
2025-05-22 19:49 ` Aditya Garg
2025-05-22 19:49 ` [PATCH v2 0/3] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-22 19:49 ` [PATCH v2 1/3] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-22 23:27 ` Junio C Hamano
2025-05-22 19:49 ` [PATCH v2 2/3] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-05-22 19:49 ` [PATCH v2 3/3] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-05-23 3:58 ` [PATCH v3 0/3] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-23 3:58 ` [PATCH v3 1/3] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-23 3:58 ` [PATCH v3 2/3] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-05-23 3:58 ` [PATCH v3 3/3] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-05-23 12:14 ` [PATCH v4 0/4] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-23 12:14 ` [PATCH v4 1/4] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-23 12:14 ` [PATCH v4 2/4] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-05-23 12:14 ` [PATCH v4 3/4] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-05-23 12:14 ` [PATCH v4 4/4] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-05-25 18:54 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-25 18:54 ` [PATCH v5 1/6] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-25 18:54 ` [PATCH v5 2/6] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-05-25 18:54 ` [PATCH v5 3/6] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-05-25 18:55 ` [PATCH v5 4/6] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-05-25 18:55 ` [PATCH v5 5/6] imap-send: enable specifying the folder using the command line Aditya Garg
2025-05-25 18:55 ` [PATCH v5 6/6] imap-send: enable user to choose between libcurl and openssl using the config Aditya Garg
2025-05-25 20:34 ` [PATCH v5 0/6] imap-send: make it usable again and add OAuth2.0 support Eric Sunshine
2025-05-26 9:06 ` Aditya Garg
2025-05-28 7:38 ` [PATCH v6 " Aditya Garg
2025-05-28 7:38 ` [PATCH v6 1/6] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-28 7:38 ` [PATCH v6 2/6] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-05-28 7:38 ` [PATCH v6 3/6] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-05-28 7:38 ` [PATCH v6 4/6] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-05-28 7:38 ` [PATCH v6 5/6] imap-send: enable specifying the folder using the command line Aditya Garg
2025-05-28 7:38 ` [PATCH v6 6/6] imap-send: enable user to choose between libcurl and openssl using the config Aditya Garg
2025-05-28 17:17 ` [PATCH v7 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-28 17:17 ` [PATCH v7 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-28 17:17 ` [PATCH v7 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-05-28 17:17 ` [PATCH v7 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-05-28 17:17 ` [PATCH v7 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-05-28 17:17 ` [PATCH v7 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
2025-05-28 17:17 ` [PATCH v7 6/9] imap-send: enable user to choose between libcurl and openssl using the config Aditya Garg
2025-05-29 13:58 ` Phillip Wood
2025-05-29 14:09 ` Aditya Garg
2025-05-29 16:25 ` Junio C Hamano
2025-05-29 16:28 ` Aditya Garg
2025-05-28 17:17 ` [PATCH v7 7/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
2025-05-28 17:17 ` [PATCH v7 8/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-05-28 17:17 ` [PATCH v7 9/9] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-05-29 16:21 ` [PATCH v8 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-29 16:21 ` [PATCH v8 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-29 16:21 ` [PATCH v8 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-05-29 16:21 ` [PATCH v8 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-05-29 16:21 ` [PATCH v8 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-05-29 16:21 ` [PATCH v8 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
2025-05-29 16:21 ` [PATCH v8 6/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
2025-05-29 16:21 ` [PATCH v8 7/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-05-29 16:21 ` [PATCH v8 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-05-29 16:21 ` [PATCH v8 9/9] imap-send: add ability to list the available folders Aditya Garg
2025-05-30 17:32 ` [PATCH v9 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-05-30 17:32 ` [PATCH v9 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-05-30 17:32 ` [PATCH v9 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-05-30 20:51 ` Eric Sunshine
2025-05-30 21:12 ` Junio C Hamano
2025-05-31 9:06 ` Aditya Garg
2025-05-30 17:32 ` [PATCH v9 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-05-30 17:32 ` [PATCH v9 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-05-30 17:32 ` [PATCH v9 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
2025-05-31 0:45 ` Junio C Hamano
2025-05-31 9:16 ` Aditya Garg
2025-06-01 2:40 ` Junio C Hamano
2025-06-01 4:41 ` Aditya Garg
2025-05-30 17:32 ` [PATCH v9 6/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
2025-05-30 17:32 ` [PATCH v9 7/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-05-30 17:32 ` [PATCH v9 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-05-30 17:32 ` [PATCH v9 9/9] imap-send: add ability to list the available folders Aditya Garg
2025-06-01 7:10 ` [PATCH v10 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-01 7:10 ` [PATCH v10 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-01 7:10 ` [PATCH v10 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-06-01 7:10 ` [PATCH v10 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-06-01 7:10 ` [PATCH v10 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-06-01 7:10 ` [PATCH v10 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
2025-06-01 7:10 ` [PATCH v10 6/9] imap-send: fix numerous spelling and grammar mistakes in logs Aditya Garg
2025-06-01 7:28 ` Eric Sunshine
2025-06-01 7:30 ` Aditya Garg
2025-06-01 7:32 ` Aditya Garg
2025-06-01 7:10 ` [PATCH v10 7/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-06-01 7:10 ` [PATCH v10 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-01 7:10 ` [PATCH v10 9/9] imap-send: add ability to list the available folders Aditya Garg
2025-06-01 8:38 ` [PATCH v11 0/9] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-01 8:38 ` [PATCH v11 1/9] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-01 8:38 ` [PATCH v11 2/9] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-06-02 0:13 ` Junio C Hamano
2025-06-01 8:38 ` [PATCH v11 3/9] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-06-02 0:27 ` Junio C Hamano
2025-06-02 4:01 ` Aditya Garg
2025-06-01 8:38 ` [PATCH v11 4/9] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-06-01 8:38 ` [PATCH v11 5/9] imap-send: enable specifying the folder using the command line Aditya Garg
2025-06-02 0:39 ` Junio C Hamano
2025-06-02 3:45 ` Aditya Garg
2025-06-01 8:38 ` [PATCH v11 6/9] imap-send: fix minor mistakes in the logs Aditya Garg
2025-06-02 0:42 ` Junio C Hamano
2025-06-02 3:41 ` Aditya Garg
2025-06-01 8:38 ` [PATCH v11 7/9] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-06-01 8:38 ` [PATCH v11 8/9] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-02 0:43 ` Junio C Hamano
2025-06-01 8:38 ` [PATCH v11 9/9] imap-send: add ability to list the available folders Aditya Garg
2025-06-02 10:59 ` [PATCH v12 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-02 10:59 ` [PATCH v12 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-02 10:59 ` [PATCH v12 02/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-06-05 8:00 ` Jeff King
2025-06-05 8:12 ` Aditya Garg
2025-06-05 16:08 ` Junio C Hamano
2025-06-05 16:17 ` Aditya Garg
2025-06-05 16:28 ` Junio C Hamano
2025-06-05 22:50 ` Jeff King
2025-06-02 10:59 ` [PATCH v12 03/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-06-02 10:59 ` [PATCH v12 04/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-06-02 10:59 ` [PATCH v12 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
2025-06-02 10:59 ` [PATCH v12 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
2025-06-02 10:59 ` [PATCH v12 07/10] imap-send: fix minor mistakes in the logs Aditya Garg
2025-06-02 10:59 ` [PATCH v12 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-06-02 10:59 ` [PATCH v12 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-02 10:59 ` [PATCH v12 10/10] imap-send: add ability to list the available folders Aditya Garg
2025-06-05 8:42 ` [PATCH v13 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-05 8:42 ` [PATCH v13 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-05 8:42 ` [PATCH v13 02/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-06-05 16:33 ` Junio C Hamano
2025-06-05 17:16 ` Aditya Garg
2025-06-05 18:48 ` Junio C Hamano
2025-06-06 3:28 ` Aditya Garg
2025-06-06 4:04 ` Aditya Garg
2025-06-06 4:35 ` Junio C Hamano
2025-06-06 4:40 ` Aditya Garg
2025-06-05 8:42 ` [PATCH v13 04/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-06-05 8:42 ` [PATCH v13 03/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-06-05 8:42 ` [PATCH v13 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
2025-06-05 8:42 ` [PATCH v13 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
2025-06-05 8:42 ` [PATCH v13 07/10] imap-send: fix minor mistakes in the logs Aditya Garg
2025-06-05 8:42 ` [PATCH v13 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-06-05 8:42 ` [PATCH v13 10/10] imap-send: add ability to list the available folders Aditya Garg
2025-06-05 8:42 ` [PATCH v13 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-06 20:06 ` [PATCH v14 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-06 20:06 ` [PATCH v14 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-06 20:06 ` [PATCH v14 02/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-06-06 20:06 ` [PATCH v14 03/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-06-06 20:06 ` [PATCH v14 04/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-06-06 20:06 ` [PATCH v14 05/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
2025-06-07 15:32 ` Junio C Hamano
2025-06-07 17:13 ` Aditya Garg
2025-06-08 4:20 ` Junio C Hamano
2025-06-08 7:54 ` Aditya Garg
2025-06-08 10:56 ` Aditya Garg
2025-06-06 20:06 ` [PATCH v14 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
2025-06-06 20:06 ` [PATCH v14 07/10] imap-send: fix minor mistakes in the logs Aditya Garg
2025-06-06 20:06 ` [PATCH v14 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-06-06 20:06 ` [PATCH v14 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-06 20:06 ` [PATCH v14 10/10] imap-send: add ability to list the available folders Aditya Garg
2025-06-08 10:55 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-08 10:55 ` [PATCH v15 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-08 10:55 ` [PATCH v15 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-06-08 10:55 ` [PATCH v15 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
2025-06-08 10:55 ` [PATCH v15 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-06-08 10:55 ` [PATCH v15 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-06-08 10:55 ` [PATCH v15 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
2025-06-08 10:55 ` [PATCH v15 07/10] imap-send: fix minor mistakes in the logs Aditya Garg
2025-06-08 10:55 ` [PATCH v15 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-06-08 10:55 ` [PATCH v15 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-08 10:55 ` [PATCH v15 10/10] imap-send: add ability to list the available folders Aditya Garg
2025-06-08 20:50 ` [PATCH v15 00/10] imap-send: make it usable again and add OAuth2.0 support Junio C Hamano
2025-06-09 4:31 ` Aditya Garg
2025-06-09 7:23 ` Aditya Garg
2025-06-09 7:20 ` [PATCH v16 " Aditya Garg
2025-06-09 7:20 ` [PATCH v16 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-09 7:20 ` [PATCH v16 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-06-09 7:20 ` [PATCH v16 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
2025-06-09 19:15 ` Junio C Hamano
2025-06-09 19:20 ` Aditya Garg
2025-06-09 7:20 ` [PATCH v16 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-06-09 7:20 ` [PATCH v16 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-06-09 7:20 ` [PATCH v16 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
2025-06-09 18:33 ` Junio C Hamano
2025-06-09 7:20 ` [PATCH v16 07/10] imap-send: add ability to list the available folders Aditya Garg
2025-06-09 18:42 ` Junio C Hamano
2025-06-09 7:20 ` [PATCH v16 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-06-09 18:55 ` Junio C Hamano
2025-06-09 19:02 ` Aditya Garg
2025-06-09 20:14 ` Junio C Hamano
2025-06-09 7:20 ` [PATCH v16 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-09 18:57 ` Junio C Hamano
2025-06-09 19:05 ` Aditya Garg
2025-06-09 7:20 ` [PATCH v16 10/10] imap-send: fix minor mistakes in the logs Aditya Garg
2025-06-09 15:41 ` [PATCH v17 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-09 15:41 ` [PATCH v17 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-09 15:41 ` [PATCH v17 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-06-09 15:41 ` [PATCH v17 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
2025-06-09 15:41 ` [PATCH v17 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-06-09 15:41 ` [PATCH v17 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-06-09 15:41 ` [PATCH v17 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
2025-06-09 15:41 ` [PATCH v17 07/10] imap-send: add ability to list the available folders Aditya Garg
2025-06-09 15:41 ` [PATCH v17 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-06-09 15:41 ` [PATCH v17 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-09 15:41 ` [PATCH v17 10/10] imap-send: fix minor mistakes in the logs Aditya Garg
2025-06-09 20:22 ` [PATCH v18 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-09 20:22 ` [PATCH v18 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-09 20:22 ` [PATCH v18 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-06-09 20:22 ` [PATCH v18 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
2025-06-09 20:22 ` [PATCH v18 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-06-17 10:27 ` Phillip Wood
2025-06-20 5:16 ` Aditya Garg
2025-06-20 7:00 ` Aditya Garg
2025-06-09 20:22 ` [PATCH v18 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-06-09 20:22 ` [PATCH v18 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
2025-06-09 20:22 ` [PATCH v18 07/10] imap-send: add ability to list the available folders Aditya Garg
2025-06-09 20:22 ` [PATCH v18 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-06-09 20:22 ` [PATCH v18 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-09 20:22 ` [PATCH v18 10/10] imap-send: fix minor mistakes in the logs Aditya Garg
2025-06-20 6:40 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Aditya Garg
2025-06-20 6:40 ` [PATCH v19 01/10] imap-send: fix bug causing cfg->folder being set to NULL Aditya Garg
2025-06-20 6:40 ` [PATCH v19 02/10] imap-send: fix memory leak in case auth_cram_md5 fails Aditya Garg
2025-06-20 6:40 ` [PATCH v19 03/10] imap-send: gracefully fail if CRAM-MD5 authentication is requested without OpenSSL Aditya Garg
2025-06-20 6:40 ` [PATCH v19 04/10] imap-send: add support for OAuth2.0 authentication Aditya Garg
2025-06-20 6:40 ` [PATCH v19 05/10] imap-send: add PLAIN authentication method to OpenSSL Aditya Garg
2025-06-20 6:40 ` [PATCH v19 06/10] imap-send: enable specifying the folder using the command line Aditya Garg
2025-06-20 6:40 ` [PATCH v19 07/10] imap-send: add ability to list the available folders Aditya Garg
2025-06-20 6:40 ` [PATCH v19 08/10] imap-send: display port alongwith host when git credential is invoked Aditya Garg
2025-06-20 6:40 ` [PATCH v19 09/10] imap-send: display the destination mailbox when sending a message Aditya Garg
2025-06-20 6:40 ` [PATCH v19 10/10] imap-send: fix minor mistakes in the logs Aditya Garg
2025-06-20 15:50 ` [PATCH v19 00/10] imap-send: make it usable again and add OAuth2.0 support Junio C Hamano
2025-06-23 9:09 ` Phillip Wood
2025-06-23 16:27 ` Junio C Hamano
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).