diff options
| author | Nikolaus Rath | 2018-07-23 10:21:46 +0100 |
|---|---|---|
| committer | Eli Zaretskii | 2018-08-11 10:46:02 +0300 |
| commit | 31263d67d591cf2c074fad4f17b968b87c88b5e2 (patch) | |
| tree | 27805e891b106956a6a4b0b592817557ad9efe1a | |
| parent | 3f8324e0de182945a809f63766cf9611aa45610c (diff) | |
| download | emacs-31263d67d591cf2c074fad4f17b968b87c88b5e2.tar.gz emacs-31263d67d591cf2c074fad4f17b968b87c88b5e2.zip | |
Make nnimap support IMAP namespaces
* lisp/gnus/nnimap.el (nnimap-use-namespaces): Introduce new
server variable.
(nnimap-group-to-imap, nnimap-get-groups): Transform IMAP group
names to Gnus group name by stripping / prefixing personal
namespace prefix.
(nnimap-open-connection-1): Ask server for namespaces and store
them.
* lisp/gnus/nnimap.el (nnimap-request-group-scan)
(nnimap-request-create-group, nnimap-request-delete-group)
(nnimap-request-rename-group, nnimap-request-move-article)
(nnimap-process-expiry-targets)
(nnimap-request-update-group-status)
(nnimap-request-accept-article, nnimap-request-list)
(nnimap-retrieve-group-data-early, nnimap-change-group)
(nnimap-split-incoming-mail): Use nnimap-group-to-imap.
(nnimap-group-to-imap): New function to map Gnus group names to
IMAP folder names. (Bug#21057)
| -rw-r--r-- | doc/misc/gnus.texi | 6 | ||||
| -rw-r--r-- | etc/NEWS | 7 | ||||
| -rw-r--r-- | lisp/gnus/nnimap.el | 93 |
3 files changed, 79 insertions, 27 deletions
diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi index 6793ed2e9f1..6ccb9e55f31 100644 --- a/doc/misc/gnus.texi +++ b/doc/misc/gnus.texi | |||
| @@ -14320,6 +14320,12 @@ fetch all textual parts, while leaving the rest on the server. | |||
| 14320 | If non-@code{nil}, record all @acronym{IMAP} commands in the | 14320 | If non-@code{nil}, record all @acronym{IMAP} commands in the |
| 14321 | @samp{"*imap log*"} buffer. | 14321 | @samp{"*imap log*"} buffer. |
| 14322 | 14322 | ||
| 14323 | @item nnimap-use-namespaces | ||
| 14324 | If non-@code{nil}, omit the IMAP namespace prefix in nnimap group | ||
| 14325 | names. If your IMAP mailboxes are called something like @samp{INBOX} | ||
| 14326 | and @samp{INBOX.Lists.emacs}, but you'd like the nnimap group names to | ||
| 14327 | be @samp{INBOX} and @samp{Lists.emacs}, you should enable this option. | ||
| 14328 | |||
| 14323 | @end table | 14329 | @end table |
| 14324 | 14330 | ||
| 14325 | 14331 | ||
| @@ -53,6 +53,13 @@ option --enable-check-lisp-object-type is therefore no longer as | |||
| 53 | useful and so is no longer enabled by default in developer builds, | 53 | useful and so is no longer enabled by default in developer builds, |
| 54 | to reduce differences between developer and production builds. | 54 | to reduce differences between developer and production builds. |
| 55 | 55 | ||
| 56 | ** Gnus | ||
| 57 | |||
| 58 | +++ | ||
| 59 | *** The nnimap backend now has support for IMAP namespaces. | ||
| 60 | This feature can be enabled by setting the new 'nnimap-use-namespaces' | ||
| 61 | server variable to non-nil. | ||
| 62 | |||
| 56 | 63 | ||
| 57 | * Startup Changes in Emacs 27.1 | 64 | * Startup Changes in Emacs 27.1 |
| 58 | 65 | ||
diff --git a/lisp/gnus/nnimap.el b/lisp/gnus/nnimap.el index 3b397319272..12892c516a7 100644 --- a/lisp/gnus/nnimap.el +++ b/lisp/gnus/nnimap.el | |||
| @@ -55,6 +55,13 @@ | |||
| 55 | If nnimap-stream is `ssl', this will default to `imaps'. If not, | 55 | If nnimap-stream is `ssl', this will default to `imaps'. If not, |
| 56 | it will default to `imap'.") | 56 | it will default to `imap'.") |
| 57 | 57 | ||
| 58 | (defvoo nnimap-use-namespaces nil | ||
| 59 | "Whether to use IMAP namespaces. | ||
| 60 | If in Gnus your folder names in all start with (e.g.) `INBOX', | ||
| 61 | you probably want to set this to t. The effects of this are | ||
| 62 | purely cosmetic, but changing this variable will affect the | ||
| 63 | names of your nnimap groups. ") | ||
| 64 | |||
| 58 | (defvoo nnimap-stream 'undecided | 65 | (defvoo nnimap-stream 'undecided |
| 59 | "How nnimap talks to the IMAP server. | 66 | "How nnimap talks to the IMAP server. |
| 60 | The value should be either `undecided', `ssl' or `tls', | 67 | The value should be either `undecided', `ssl' or `tls', |
| @@ -110,6 +117,8 @@ some servers.") | |||
| 110 | 117 | ||
| 111 | (defvoo nnimap-current-infos nil) | 118 | (defvoo nnimap-current-infos nil) |
| 112 | 119 | ||
| 120 | (defvoo nnimap-namespace nil) | ||
| 121 | |||
| 113 | (defun nnimap-decode-gnus-group (group) | 122 | (defun nnimap-decode-gnus-group (group) |
| 114 | (decode-coding-string group 'utf-8)) | 123 | (decode-coding-string group 'utf-8)) |
| 115 | 124 | ||
| @@ -166,6 +175,19 @@ textual parts.") | |||
| 166 | 175 | ||
| 167 | (defvar nnimap-inhibit-logging nil) | 176 | (defvar nnimap-inhibit-logging nil) |
| 168 | 177 | ||
| 178 | (defun nnimap-group-to-imap (group) | ||
| 179 | "Convert Gnus group name to IMAP mailbox name." | ||
| 180 | (let* ((inbox (if nnimap-namespace | ||
| 181 | (substring nnimap-namespace 0 -1) nil))) | ||
| 182 | (utf7-encode | ||
| 183 | (cond ((or (not inbox) | ||
| 184 | (string-equal group inbox)) | ||
| 185 | group) | ||
| 186 | ((string-prefix-p "#" group) | ||
| 187 | (substring group 1)) | ||
| 188 | (t | ||
| 189 | (concat nnimap-namespace group))) t))) | ||
| 190 | |||
| 169 | (defun nnimap-buffer () | 191 | (defun nnimap-buffer () |
| 170 | (nnimap-find-process-buffer nntp-server-buffer)) | 192 | (nnimap-find-process-buffer nntp-server-buffer)) |
| 171 | 193 | ||
| @@ -442,7 +464,8 @@ textual parts.") | |||
| 442 | (props (cdr stream-list)) | 464 | (props (cdr stream-list)) |
| 443 | (greeting (plist-get props :greeting)) | 465 | (greeting (plist-get props :greeting)) |
| 444 | (capabilities (plist-get props :capabilities)) | 466 | (capabilities (plist-get props :capabilities)) |
| 445 | (stream-type (plist-get props :type))) | 467 | (stream-type (plist-get props :type)) |
| 468 | (server (nnoo-current-server 'nnimap))) | ||
| 446 | (when (and stream (not (memq (process-status stream) '(open run)))) | 469 | (when (and stream (not (memq (process-status stream) '(open run)))) |
| 447 | (setq stream nil)) | 470 | (setq stream nil)) |
| 448 | 471 | ||
| @@ -475,9 +498,7 @@ textual parts.") | |||
| 475 | ;; the virtual server name and the address | 498 | ;; the virtual server name and the address |
| 476 | (nnimap-credentials | 499 | (nnimap-credentials |
| 477 | (gnus-delete-duplicates | 500 | (gnus-delete-duplicates |
| 478 | (list | 501 | (list server nnimap-address)) |
| 479 | (nnoo-current-server 'nnimap) | ||
| 480 | nnimap-address)) | ||
| 481 | ports | 502 | ports |
| 482 | nnimap-user)))) | 503 | nnimap-user)))) |
| 483 | (setq nnimap-object nil) | 504 | (setq nnimap-object nil) |
| @@ -496,8 +517,17 @@ textual parts.") | |||
| 496 | (dolist (response (cddr (nnimap-command "CAPABILITY"))) | 517 | (dolist (response (cddr (nnimap-command "CAPABILITY"))) |
| 497 | (when (string= "CAPABILITY" (upcase (car response))) | 518 | (when (string= "CAPABILITY" (upcase (car response))) |
| 498 | (setf (nnimap-capabilities nnimap-object) | 519 | (setf (nnimap-capabilities nnimap-object) |
| 499 | (mapcar #'upcase (cdr response)))))) | 520 | (mapcar #'upcase (cdr response))))) |
| 500 | ;; If the login failed, then forget the credentials | 521 | (when (and nnimap-use-namespaces |
| 522 | (nnimap-capability "NAMESPACE")) | ||
| 523 | (erase-buffer) | ||
| 524 | (nnimap-wait-for-response (nnimap-send-command "NAMESPACE")) | ||
| 525 | (let ((response (nnimap-last-response-string))) | ||
| 526 | (when (string-match | ||
| 527 | "^\\*\\W+NAMESPACE\\W+((\"\\([^\"\n]+\\)\"\\W+\"\\(.\\)\"))\\W+" | ||
| 528 | response) | ||
| 529 | (setq nnimap-namespace (match-string 1 response)))))) | ||
| 530 | ;; If the login failed, then forget the credentials | ||
| 501 | ;; that are now possibly cached. | 531 | ;; that are now possibly cached. |
| 502 | (dolist (host (list (nnoo-current-server 'nnimap) | 532 | (dolist (host (list (nnoo-current-server 'nnimap) |
| 503 | nnimap-address)) | 533 | nnimap-address)) |
| @@ -837,7 +867,7 @@ textual parts.") | |||
| 837 | (with-current-buffer (nnimap-buffer) | 867 | (with-current-buffer (nnimap-buffer) |
| 838 | (erase-buffer) | 868 | (erase-buffer) |
| 839 | (let ((group-sequence | 869 | (let ((group-sequence |
| 840 | (nnimap-send-command "SELECT %S" (utf7-encode group t))) | 870 | (nnimap-send-command "SELECT %S" (nnimap-group-to-imap group))) |
| 841 | (flag-sequence | 871 | (flag-sequence |
| 842 | (nnimap-send-command "UID FETCH 1:* FLAGS"))) | 872 | (nnimap-send-command "UID FETCH 1:* FLAGS"))) |
| 843 | (setf (nnimap-group nnimap-object) group) | 873 | (setf (nnimap-group nnimap-object) group) |
| @@ -870,13 +900,13 @@ textual parts.") | |||
| 870 | (setq group (nnimap-decode-gnus-group group)) | 900 | (setq group (nnimap-decode-gnus-group group)) |
| 871 | (when (nnimap-change-group nil server) | 901 | (when (nnimap-change-group nil server) |
| 872 | (with-current-buffer (nnimap-buffer) | 902 | (with-current-buffer (nnimap-buffer) |
| 873 | (car (nnimap-command "CREATE %S" (utf7-encode group t)))))) | 903 | (car (nnimap-command "CREATE %S" (nnimap-group-to-imap group)))))) |
| 874 | 904 | ||
| 875 | (deffoo nnimap-request-delete-group (group &optional _force server) | 905 | (deffoo nnimap-request-delete-group (group &optional _force server) |
| 876 | (setq group (nnimap-decode-gnus-group group)) | 906 | (setq group (nnimap-decode-gnus-group group)) |
| 877 | (when (nnimap-change-group nil server) | 907 | (when (nnimap-change-group nil server) |
| 878 | (with-current-buffer (nnimap-buffer) | 908 | (with-current-buffer (nnimap-buffer) |
| 879 | (car (nnimap-command "DELETE %S" (utf7-encode group t)))))) | 909 | (car (nnimap-command "DELETE %S" (nnimap-group-to-imap group)))))) |
| 880 | 910 | ||
| 881 | (deffoo nnimap-request-rename-group (group new-name &optional server) | 911 | (deffoo nnimap-request-rename-group (group new-name &optional server) |
| 882 | (setq group (nnimap-decode-gnus-group group)) | 912 | (setq group (nnimap-decode-gnus-group group)) |
| @@ -884,7 +914,7 @@ textual parts.") | |||
| 884 | (with-current-buffer (nnimap-buffer) | 914 | (with-current-buffer (nnimap-buffer) |
| 885 | (nnimap-unselect-group) | 915 | (nnimap-unselect-group) |
| 886 | (car (nnimap-command "RENAME %S %S" | 916 | (car (nnimap-command "RENAME %S %S" |
| 887 | (utf7-encode group t) (utf7-encode new-name t)))))) | 917 | (nnimap-group-to-imap group) (nnimap-group-to-imap new-name)))))) |
| 888 | 918 | ||
| 889 | (defun nnimap-unselect-group () | 919 | (defun nnimap-unselect-group () |
| 890 | ;; Make sure we don't have this group open read/write by asking | 920 | ;; Make sure we don't have this group open read/write by asking |
| @@ -944,7 +974,7 @@ textual parts.") | |||
| 944 | "UID COPY %d %S")) | 974 | "UID COPY %d %S")) |
| 945 | (result (nnimap-command | 975 | (result (nnimap-command |
| 946 | command article | 976 | command article |
| 947 | (utf7-encode internal-move-group t)))) | 977 | (nnimap-group-to-imap internal-move-group)))) |
| 948 | (when (and (car result) (not can-move)) | 978 | (when (and (car result) (not can-move)) |
| 949 | (nnimap-delete-article article)) | 979 | (nnimap-delete-article article)) |
| 950 | (cons internal-move-group | 980 | (cons internal-move-group |
| @@ -1011,7 +1041,7 @@ textual parts.") | |||
| 1011 | "UID MOVE %s %S" | 1041 | "UID MOVE %s %S" |
| 1012 | "UID COPY %s %S") | 1042 | "UID COPY %s %S") |
| 1013 | (nnimap-article-ranges (gnus-compress-sequence articles)) | 1043 | (nnimap-article-ranges (gnus-compress-sequence articles)) |
| 1014 | (utf7-encode (gnus-group-real-name nnmail-expiry-target) t)) | 1044 | (nnimap-group-to-imap (gnus-group-real-name nnmail-expiry-target))) |
| 1015 | (set (if can-move 'deleted-articles 'articles-to-delete) articles)))) | 1045 | (set (if can-move 'deleted-articles 'articles-to-delete) articles)))) |
| 1016 | t) | 1046 | t) |
| 1017 | (t | 1047 | (t |
| @@ -1136,7 +1166,7 @@ If LIMIT, first try to limit the search to the N last articles." | |||
| 1136 | (unsubscribe "UNSUBSCRIBE"))))) | 1166 | (unsubscribe "UNSUBSCRIBE"))))) |
| 1137 | (when command | 1167 | (when command |
| 1138 | (with-current-buffer (nnimap-buffer) | 1168 | (with-current-buffer (nnimap-buffer) |
| 1139 | (nnimap-command "%s %S" (cadr command) (utf7-encode group t))))))) | 1169 | (nnimap-command "%s %S" (cadr command) (nnimap-group-to-imap group))))))) |
| 1140 | 1170 | ||
| 1141 | (deffoo nnimap-request-set-mark (group actions &optional server) | 1171 | (deffoo nnimap-request-set-mark (group actions &optional server) |
| 1142 | (setq group (nnimap-decode-gnus-group group)) | 1172 | (setq group (nnimap-decode-gnus-group group)) |
| @@ -1191,7 +1221,7 @@ If LIMIT, first try to limit the search to the N last articles." | |||
| 1191 | (nnimap-unselect-group)) | 1221 | (nnimap-unselect-group)) |
| 1192 | (erase-buffer) | 1222 | (erase-buffer) |
| 1193 | (setq sequence (nnimap-send-command | 1223 | (setq sequence (nnimap-send-command |
| 1194 | "APPEND %S {%d}" (utf7-encode group t) | 1224 | "APPEND %S {%d}" (nnimap-group-to-imap group) |
| 1195 | (length message))) | 1225 | (length message))) |
| 1196 | (unless nnimap-streaming | 1226 | (unless nnimap-streaming |
| 1197 | (nnimap-wait-for-connection "^[+]")) | 1227 | (nnimap-wait-for-connection "^[+]")) |
| @@ -1271,8 +1301,12 @@ If LIMIT, first try to limit the search to the N last articles." | |||
| 1271 | 1301 | ||
| 1272 | (defun nnimap-get-groups () | 1302 | (defun nnimap-get-groups () |
| 1273 | (erase-buffer) | 1303 | (erase-buffer) |
| 1274 | (let ((sequence (nnimap-send-command "LIST \"\" \"*\"")) | 1304 | (let* ((sequence (nnimap-send-command "LIST \"\" \"*\"")) |
| 1275 | groups) | 1305 | (prefix nnimap-namespace) |
| 1306 | (prefix-len (if prefix (length prefix) nil)) | ||
| 1307 | (inbox (if prefix | ||
| 1308 | (substring prefix 0 -1) nil)) | ||
| 1309 | groups) | ||
| 1276 | (nnimap-wait-for-response sequence) | 1310 | (nnimap-wait-for-response sequence) |
| 1277 | (subst-char-in-region (point-min) (point-max) | 1311 | (subst-char-in-region (point-min) (point-max) |
| 1278 | ?\\ ?% t) | 1312 | ?\\ ?% t) |
| @@ -1289,11 +1323,16 @@ If LIMIT, first try to limit the search to the N last articles." | |||
| 1289 | (skip-chars-backward " \r\"") | 1323 | (skip-chars-backward " \r\"") |
| 1290 | (point))))) | 1324 | (point))))) |
| 1291 | (unless (member '%NoSelect flags) | 1325 | (unless (member '%NoSelect flags) |
| 1292 | (push (utf7-decode (if (stringp group) | 1326 | (let* ((group (utf7-decode (if (stringp group) group |
| 1293 | group | 1327 | (format "%s" group)) t)) |
| 1294 | (format "%s" group)) | 1328 | (group (cond ((or (not prefix) |
| 1295 | t) | 1329 | (equal inbox group)) |
| 1296 | groups)))) | 1330 | group) |
| 1331 | ((string-prefix-p prefix group) | ||
| 1332 | (substring group prefix-len)) | ||
| 1333 | (t | ||
| 1334 | (concat "#" group))))) | ||
| 1335 | (push group groups))))) | ||
| 1297 | (nreverse groups))) | 1336 | (nreverse groups))) |
| 1298 | 1337 | ||
| 1299 | (defun nnimap-get-responses (sequences) | 1338 | (defun nnimap-get-responses (sequences) |
| @@ -1319,7 +1358,7 @@ If LIMIT, first try to limit the search to the N last articles." | |||
| 1319 | (dolist (group groups) | 1358 | (dolist (group groups) |
| 1320 | (setf (nnimap-examined nnimap-object) group) | 1359 | (setf (nnimap-examined nnimap-object) group) |
| 1321 | (push (list (nnimap-send-command "EXAMINE %S" | 1360 | (push (list (nnimap-send-command "EXAMINE %S" |
| 1322 | (utf7-encode group t)) | 1361 | (nnimap-group-to-imap group)) |
| 1323 | group) | 1362 | group) |
| 1324 | sequences)) | 1363 | sequences)) |
| 1325 | (nnimap-wait-for-response (caar sequences)) | 1364 | (nnimap-wait-for-response (caar sequences)) |
| @@ -1391,7 +1430,7 @@ If LIMIT, first try to limit the search to the N last articles." | |||
| 1391 | unexist) | 1430 | unexist) |
| 1392 | (push | 1431 | (push |
| 1393 | (list (nnimap-send-command "EXAMINE %S (%s (%s %s))" | 1432 | (list (nnimap-send-command "EXAMINE %S (%s (%s %s))" |
| 1394 | (utf7-encode group t) | 1433 | (nnimap-group-to-imap group) |
| 1395 | (nnimap-quirk "QRESYNC") | 1434 | (nnimap-quirk "QRESYNC") |
| 1396 | uidvalidity modseq) | 1435 | uidvalidity modseq) |
| 1397 | 'qresync | 1436 | 'qresync |
| @@ -1413,7 +1452,7 @@ If LIMIT, first try to limit the search to the N last articles." | |||
| 1413 | (cl-incf (nnimap-initial-resync nnimap-object)) | 1452 | (cl-incf (nnimap-initial-resync nnimap-object)) |
| 1414 | (setq start 1)) | 1453 | (setq start 1)) |
| 1415 | (push (list (nnimap-send-command "%s %S" command | 1454 | (push (list (nnimap-send-command "%s %S" command |
| 1416 | (utf7-encode group t)) | 1455 | (nnimap-group-to-imap group)) |
| 1417 | (nnimap-send-command "UID FETCH %d:* FLAGS" start) | 1456 | (nnimap-send-command "UID FETCH %d:* FLAGS" start) |
| 1418 | start group command) | 1457 | start group command) |
| 1419 | sequences)))) | 1458 | sequences)))) |
| @@ -1847,7 +1886,7 @@ Return the server's response to the SELECT or EXAMINE command." | |||
| 1847 | (if read-only | 1886 | (if read-only |
| 1848 | "EXAMINE" | 1887 | "EXAMINE" |
| 1849 | "SELECT") | 1888 | "SELECT") |
| 1850 | (utf7-encode group t)))) | 1889 | (nnimap-group-to-imap group)))) |
| 1851 | (when (car result) | 1890 | (when (car result) |
| 1852 | (setf (nnimap-group nnimap-object) group | 1891 | (setf (nnimap-group nnimap-object) group |
| 1853 | (nnimap-select-result nnimap-object) result) | 1892 | (nnimap-select-result nnimap-object) result) |
| @@ -2105,7 +2144,7 @@ Return the server's response to the SELECT or EXAMINE command." | |||
| 2105 | (dolist (spec specs) | 2144 | (dolist (spec specs) |
| 2106 | (when (and (not (member (car spec) groups)) | 2145 | (when (and (not (member (car spec) groups)) |
| 2107 | (not (eq (car spec) 'junk))) | 2146 | (not (eq (car spec) 'junk))) |
| 2108 | (nnimap-command "CREATE %S" (utf7-encode (car spec) t)))) | 2147 | (nnimap-command "CREATE %S" (nnimap-group-to-imap (car spec))))) |
| 2109 | ;; Then copy over all the messages. | 2148 | ;; Then copy over all the messages. |
| 2110 | (erase-buffer) | 2149 | (erase-buffer) |
| 2111 | (dolist (spec specs) | 2150 | (dolist (spec specs) |
| @@ -2121,7 +2160,7 @@ Return the server's response to the SELECT or EXAMINE command." | |||
| 2121 | "UID MOVE %s %S" | 2160 | "UID MOVE %s %S" |
| 2122 | "UID COPY %s %S") | 2161 | "UID COPY %s %S") |
| 2123 | (nnimap-article-ranges ranges) | 2162 | (nnimap-article-ranges ranges) |
| 2124 | (utf7-encode group t)) | 2163 | (nnimap-group-to-imap group)) |
| 2125 | ranges) | 2164 | ranges) |
| 2126 | sequences))))) | 2165 | sequences))))) |
| 2127 | ;; Wait for the last COPY response... | 2166 | ;; Wait for the last COPY response... |