Skip to content

Commit 72efbda

Browse files
AdityaGarg8gitster
authored andcommitted
imap-send: add ability to list the available folders
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent f4e47d7 commit 72efbda

File tree

2 files changed

+87
-17
lines changed

2 files changed

+87
-17
lines changed

Documentation/git-imap-send.adoc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ SYNOPSIS
1010
--------
1111
[verse]
1212
'git imap-send' [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>]
13+
'git imap-send' --list
1314

1415

1516
DESCRIPTION
@@ -54,6 +55,8 @@ OPTIONS
5455
using libcurl. Ignored if Git was built with the NO_OPENSSL option
5556
set.
5657

58+
--list::
59+
Run the IMAP LIST command to output a list of all the folders present.
5760

5861
CONFIGURATION
5962
-------------
@@ -123,7 +126,8 @@ it. Alternatively, use OAuth2.0 authentication as described below.
123126

124127
[NOTE]
125128
You might need to instead use: `folder = "[Google Mail]/Drafts"` if you get an error
126-
that the "Folder doesn't exist".
129+
that the "Folder doesn't exist". You can also run `git imap-send --list` to get a
130+
list of available folders.
127131

128132
[NOTE]
129133
If your Gmail account is set to another language than English, the name of the "Drafts"

imap-send.c

Lines changed: 82 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,21 @@
4545
#endif
4646

4747
static int verbosity;
48+
static int list_folders = 0;
4849
static int use_curl = USE_CURL_DEFAULT;
4950
static char *opt_folder = NULL;
5051

51-
static const char * const imap_send_usage[] = { "git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>", NULL };
52+
static char const * const imap_send_usage[] = {
53+
N_("git imap-send [-v] [-q] [--[no-]curl] [(--folder|-f) <folder>] < <mbox>"),
54+
"git imap-send --list",
55+
NULL
56+
};
5257

5358
static struct option imap_send_options[] = {
5459
OPT__VERBOSITY(&verbosity),
5560
OPT_BOOL(0, "curl", &use_curl, "use libcurl to communicate with the IMAP server"),
5661
OPT_STRING('f', "folder", &opt_folder, "folder", "specify the IMAP folder"),
62+
OPT_BOOL(0, "list", &list_folders, "list all folders on the IMAP server"),
5763
OPT_END()
5864
};
5965

@@ -429,7 +435,7 @@ static int buffer_gets(struct imap_buffer *b, char **s)
429435
if (b->buf[b->offset + 1] == '\n') {
430436
b->buf[b->offset] = 0; /* terminate the string */
431437
b->offset += 2; /* next line */
432-
if (0 < verbosity)
438+
if ((0 < verbosity) || (list_folders && strstr(*s, "* LIST")))
433439
puts(*s);
434440
return 0;
435441
}
@@ -1579,6 +1585,26 @@ static int append_msgs_to_imap(struct imap_server_conf *server,
15791585
return 0;
15801586
}
15811587

1588+
static int list_imap_folders(struct imap_server_conf *server)
1589+
{
1590+
struct imap_store *ctx = imap_open_store(server, "INBOX");
1591+
if (!ctx) {
1592+
fprintf(stderr, "failed to connect to IMAP server\n");
1593+
return 1;
1594+
}
1595+
1596+
fprintf(stderr, "Fetching the list of available folders...\n");
1597+
/* Issue the LIST command and print the results */
1598+
if (imap_exec(ctx, NULL, "LIST \"\" \"*\"") != RESP_OK) {
1599+
fprintf(stderr, "failed to list folders\n");
1600+
imap_close_store(ctx);
1601+
return 1;
1602+
}
1603+
1604+
imap_close_store(ctx);
1605+
return 0;
1606+
}
1607+
15821608
#ifdef USE_CURL_FOR_IMAP_SEND
15831609
static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
15841610
{
@@ -1612,11 +1638,13 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
16121638
if (!path.len || path.buf[path.len - 1] != '/')
16131639
strbuf_addch(&path, '/');
16141640

1615-
uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
1616-
if (!uri_encoded_folder)
1617-
die("failed to encode server folder");
1618-
strbuf_addstr(&path, uri_encoded_folder);
1619-
curl_free(uri_encoded_folder);
1641+
if (!list_folders) {
1642+
uri_encoded_folder = curl_easy_escape(curl, srvc->folder, 0);
1643+
if (!uri_encoded_folder)
1644+
die("failed to encode server folder");
1645+
strbuf_addstr(&path, uri_encoded_folder);
1646+
curl_free(uri_encoded_folder);
1647+
}
16201648

16211649
curl_easy_setopt(curl, CURLOPT_URL, path.buf);
16221650
strbuf_release(&path);
@@ -1647,10 +1675,6 @@ static CURL *setup_curl(struct imap_server_conf *srvc, struct credential *cred)
16471675
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, srvc->ssl_verify);
16481676
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, srvc->ssl_verify);
16491677

