@@ -387,7 +387,7 @@ static void list_status_logger(mailstream *imap_stream, int log_type, const char
387387 return ;
388388 }
389389
390- /* Can be broken up across multiple log callback calls, so append to a dynstr */
390+ /* Can be broken up across multiple log callback calls */
391391 client_debug (6 , "Log callback of %lu bytes for LIST-STATUS" , size );
392392 /* Since this can take quite a bit of time, send an update here */
393393 /* Look for "* STATUS " */
@@ -875,6 +875,7 @@ static int __client_list(struct client *client)
875875 clist * imap_list ;
876876 int res , i ;
877877 int needunselect = 0 ;
878+ int numother = 0 ;
878879 /* This is a single-threaded application, so there is no concurrency risk to making this static/global,
879880 * and it's probably better to put such a large buffer in the global segment rather than on the stack. */
880881 static char list_status_buf [32768 ]; /* Hopefully big enough to fit the entire LIST-STATUS response */
@@ -956,6 +957,10 @@ static int __client_list(struct client *client)
956957 client -> delimiter = mb_list -> mb_delimiter ;
957958 client -> mailboxes [i ].name = strdup (name );
958959
960+ if (IMAP_HAS_CAPABILITY (client , IMAP_CAPABILITY_NOTIFY ) && !strncmp (name , "Other Users." , STRLEN ("Other Users." ))) {
961+ numother ++ ;
962+ }
963+
959964 if (client -> mailboxes [i ].flags & IMAP_MAILBOX_NOSELECT ) {
960965 continue ;
961966 }
@@ -1019,6 +1024,53 @@ static int __client_list(struct client *client)
10191024 free (mb_names );
10201025 }
10211026
1027+ if (IMAP_HAS_CAPABILITY (client , IMAP_CAPABILITY_NOTIFY )) {
1028+ char cmd [2048 ];
1029+ char buf [sizeof (cmd ) - 147 ];
1030+ int bytes = 0 ;
1031+ int otherinboxonly = numother > 10 ;
1032+
1033+ #define OTHER_USERS_WATCHALL_THRESHOLD 25
1034+
1035+ if (numother > OTHER_USERS_WATCHALL_THRESHOLD ) {
1036+ clistiter * cur ;
1037+ for (cur = clist_begin (imap_list ); cur ; cur = clist_next (cur )) {
1038+ struct mailimap_mailbox_list * mb_list = clist_content (cur );
1039+ const char * name = mb_list -> mb_name ;
1040+ if (!strncmp (name , "Other Users." , STRLEN ("Other Users." ))) {
1041+ if (otherinboxonly && !strcasestr (name , "INBOX" )) {
1042+ continue ;
1043+ }
1044+ } else if (!strncmp (name , "Shared Folders." , STRLEN ("Shared Folders." ))) {
1045+ if (!strcasestr (name , "INBOX" )) {
1046+ continue ;
1047+ }
1048+ } else {
1049+ continue ; /* Personal namespace */
1050+ }
1051+ bytes += snprintf (buf + bytes , sizeof (buf ) - bytes , " %c%s%c" , '"' , name , '"' );
1052+ if ((size_t ) bytes >= sizeof (buf )) {
1053+ client_warning ("Truncation occured when building NOTIFY command" );
1054+ break ;
1055+ }
1056+ }
1057+ }
1058+
1059+ /* We just want notifications when something happens, having an untagged FETCH sent to us isn't that important.
1060+ * We can wake up and do some work if really needed. */
1061+ if (bytes > 0 && (size_t ) bytes <= sizeof (buf )) {
1062+ snprintf (cmd , sizeof (cmd ), "NOTIFY SET (SELECTED-DELAYED (MessageNew MessageExpunge FlagChange)) %s(personal (MessageNew MessageExpunge)) (mailboxes%s (MessageNew MessageExpunge))" ,
1063+ numother <= OTHER_USERS_WATCHALL_THRESHOLD ? "(subtree \"Other Users\" (MessageNew MessageExpunge FlagChange))" : "" ,
1064+ buf );
1065+ } else {
1066+ snprintf (cmd , sizeof (cmd ), "NOTIFY SET (SELECTED-DELAYED (MessageNew MessageExpunge FlagChange)) (personal (MessageNew MessageExpunge))%s" , numother ? " (subtree \"Other Users\" (MessageNew MessageExpunge))" : "" );
1067+ }
1068+ res = mailimap_custom_command (client -> imap , cmd );
1069+ if (MAILIMAP_ERROR (res )) {
1070+ client_warning ("NOTIFY SET failed\n" );
1071+ }
1072+ }
1073+
10221074 if (needunselect ) {
10231075 /* UNSELECT itself is an extension. Only do if supported. */
10241076 if (IMAP_HAS_CAPABILITY (client , IMAP_CAPABILITY_UNSELECT )) {
@@ -1325,13 +1377,31 @@ static inline void masquerade_mailbox(struct mailbox *restrict new_mbox, struct
13251377#undef COPY_MBOX_FIELD
13261378}
13271379
1380+ static int str_case_ends_with (const char * str , const char * suffix )
1381+ {
1382+ size_t str_len ;
1383+ size_t suffix_len ;
1384+
1385+ str_len = strlen (str );
1386+ suffix_len = strlen (suffix );
1387+
1388+ return str_len >= suffix_len && !strcasecmp (str + str_len - suffix_len , suffix );
1389+ }
1390+
13281391static struct mailbox * find_mailbox_by_name (struct client * client , const char * name )
13291392{
13301393 int i ;
13311394 for (i = 0 ; i < client -> num_mailboxes ; i ++ ) {
1395+ /* In IMAP, mailbox names are case-sensitive...
1396+ * (even though most server implementations are case-insensitive, we can't assume that) */
13321397 if (!strcmp (client -> mailboxes [i ].name , name )) {
13331398 return & client -> mailboxes [i ];
13341399 }
1400+ /* ... except for INBOX (RFC 3501 5.1)
1401+ * We do case-insensitive matches for INBOX, as well as any subfolder named "INBOX". */
1402+ if ((!strcasecmp (name , "INBOX" ) || str_case_ends_with (name , ".INBOX" )) && !strcasecmp (client -> mailboxes [i ].name , name )) {
1403+ return & client -> mailboxes [i ];
1404+ }
13351405 }
13361406 return NULL ;
13371407}
0 commit comments