Skip to content

Commit 55ff4ac

Browse files
Added conditional capabilities functionality
This change adds a new 'capability' structure which contains both a capability word and a set of flags which specify the circumstances that must be met for the respective word to be advertised. Some examples of circumstances that can be specified when adding new capabilities are: - Only show at pre-login/post-login - Only show if STARTTLS is active/inactive (not RAW SSL) - Only show if raw SSL is active/inactive (not STARTTLS) - Only show if any TLS/SSL is active/inactive The default operation is to show the capabaility word if at least one of the circumstances is met, an additional flag provides the ability to only show the capability word if exactly all of the circumstances are met. The API for adding capabilities now simply wraps the API for adding entries to the capability list with the 'show always' flag set. Retrieving the capability string is now a loop that iterates the list of capability objects and evaluates whether each capability should be added to the capability string based on the state of the current IMAP session.
1 parent bd08690 commit 55ff4ac

File tree

9 files changed

+502
-71
lines changed

9 files changed

+502
-71
lines changed

src/imap-login/imap-login-client.c

Lines changed: 121 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "imap-quote.h"
2121
#include "imap-login-commands.h"
2222
#include "imap-login-settings.h"
23+
#include "imap-capability-list.h"
2324

2425
#if LOGIN_MAX_INBUF_SIZE < 1024+2
2526
# error LOGIN_MAX_INBUF_SIZE too short to fit all ID command parameters
@@ -93,31 +94,80 @@ static const char *get_capability(struct client *client)
9394
{
9495
struct imap_client *imap_client = (struct imap_client *)client;
9596
string_t *cap_str = t_str_new(256);
96-
bool explicit_capability = FALSE;
97-
98-
if (*imap_client->set->imap_capability == '\0')
99-
str_append(cap_str, CAPABILITY_BANNER_STRING);
100-
else if (*imap_client->set->imap_capability != '+') {
101-
explicit_capability = TRUE;
102-
str_append(cap_str, imap_client->set->imap_capability);
103-
} else {
104-
str_append(cap_str, CAPABILITY_BANNER_STRING);
105-
str_append_c(cap_str, ' ');
106-
str_append(cap_str, imap_client->set->imap_capability + 1);
107-
}
10897

109-
if (!explicit_capability) {
110-
if (imap_client->set->imap_literal_minus)
111-
str_append(cap_str, " LITERAL-");
112-
else
113-
str_append(cap_str, " LITERAL+");
98+
/* iterate capability list and append each capability if conditions met */
99+
const struct imap_capability *capp;
100+
array_foreach(&imap_client->capability_list->imap_capabilities, capp) {
101+
/* local flags variable will get filled with all the flags
102+
that matched the current conditions -- then at the end
103+
if FLAG_REQUIRE_ALL is present the local flags variable
104+
is compared against the command flags to check for a
105+
perfect match */
106+
uint32_t flags = 0;
107+
if (capp->flags) {
108+
/* is this pre-auth? (yes) */
109+
if ((capp->flags & CAP_PREAUTH) != 0) {
110+
flags |= CAP_PREAUTH;
111+
}
112+
/* is starttls active? */
113+
if ((capp->flags & CAP_TLSACTIVE) != 0 &&
114+
client->starttls) {
115+
flags |= CAP_TLSACTIVE;
116+
}
117+
/* is starttls inactive? */
118+
if ((capp->flags & CAP_TLSINACTIVE) != 0 &&
119+
!client->starttls) {
120+
flags |= CAP_TLSINACTIVE;
121+
}
122+
/* is ssl active? */
123+
if ((capp->flags & CAP_SSLACTIVE) != 0 &&
124+
(!client->starttls && client->tls)) {
125+
flags |= CAP_SSLACTIVE;
126+
}
127+
/* is ssl inactive? */
128+
if ((capp->flags & CAP_SSLINACTIVE) != 0 &&
129+
(client->starttls || !client->tls)) {
130+
flags |= CAP_SSLINACTIVE;
131+
}
132+
/* is login disabled? */
133+
if ((capp->flags & CAP_NO_LOGIN) != 0 &&
134+
is_login_cmd_disabled(client)) {
135+
flags |= CAP_NO_LOGIN;
136+
}
137+
/* are we secure? */
138+
if ((capp->flags & CAP_SECURE) != 0 &&
139+
client->tls) {
140+
flags |= CAP_SECURE;
141+
}
142+
/* are we insecure? */
143+
if ((capp->flags & CAP_INSECURE) != 0 &&
144+
!client->tls) {
145+
flags |= CAP_INSECURE;
146+
}
147+
148+
bool add = FALSE;
149+
/* now check if CAP_REQUIRE_ALL is present */
150+
if ((capp->flags & CAP_REQUIRE_ALL) != 0) {
151+
/* did we match all? */
152+
if ((capp->flags & ~CAP_REQUIRE_ALL) == flags) {
153+
add = TRUE;
154+
}
155+
/* no CAP_REQUIRE_ALL - did we match any flags? */
156+
} else if (flags > 0) {
157+
add = TRUE;
158+
}
159+
160+
/* should we add this entry? */
161+
if (add) {
162+
if (*str_c(cap_str) != '\0') {
163+
str_append_c(cap_str, ' ');
164+
}
165+
str_append(cap_str, capp->command);
166+
}
167+
}
114168
}
115169

116-
if (client_is_tls_enabled(client) && !client->tls)
117-
str_append(cap_str, " STARTTLS");
118-
if (is_login_cmd_disabled(client))
119-
str_append(cap_str, " LOGINDISABLED");
120-
170+
/* grab the AUTH= capabilities from auth server */
121171
client_authenticate_get_capabilities(client, cap_str);
122172
return str_c(cap_str);
123173
}
@@ -381,6 +431,7 @@ static struct client *imap_client_alloc(pool_t pool)
381431
static void imap_client_create(struct client *client, void **other_sets)
382432
{
383433
struct imap_client *imap_client = (struct imap_client *)client;
434+
bool explicit_capability = FALSE;
384435

385436
imap_client->set = other_sets[0];
386437
imap_client->parser =
@@ -389,6 +440,54 @@ static void imap_client_create(struct client *client, void **other_sets)
389440
IMAP_LOGIN_MAX_LINE_LENGTH);
390441
if (imap_client->set->imap_literal_minus)
391442
imap_parser_enable_literal_minus(imap_client->parser);
443+
444+
/* create our capability list from CAPABILITY_BANNER_STRING */
445+
imap_client->capability_list =
446+
imap_capability_list_create(NULL);
447+
448+
/* do we have capabilities defined in our settings? */
449+
if (*imap_client->set->imap_capability != '\0') {
450+
/* does the configuration start with a + sign? */
451+
if (*imap_client->set->imap_capability == '+') {
452+
/* add the capability banner string to the cap list */
453+
imap_capability_list_append_string(imap_client->capability_list,
454+
CAPABILITY_BANNER_STRING);
455+
/* add everything after the plus to our cap list */
456+
imap_capability_list_append_string(imap_client->capability_list,
457+
imap_client->set->imap_capability + 1);
458+
} else {
459+
/* add everything to our cap list */
460+
imap_capability_list_append_string(imap_client->capability_list,
461+
imap_client->set->imap_capability);
462+
explicit_capability = TRUE;
463+
}
464+
} else {
465+
/* add the capability banner string to the cap list */
466+
imap_capability_list_append_string(imap_client->capability_list,
467+
CAPABILITY_BANNER_STRING);
468+
}
469+
470+
/* add STARTTLS to cap list if it is needed */
471+
if (strcmp(client->ssl_set->ssl, "no") != 0) {
472+
imap_capability_list_add(imap_client->capability_list,
473+
"STARTTLS", CAP_REQUIRE_ALL | CAP_PREAUTH | CAP_INSECURE);
474+
}
475+
476+
/* add LOGINDISABLED to cap list */
477+
imap_capability_list_add(imap_client->capability_list,
478+
"LOGINDISABLED", CAP_REQUIRE_ALL | CAP_PREAUTH | CAP_NO_LOGIN);
479+
480+
/* Add the LITERAL+/- if the capability isn't explicitly set */
481+
if (!explicit_capability) {
482+
if (imap_client->set->imap_literal_minus) {
483+
imap_capability_list_add(imap_client->capability_list,
484+
"LITERAL-", CAP_ALWAYS);
485+
} else {
486+
imap_capability_list_add(imap_client->capability_list,
487+
"LITERAL+", CAP_ALWAYS);
488+
}
489+
}
490+
392491
client->io = io_add_istream(client->input, client_input, client);
393492
}
394493

src/imap-login/imap-login-client.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ struct imap_client {
5656

5757
const struct imap_login_settings *set;
5858
struct imap_parser *parser;
59+
struct imap_capability_list *capability_list;
5960
char *proxy_backend_capability;
6061

6162
const char *cmd_tag, *cmd_name;

src/imap/cmd-capability.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
bool cmd_capability(struct client_command_context *cmd)
88
{
99
client_send_line(cmd->client, t_strconcat(
10-
"* CAPABILITY ", str_c(cmd->client->capability_string), NULL));
10+
"* CAPABILITY ", client_get_capability(cmd->client), NULL));
1111

1212
client_send_tagline(cmd, "OK Capability completed.");
1313
return TRUE;

0 commit comments

Comments
 (0)