1650-
curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
1651-
1652-
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
1653-
16541678
if (0 < verbosity || getenv("GIT_CURL_VERBOSE"))
16551679
http_trace_curl_no_data();
16561680
setup_curl_trace(curl);
@@ -1669,6 +1693,10 @@ static int curl_append_msgs_to_imap(struct imap_server_conf *server,
16691693
struct credential cred = CREDENTIAL_INIT;
16701694

16711695
curl = setup_curl(server, &cred);
1696+
1697+
curl_easy_setopt(curl, CURLOPT_READFUNCTION, fread_buffer);
1698+
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
1699+
16721700
curl_easy_setopt(curl, CURLOPT_READDATA, &msgbuf);
16731701

16741702
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,
17141742

17151743
return res != CURLE_OK;
17161744
}
1745+
1746+
static int curl_list_imap_folders(struct imap_server_conf *server)
1747+
{
1748+
CURL *curl;
1749+
CURLcode res = CURLE_OK;
1750+
struct credential cred = CREDENTIAL_INIT;
1751+
1752+
fprintf(stderr, "Fetching the list of available folders...\n");
1753+
curl = setup_curl(server, &cred);
1754+
res = curl_easy_perform(curl);
1755+
1756+
curl_easy_cleanup(curl);
1757+
curl_global_cleanup();
1758+
1759+
if (cred.username) {
1760+
if (res == CURLE_OK)
1761+
credential_approve(the_repository, &cred);
1762+
else if (res == CURLE_LOGIN_DENIED)
1763+
credential_reject(the_repository, &cred);
1764+
}
1765+
1766+
credential_clear(&cred);
1767+
1768+
return res != CURLE_OK;
1769+
}
17171770
#endif
17181771

17191772
int cmd_main(int argc, const char **argv)
@@ -1754,11 +1807,6 @@ int cmd_main(int argc, const char **argv)
17541807
if (!server.port)
17551808
server.port = server.use_ssl ? 993 : 143;
17561809

1757-
if (!server.folder) {
1758-
fprintf(stderr, "no imap store specified\n");
1759-
ret = 1;
1760-
goto out;
1761-
}
17621810
if (!server.host) {
17631811
if (!server.tunnel) {
17641812
fprintf(stderr, "no imap host specified\n");
@@ -1768,6 +1816,24 @@ int cmd_main(int argc, const char **argv)
17681816
server.host = xstrdup("tunnel");
17691817
}
17701818

1819+
if (list_folders) {
1820+
if (server.tunnel)
1821+
ret = list_imap_folders(&server);
1822+
#ifdef USE_CURL_FOR_IMAP_SEND
1823+
else if (use_curl)
1824+
ret = curl_list_imap_folders(&server);
1825+
#endif
1826+
else
1827+
ret = list_imap_folders(&server);
1828+
goto out;
1829+
}
1830+
1831+
if (!server.folder) {
1832+
fprintf(stderr, "no imap store specified\n");
1833+
ret = 1;
1834+
goto out;
1835+
}
1836+
17711837
/* read the messages */
17721838
if (strbuf_read(&all_msgs, 0, 0) < 0) {
17731839
error_errno(_("could not read from stdin"));

0 commit comments

Comments
 (0)