aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/misc/gnus.texi6
-rw-r--r--etc/NEWS5
-rw-r--r--lisp/gnus/nnimap.el90
3 files changed, 74 insertions, 27 deletions
diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi
index 6793ed2e9f1..cd97cff2a05 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.
14320If non-@code{nil}, record all @acronym{IMAP} commands in the 14320If 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
14324If non-@code{nil}, omit the IMAP namespace prefix in nnimap group
14325names. If your IMAP mailboxes are called something like @samp{INBOX}
14326and @samp{INBOX.Lists.emacs}, but you'd like the nnimap group names to
14327be @samp{INBOX} and @samp{Lists.emacs}, you should enable this option.
14328
14323@end table 14329@end table
14324 14330
14325 14331
diff --git a/etc/NEWS b/etc/NEWS
index fc2a5d4c039..57b51f61b60 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -46,6 +46,11 @@ option --enable-check-lisp-object-type is therefore no longer as
46useful and so is no longer enabled by default in developer builds, 46useful and so is no longer enabled by default in developer builds,
47to reduce differences between developer and production builds. 47to reduce differences between developer and production builds.
48 48
49** Gnus
50
51+++
52*** The nnimap backend now has support for IMAP namespaces.
53
49 54
50* Startup Changes in Emacs 27.1 55* Startup Changes in Emacs 27.1
51 56
diff --git a/lisp/gnus/nnimap.el b/lisp/gnus/nnimap.el
index 3b397319272..af7899f7897 100644
--- a/lisp/gnus/nnimap.el
+++ b/lisp/gnus/nnimap.el
@@ -55,6 +55,13 @@
55If nnimap-stream is `ssl', this will default to `imaps'. If not, 55If nnimap-stream is `ssl', this will default to `imaps'. If not,
56it will default to `imap'.") 56it will default to `imap'.")
57 57
58(defvoo nnimap-use-namespaces nil
59 "Whether to use IMAP namespaces.
60If in Gnus your folder names in all start with (e.g.) `INBOX',
61you probably want to set this to t. The effects of this are
62purely cosmetical, but changing this variable will affect the
63names 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.
60The value should be either `undecided', `ssl' or `tls', 67The 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,18 @@ 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 (substring nnimap-namespace 0 -1)))
181 (utf7-encode
182 (cond ((or (not nnimap-namespace)
183 (string-equal group inbox))
184 group)
185 ((string-prefix-p "#" group)
186 (substring group 1))
187 (t
188 (concat nnimap-namespace group))) t)))
189
169(defun nnimap-buffer () 190(defun nnimap-buffer ()
170 (nnimap-find-process-buffer nntp-server-buffer)) 191 (nnimap-find-process-buffer nntp-server-buffer))
171 192
@@ -442,7 +463,8 @@ textual parts.")
442 (props (cdr stream-list)) 463 (props (cdr stream-list))
443 (greeting (plist-get props :greeting)) 464 (greeting (plist-get props :greeting))
444 (capabilities (plist-get props :capabilities)) 465 (capabilities (plist-get props :capabilities))
445 (stream-type (plist-get props :type))) 466 (stream-type (plist-get props :type))
467 (server (nnoo-current-server 'nnimap)))
446 (when (and stream (not (memq (process-status stream) '(open run)))) 468 (when (and stream (not (memq (process-status stream) '(open run))))
447 (setq stream nil)) 469 (setq stream nil))
448 470
@@ -475,9 +497,7 @@ textual parts.")
475 ;; the virtual server name and the address 497 ;; the virtual server name and the address
476 (nnimap-credentials 498 (nnimap-credentials
477 (gnus-delete-duplicates 499 (gnus-delete-duplicates
478 (list 500 (list server nnimap-address))
479 (nnoo-current-server 'nnimap)
480 nnimap-address))
481 ports 501 ports
482 nnimap-user)))) 502 nnimap-user))))
483 (setq nnimap-object nil) 503 (setq nnimap-object nil)
@@ -496,8 +516,17 @@ textual parts.")
496 (dolist (response (cddr (nnimap-command "CAPABILITY"))) 516 (dolist (response (cddr (nnimap-command "CAPABILITY")))
497 (when (string= "CAPABILITY" (upcase (car response))) 517 (when (string= "CAPABILITY" (upcase (car response)))
498 (setf (nnimap-capabilities nnimap-object) 518 (setf (nnimap-capabilities nnimap-object)
499 (mapcar #'upcase (cdr response)))))) 519 (mapcar #'upcase (cdr response)))))
500 ;; If the login failed, then forget the credentials 520 (when (and nnimap-use-namespaces
521 (nnimap-capability "NAMESPACE"))
522 (erase-buffer)
523 (nnimap-wait-for-response (nnimap-send-command "NAMESPACE"))
524 (let ((response (nnimap-last-response-string)))
525 (when (string-match
526 "^\\*\\W+NAMESPACE\\W+((\"\\([^\"\n]+\\)\"\\W+\"\\(.\\)\"))\\W+"
527 response)
528 (setq nnimap-namespace (match-string 1 response))))))
529 ;; If the login failed, then forget the credentials
501 ;; that are now possibly cached. 530 ;; that are now possibly cached.
502 (dolist (host (list (nnoo-current-server 'nnimap) 531 (dolist (host (list (nnoo-current-server 'nnimap)
503 nnimap-address)) 532 nnimap-address))
@@ -837,7 +866,7 @@ textual parts.")
837 (with-current-buffer (nnimap-buffer) 866 (with-current-buffer (nnimap-buffer)
838 (erase-buffer) 867 (erase-buffer)
839 (let ((group-sequence 868 (let ((group-sequence
840 (nnimap-send-command "SELECT %S" (utf7-encode group t))) 869 (nnimap-send-command "SELECT %S" (nnimap-group-to-imap group)))
841 (flag-sequence 870 (flag-sequence
842 (nnimap-send-command "UID FETCH 1:* FLAGS"))) 871 (nnimap-send-command "UID FETCH 1:* FLAGS")))
843 (setf (nnimap-group nnimap-object) group) 872 (setf (nnimap-group nnimap-object) group)
@@ -870,13 +899,13 @@ textual parts.")
870 (setq group (nnimap-decode-gnus-group group)) 899 (setq group (nnimap-decode-gnus-group group))
871 (when (nnimap-change-group nil server) 900 (when (nnimap-change-group nil server)
872 (with-current-buffer (nnimap-buffer) 901 (with-current-buffer (nnimap-buffer)
873 (car (nnimap-command "CREATE %S" (utf7-encode group t)))))) 902 (car (nnimap-command "CREATE %S" (nnimap-group-to-imap group))))))
874 903
875(deffoo nnimap-request-delete-group (group &optional _force server) 904(deffoo nnimap-request-delete-group (group &optional _force server)
876 (setq group (nnimap-decode-gnus-group group)) 905 (setq group (nnimap-decode-gnus-group group))
877 (when (nnimap-change-group nil server) 906 (when (nnimap-change-group nil server)
878 (with-current-buffer (nnimap-buffer) 907 (with-current-buffer (nnimap-buffer)
879 (car (nnimap-command "DELETE %S" (utf7-encode group t)))))) 908 (car (nnimap-command "DELETE %S" (nnimap-group-to-imap group))))))
880 909
881(deffoo nnimap-request-rename-group (group new-name &optional server) 910(deffoo nnimap-request-rename-group (group new-name &optional server)
882 (setq group (nnimap-decode-gnus-group group)) 911 (setq group (nnimap-decode-gnus-group group))
@@ -884,7 +913,7 @@ textual parts.")
884 (with-current-buffer (nnimap-buffer) 913 (with-current-buffer (nnimap-buffer)
885 (nnimap-unselect-group) 914 (nnimap-unselect-group)
886 (car (nnimap-command "RENAME %S %S" 915 (car (nnimap-command "RENAME %S %S"
887 (utf7-encode group t) (utf7-encode new-name t)))))) 916 (nnimap-group-to-imap group) (nnimap-group-to-imap new-name))))))
888 917
889(defun nnimap-unselect-group () 918(defun nnimap-unselect-group ()
890 ;; Make sure we don't have this group open read/write by asking 919 ;; Make sure we don't have this group open read/write by asking
@@ -944,7 +973,7 @@ textual parts.")
944 "UID COPY %d %S")) 973 "UID COPY %d %S"))
945 (result (nnimap-command 974 (result (nnimap-command
946 command article 975 command article
947 (utf7-encode internal-move-group t)))) 976 (nnimap-group-to-imap internal-move-group))))
948 (when (and (car result) (not can-move)) 977 (when (and (car result) (not can-move))
949 (nnimap-delete-article article)) 978 (nnimap-delete-article article))
950 (cons internal-move-group 979 (cons internal-move-group
@@ -1011,7 +1040,7 @@ textual parts.")
1011 "UID MOVE %s %S" 1040 "UID MOVE %s %S"
1012 "UID COPY %s %S") 1041 "UID COPY %s %S")
1013 (nnimap-article-ranges (gnus-compress-sequence articles)) 1042 (nnimap-article-ranges (gnus-compress-sequence articles))
1014 (utf7-encode (gnus-group-real-name nnmail-expiry-target) t)) 1043 (nnimap-group-to-imap (gnus-group-real-name nnmail-expiry-target)))
1015 (set (if can-move 'deleted-articles 'articles-to-delete) articles)))) 1044 (set (if can-move 'deleted-articles 'articles-to-delete) articles))))
1016 t) 1045 t)
1017 (t 1046 (t
@@ -1136,7 +1165,7 @@ If LIMIT, first try to limit the search to the N last articles."
1136 (unsubscribe "UNSUBSCRIBE"))))) 1165 (unsubscribe "UNSUBSCRIBE")))))
1137 (when command 1166 (when command
1138 (with-current-buffer (nnimap-buffer) 1167 (with-current-buffer (nnimap-buffer)
1139 (nnimap-command "%s %S" (cadr command) (utf7-encode group t))))))) 1168 (nnimap-command "%s %S" (cadr command) (nnimap-group-to-imap group)))))))
1140 1169
1141(deffoo nnimap-request-set-mark (group actions &optional server) 1170(deffoo nnimap-request-set-mark (group actions &optional server)
1142 (setq group (nnimap-decode-gnus-group group)) 1171 (setq group (nnimap-decode-gnus-group group))
@@ -1191,7 +1220,7 @@ If LIMIT, first try to limit the search to the N last articles."
1191 (nnimap-unselect-group)) 1220 (nnimap-unselect-group))
1192 (erase-buffer) 1221 (erase-buffer)
1193 (setq sequence (nnimap-send-command 1222 (setq sequence (nnimap-send-command
1194 "APPEND %S {%d}" (utf7-encode group t) 1223 "APPEND %S {%d}" (nnimap-group-to-imap group)
1195 (length message))) 1224 (length message)))
1196 (unless nnimap-streaming 1225 (unless nnimap-streaming
1197 (nnimap-wait-for-connection "^[+]")) 1226 (nnimap-wait-for-connection "^[+]"))
@@ -1271,8 +1300,11 @@ If LIMIT, first try to limit the search to the N last articles."
1271 1300
1272(defun nnimap-get-groups () 1301(defun nnimap-get-groups ()
1273 (erase-buffer) 1302 (erase-buffer)
1274 (let ((sequence (nnimap-send-command "LIST \"\" \"*\"")) 1303 (let* ((sequence (nnimap-send-command "LIST \"\" \"*\""))
1275 groups) 1304 (prefix nnimap-namespace)
1305 (prefix-len (length prefix))
1306 (inbox (substring prefix 0 -1))
1307 groups)
1276 (nnimap-wait-for-response sequence) 1308 (nnimap-wait-for-response sequence)
1277 (subst-char-in-region (point-min) (point-max) 1309 (subst-char-in-region (point-min) (point-max)
1278 ?\\ ?% t) 1310 ?\\ ?% t)
@@ -1289,11 +1321,15 @@ If LIMIT, first try to limit the search to the N last articles."
1289 (skip-chars-backward " \r\"") 1321 (skip-chars-backward " \r\"")
1290 (point))))) 1322 (point)))))
1291 (unless (member '%NoSelect flags) 1323 (unless (member '%NoSelect flags)
1292 (push (utf7-decode (if (stringp group) 1324 (let* ((group (utf7-decode (if (stringp group) group
1293 group 1325 (format "%s" group)) t))
1294 (format "%s" group)) 1326 (group (cond ((equal inbox group)
1295 t) 1327 group)
1296 groups)))) 1328 ((string-prefix-p prefix group)
1329 (substring group prefix-len))
1330 (t
1331 (concat "#" group)))))
1332 (push group groups)))))
1297 (nreverse groups))) 1333 (nreverse groups)))
1298 1334
1299(defun nnimap-get-responses (sequences) 1335(defun nnimap-get-responses (sequences)
@@ -1319,7 +1355,7 @@ If LIMIT, first try to limit the search to the N last articles."
1319 (dolist (group groups) 1355 (dolist (group groups)
1320 (setf (nnimap-examined nnimap-object) group) 1356 (setf (nnimap-examined nnimap-object) group)
1321 (push (list (nnimap-send-command "EXAMINE %S" 1357 (push (list (nnimap-send-command "EXAMINE %S"
1322 (utf7-encode group t)) 1358 (nnimap-group-to-imap group))
1323 group) 1359 group)
1324 sequences)) 1360 sequences))
1325 (nnimap-wait-for-response (caar sequences)) 1361 (nnimap-wait-for-response (caar sequences))
@@ -1391,7 +1427,7 @@ If LIMIT, first try to limit the search to the N last articles."
1391 unexist) 1427 unexist)
1392 (push 1428 (push
1393 (list (nnimap-send-command "EXAMINE %S (%s (%s %s))" 1429 (list (nnimap-send-command "EXAMINE %S (%s (%s %s))"
1394 (utf7-encode group t) 1430 (nnimap-group-to-imap group)
1395 (nnimap-quirk "QRESYNC") 1431 (nnimap-quirk "QRESYNC")
1396 uidvalidity modseq) 1432 uidvalidity modseq)
1397 'qresync 1433 'qresync
@@ -1413,7 +1449,7 @@ If LIMIT, first try to limit the search to the N last articles."
1413 (cl-incf (nnimap-initial-resync nnimap-object)) 1449 (cl-incf (nnimap-initial-resync nnimap-object))
1414 (setq start 1)) 1450 (setq start 1))
1415 (push (list (nnimap-send-command "%s %S" command 1451 (push (list (nnimap-send-command "%s %S" command
1416 (utf7-encode group t)) 1452 (nnimap-group-to-imap group))
1417 (nnimap-send-command "UID FETCH %d:* FLAGS" start) 1453 (nnimap-send-command "UID FETCH %d:* FLAGS" start)
1418 start group command) 1454 start group command)
1419 sequences)))) 1455 sequences))))
@@ -1847,7 +1883,7 @@ Return the server's response to the SELECT or EXAMINE command."
1847 (if read-only 1883 (if read-only
1848 "EXAMINE" 1884 "EXAMINE"
1849 "SELECT") 1885 "SELECT")
1850 (utf7-encode group t)))) 1886 (nnimap-group-to-imap group))))
1851 (when (car result) 1887 (when (car result)
1852 (setf (nnimap-group nnimap-object) group 1888 (setf (nnimap-group nnimap-object) group
1853 (nnimap-select-result nnimap-object) result) 1889 (nnimap-select-result nnimap-object) result)
@@ -2105,7 +2141,7 @@ Return the server's response to the SELECT or EXAMINE command."
2105 (dolist (spec specs) 2141 (dolist (spec specs)
2106 (when (and (not (member (car spec) groups)) 2142 (when (and (not (member (car spec) groups))
2107 (not (eq (car spec) 'junk))) 2143 (not (eq (car spec) 'junk)))
2108 (nnimap-command "CREATE %S" (utf7-encode (car spec) t)))) 2144 (nnimap-command "CREATE %S" (nnimap-group-to-imap (car spec)))))
2109 ;; Then copy over all the messages. 2145 ;; Then copy over all the messages.
2110 (erase-buffer) 2146 (erase-buffer)
2111 (dolist (spec specs) 2147 (dolist (spec specs)
@@ -2121,7 +2157,7 @@ Return the server's response to the SELECT or EXAMINE command."
2121 "UID MOVE %s %S" 2157 "UID MOVE %s %S"
2122 "UID COPY %s %S") 2158 "UID COPY %s %S")
2123 (nnimap-article-ranges ranges) 2159 (nnimap-article-ranges ranges)
2124 (utf7-encode group t)) 2160 (nnimap-group-to-imap group))
2125 ranges) 2161 ranges)
2126 sequences))))) 2162 sequences)))))
2127 ;; Wait for the last COPY response... 2163 ;; Wait for the last COPY response...