From mboxrd@z Thu Jan 1 00:00:00 1970 X-Msuck: nntp://news.gmane.io/gmane.emacs.gnus.general/86072 Path: news.gmane.org!not-for-mail From: Nikolaus Rath Newsgroups: gmane.emacs.devel,gmane.emacs.gnus.general Subject: bug#21057: [PATCH] nnimap.el: add support for IMAP namespaces Date: Sun, 12 Jul 2015 18:52:40 -0700 Message-ID: <87si8qjhca.fsf@vostro.rath.org> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: ger.gmane.org 1436926644 13630 80.91.229.3 (15 Jul 2015 02:17:24 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Wed, 15 Jul 2015 02:17:24 +0000 (UTC) Cc: ding@gnus.org, emacs-devel@gnu.org To: 21057@debbugs.gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Wed Jul 15 04:17:16 2015 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1ZFCG1-0006TJ-SC for ged-emacs-devel@m.gmane.org; Wed, 15 Jul 2015 04:17:14 +0200 Original-Received: from localhost ([::1]:33511 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZFCG0-0000yK-MK for ged-emacs-devel@m.gmane.org; Tue, 14 Jul 2015 22:17:12 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:43643) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZFCFu-0000xx-VL for emacs-devel@gnu.org; Tue, 14 Jul 2015 22:17:08 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZFCFr-0005Xm-H3 for emacs-devel@gnu.org; Tue, 14 Jul 2015 22:17:06 -0400 Original-Received: from debbugs.gnu.org ([140.186.70.43]:48005) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZFCFr-0005XS-CQ for emacs-devel@gnu.org; Tue, 14 Jul 2015 22:17:03 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.80) (envelope-from ) id 1ZFCFq-0007Pu-Hj; Tue, 14 Jul 2015 22:17:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Nikolaus Rath Original-Sender: "Debbugs-submit" Resent-CC: ding@gnus.org, emacs-devel@gnu.org, bugs@gnus.org Resent-Date: Wed, 15 Jul 2015 02:17:02 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: report 21057 X-GNU-PR-Package: gnus X-GNU-PR-Keywords: patch X-Debbugs-Original-To: submit@debbugs.gnu.org X-Debbugs-Original-Xcc: ding@gnus.org, emacs-devel@gnu.org Original-Received: via spool by submit@debbugs.gnu.org id=B.143692658728463 (code B ref -1); Wed, 15 Jul 2015 02:17:02 +0000 Original-Received: (at submit) by debbugs.gnu.org; 15 Jul 2015 02:16:27 +0000 Original-Received: from localhost ([127.0.0.1]:49450 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1ZFCFG-0007Oz-LP for submit@debbugs.gnu.org; Tue, 14 Jul 2015 22:16:27 -0400 Original-Received: from ebox.rath.org ([23.92.25.96]:48870) by debbugs.gnu.org with esmtp (Exim 4.80) (envelope-from ) id 1ZFCFC-0007Op-Tf for submit@debbugs.gnu.org; Tue, 14 Jul 2015 22:16:25 -0400 Original-Received: from vostro ([192.168.12.4] helo=vostro.rath.org) by ebox.rath.org with esmtps (TLSv1.2:DHE-RSA-AES256-GCM-SHA384:256) (Exim 4.80) (envelope-from ) id 1ZFCFB-0004Wq-QV for submit@debbugs.gnu.org; Wed, 15 Jul 2015 02:16:21 +0000 Original-Received: by vostro.rath.org (Postfix, from userid 1000) id 6649FEC6347; Tue, 14 Jul 2015 19:16:21 -0700 (PDT) User-Agent: Gnus/5.130014 (Ma Gnus v0.14) Emacs/24.4 (gnu/linux) X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.15 Precedence: list X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x X-Received-From: 140.186.70.43 X-BeenThere: emacs-devel@gnu.org List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:187875 gmane.emacs.gnus.general:86072 Archived-At: --=-=-= Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Package: gnus Tags: patch Hello, The attached patch adds basic support for IMAP namespaces to nnimap. If the new server variable nnimap-use-namespaces is t, Gnus will attempt to determine the user's personal namespace (e.g. "INBOX."). When reading the list of mail folders, this prefix will be stripped. If there are folders outside the users personal namespace, they will be prefixed with the breakout character (#). This is useful for servers where all personal mailboxes appear is subfolders of the user's INBOX. For example, before the patch one would have: nnimap+fm:INBOX nnimap+fm:INBOX.Sent Items nnimap+fm:INBOX.Drafts nnimap+fm:INBOX.Archive nnimap+fm:Shared.Bob.INBOX and afterwards: nnimap+fm:INBOX nnimap+fm:Sent Items nnimap+fm:Drafts nnimap+fm:Archive nnimap+fm:#Shared.Bob.INBOX I have only tested this on my personal account, so feedback is appreciated.=20 Thanks for considering, -Nikolaus --=20 GPG encrypted emails preferred. Key id: 0xD113FCAC3C4E599F Fingerprint: ED31 791B 2C5C 1613 AF38 8B8A D113 FCAC 3C4E 599F =C2=BBTime flies like an arrow, fruit flies like a Banana.=C2= =AB --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-nnimap.el-factor-out-nnimap-group-to-imap.patch >From cd74921ce8e867dfe056d6aa94431269460822e2 Mon Sep 17 00:00:00 2001 From: Nikolaus Rath Date: Sun, 12 Jul 2015 11:10:28 -0700 Subject: [PATCH 1/2] nnimap.el: factor out nnimap-group-to-imap * lisp/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. --- lisp/ChangeLog | 13 +++++++++++++ lisp/nnimap.el | 32 ++++++++++++++++++-------------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 556be98..be5d3fb 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,5 +1,18 @@ 2015-07-12 Nikolaus Rath + * 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. + +2015-07-12 Nikolaus Rath + * nnimap.el (nnimap-open-connection-1): explicitly ask server for capabilities instead of relying on LOGIN response. diff --git a/lisp/nnimap.el b/lisp/nnimap.el index b24c3c5..19632ad 100644 --- a/lisp/nnimap.el +++ b/lisp/nnimap.el @@ -178,6 +178,10 @@ If non-nil, articles flagged as deleted (using the IMAP (defvar nnimap-inhibit-logging nil) +(defun nnimap-group-to-imap (group) + "Convert Gnus group name to IMAP mailbox name" + (utf7-encode group t)) + (defun nnimap-buffer () (nnimap-find-process-buffer nntp-server-buffer)) @@ -859,7 +863,7 @@ If non-nil, articles flagged as deleted (using the IMAP (with-current-buffer (nnimap-buffer) (erase-buffer) (let ((group-sequence - (nnimap-send-command "SELECT %S" (utf7-encode group t))) + (nnimap-send-command "SELECT %S" (nnimap-group-to-imap group))) (flag-sequence (nnimap-send-command "UID FETCH 1:* FLAGS"))) (setf (nnimap-group nnimap-object) group) @@ -891,13 +895,13 @@ If non-nil, articles flagged as deleted (using the IMAP (setq group (nnimap-decode-gnus-group group)) (when (nnimap-change-group nil server) (with-current-buffer (nnimap-buffer) - (car (nnimap-command "CREATE %S" (utf7-encode group t)))))) + (car (nnimap-command "CREATE %S" (nnimap-group-to-imap group)))))) (deffoo nnimap-request-delete-group (group &optional force server) (setq group (nnimap-decode-gnus-group group)) (when (nnimap-change-group nil server) (with-current-buffer (nnimap-buffer) - (car (nnimap-command "DELETE %S" (utf7-encode group t)))))) + (car (nnimap-command "DELETE %S" (nnimap-group-to-imap group)))))) (deffoo nnimap-request-rename-group (group new-name &optional server) (setq group (nnimap-decode-gnus-group group)) @@ -905,7 +909,7 @@ If non-nil, articles flagged as deleted (using the IMAP (with-current-buffer (nnimap-buffer) (nnimap-unselect-group) (car (nnimap-command "RENAME %S %S" - (utf7-encode group t) (utf7-encode new-name t)))))) + (nnimap-group-to-imap group) (nnimap-group-to-imap new-name)))))) (defun nnimap-unselect-group () ;; Make sure we don't have this group open read/write by asking @@ -966,7 +970,7 @@ If non-nil, articles flagged as deleted (using the IMAP "UID MOVE %d %S" "UID COPY %d %S")) (result (nnimap-command command article - (utf7-encode internal-move-group t)))) + (nnimap-group-to-imap internal-move-group)))) (when (and (car result) (not can-move)) (nnimap-delete-article article)) (cons internal-move-group @@ -1032,7 +1036,7 @@ If non-nil, articles flagged as deleted (using the IMAP "UID MOVE %s %S" "UID COPY %s %S") (nnimap-article-ranges (gnus-compress-sequence articles)) - (utf7-encode (gnus-group-real-name nnmail-expiry-target) t)) + (nnimap-group-to-imap (gnus-group-real-name nnmail-expiry-target))) (set (if can-move 'deleted-articles 'articles-to-delete) articles)))) t) (t @@ -1172,7 +1176,7 @@ If sync is non-nil, wait for server response." (unsubscribe "UNSUBSCRIBE"))))) (when command (with-current-buffer (nnimap-buffer) - (nnimap-command "%s %S" (cadr command) (utf7-encode group t))))))) + (nnimap-command "%s %S" (cadr command) (nnimap-group-to-imap group))))))) (deffoo nnimap-request-set-mark (group actions &optional server) (setq group (nnimap-decode-gnus-group group)) @@ -1227,7 +1231,7 @@ If sync is non-nil, wait for server response." (nnimap-unselect-group)) (erase-buffer) (setq sequence (nnimap-send-command - "APPEND %S {%d}" (utf7-encode group t) + "APPEND %S {%d}" (nnimap-group-to-imap group) (length message))) (unless nnimap-streaming (nnimap-wait-for-connection "^[+]")) @@ -1354,7 +1358,7 @@ If sync is non-nil, wait for server response." (dolist (group groups) (setf (nnimap-examined nnimap-object) group) (push (list (nnimap-send-command "EXAMINE %S" - (utf7-encode group t)) + (nnimap-group-to-imap group)) group) sequences)) (nnimap-wait-for-response (caar sequences)) @@ -1426,7 +1430,7 @@ If sync is non-nil, wait for server response." unexist) (push (list (nnimap-send-command "EXAMINE %S (%s (%s %s))" - (utf7-encode group t) + (nnimap-group-to-imap group) (nnimap-quirk "QRESYNC") uidvalidity modseq) 'qresync @@ -1448,7 +1452,7 @@ If sync is non-nil, wait for server response." (incf (nnimap-initial-resync nnimap-object)) (setq start 1)) (push (list (nnimap-send-command "%s %S" command - (utf7-encode group t)) + (nnimap-group-to-imap group)) (nnimap-send-command "UID FETCH %d:* FLAGS" start) start group command) sequences)))) @@ -1878,7 +1882,7 @@ Return the server's response to the SELECT or EXAMINE command." (if read-only "EXAMINE" "SELECT") - (utf7-encode group t)))) + (nnimap-group-to-imap group)))) (when (car result) (setf (nnimap-group nnimap-object) group (nnimap-select-result nnimap-object) result) @@ -2135,7 +2139,7 @@ Return the server's response to the SELECT or EXAMINE command." (dolist (spec specs) (when (and (not (member (car spec) groups)) (not (eq (car spec) 'junk))) - (nnimap-command "CREATE %S" (utf7-encode (car spec) t)))) + (nnimap-command "CREATE %S" (nnimap-group-to-imap (car spec))))) ;; Then copy over all the messages. (erase-buffer) (dolist (spec specs) @@ -2151,7 +2155,7 @@ Return the server's response to the SELECT or EXAMINE command." "UID MOVE %d %S" "UID COPY %s %S") (nnimap-article-ranges ranges) - (utf7-encode group t)) + (nnimap-group-to-imap group)) ranges) sequences))))) ;; Wait for the last COPY response... -- 2.1.4 --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0002-nnimap.el-Add-support-for-IMAP-namespaces.patch >From 2f05bec6c1d928e16cc345af35d51c391d88a3ce Mon Sep 17 00:00:00 2001 From: Nikolaus Rath Date: Tue, 14 Jul 2015 19:03:09 -0700 Subject: [PATCH 2/2] nnimap.el: Add support for IMAP namespaces. * lisp/nnimap.el (nnimap-use-namespaces): introduced 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/ChangeLog | 7 +++++++ lisp/nnimap.el | 65 +++++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 60 insertions(+), 12 deletions(-) diff --git a/lisp/ChangeLog b/lisp/ChangeLog index be5d3fb..27de0d0 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,10 @@ +2015-07-14 Nikolaus Rath + + * nnimap.el (nnimap-use-namespaces): introduced 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. + 2015-07-12 Nikolaus Rath * nnimap.el (nnimap-request-group-scan) diff --git a/lisp/nnimap.el b/lisp/nnimap.el index 19632ad..8cbf187 100644 --- a/lisp/nnimap.el +++ b/lisp/nnimap.el @@ -62,6 +62,13 @@ If nnimap-stream is `ssl', this will default to `imaps'. If not, it will default to `imap'.") +(defvoo nnimap-use-namespaces nil + "Whether to use IMAP namespaces +If in Gnus your folder names in all start with (e.g.) `INBOX', +you probably want to set this to t. The effects of this are +purely cosmetical, but changing this variable will affect the +names of your nnimap groups. ") + (defvoo nnimap-stream 'undecided "How nnimap talks to the IMAP server. The value should be either `undecided', `ssl' or `tls', @@ -124,6 +131,8 @@ some servers.") (defvoo nnimap-current-infos nil) +(setq nnimap-namespaces nil) + (defvoo nnimap-fetch-partial-articles nil "If non-nil, Gnus will fetch partial articles. If t, Gnus will fetch only the first part. If a string, it @@ -180,7 +189,17 @@ If non-nil, articles flagged as deleted (using the IMAP (defun nnimap-group-to-imap (group) "Convert Gnus group name to IMAP mailbox name" - (utf7-encode group t)) + (let* ((prefix (cadr (assoc (nnoo-current-server 'nnimap) + nnimap-namespaces))) + (inbox (substring prefix 0 -1))) + (utf7-encode + (cond ((or (not prefix) + (string-equal group inbox)) + group) + ((string-prefix-p "#" group) + (substring group 1)) + (t + (concat prefix group))) t))) (defun nnimap-buffer () (nnimap-find-process-buffer nntp-server-buffer)) @@ -467,7 +486,8 @@ If non-nil, articles flagged as deleted (using the IMAP (props (cdr stream-list)) (greeting (plist-get props :greeting)) (capabilities (plist-get props :capabilities)) - (stream-type (plist-get props :type))) + (stream-type (plist-get props :type)) + (server (nnoo-current-server 'nnimap))) (when (and stream (not (memq (process-status stream) '(open run)))) (setq stream nil)) @@ -502,9 +522,7 @@ If non-nil, articles flagged as deleted (using the IMAP ;; the virtual server name and the address (nnimap-credentials (gnus-delete-duplicates - (list - (nnoo-current-server 'nnimap) - nnimap-address)) + (list server nnimap-address)) ports nnimap-user)))) (setq nnimap-object nil) @@ -523,7 +541,21 @@ If non-nil, articles flagged as deleted (using the IMAP (dolist (response (cddr (nnimap-command "CAPABILITY"))) (when (string= "CAPABILITY" (upcase (car response))) (setf (nnimap-capabilities nnimap-object) - (mapcar #'upcase (cdr response)))))) + (mapcar #'upcase (cdr response))))) + (when (and nnimap-use-namespaces + (nnimap-capability "NAMESPACE")) + (erase-buffer) + (nnimap-wait-for-response (nnimap-send-command "NAMESPACE")) + (let ((response (nnimap-last-response-string))) + (when (string-match + "^\\*\\W+NAMESPACE\\W+((\"\\([^\"\n]+\\)\"\\W+\"\\(.\\)\"))\\W+" + response) + (let ((namespace (cons (match-string 1 response) + (match-string 2 response))) + (entry (assoc server nnimap-namespaces))) + (if entry + (setcdr entry namespace) + (push (cons server namespace) nnimap-namespaces))))))) ;; If the login failed, then forget the credentials ;; that are now possibly cached. (dolist (host (list (nnoo-current-server 'nnimap) @@ -1311,8 +1343,12 @@ If sync is non-nil, wait for server response." (defun nnimap-get-groups () (erase-buffer) - (let ((sequence (nnimap-send-command "LIST \"\" \"*\"")) - groups) + (let* ((sequence (nnimap-send-command "LIST \"\" \"*\"")) + (prefix (cadr (assoc (nnoo-current-server 'nnimap) + nnimap-namespaces))) + (prefix-len (length prefix)) + (inbox (substring prefix 0 -1)) + groups) (nnimap-wait-for-response sequence) (subst-char-in-region (point-min) (point-max) ?\\ ?% t) @@ -1329,10 +1365,15 @@ If sync is non-nil, wait for server response." (skip-chars-backward " \r\"") (point))))) (unless (member '%NoSelect flags) - (push (utf7-decode (if (stringp group) - group - (format "%s" group)) t) - groups)))) + (let* ((group (utf7-decode (if (stringp group) group + (format "%s" group)) t)) + (group (cond ((equal inbox group) + group) + ((string-prefix-p prefix group) + (substring group prefix-len)) + (t + (concat "#" group))))) + (push group groups))))) (nreverse groups))) (defun nnimap-get-responses (sequences) -- 2.1.4 --=-=-=--