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@xxxxxxxx> ## 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@xxxxxxxx> + ## 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@xxxxxxxxx +- port = 993 ++ folder = "[Gmail]/Drafts" ++ host = imaps://imap.gmail.com ++ user = user@xxxxxxxxx ++ 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@xxxxxxxxx ++ 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@xxxxxxxxxxx ++ 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