* nnheader.el (nnheader-replace-chars-in-string): Use `static-if'.
[elisp/gnus.git-] / lisp / nnimap.el
index a66d9d3..477e9af 100644 (file)
@@ -112,10 +112,6 @@ element in each \"rule\" is the name of the IMAP mailbox, and the
 second is a regexp that nnimap will try to match on the header to find
 a fit.
 
-The first element can also be a list.  In that case, the first element
-is the server the second element is the group on that server in which
-the matching article will be stored.
-
 The second element can also be a function.  In that case, it will be
 called narrowed to the headers with the first element of the rule as
 the argument.  It should return a non-nil value if it thinks that the
@@ -123,7 +119,25 @@ mail belongs in that group.
 
 This variable can also have a function as its value, the function will
 be called with the headers narrowed and should return a group where it
-thinks the article should be splitted to.")
+thinks the article should be splitted to.  See `nnimap-split-fancy'.
+
+To allow for different split rules on different virtual servers, and
+even different split rules in different inboxes on the same server,
+the syntax of this variable have been extended along the lines of:
+
+(setq nnimap-split-rule
+      '((\"my1server\"    (\".*\"    ((\"ding\"    \"ding@gnus.org\")
+                                  (\"junk\"    \"From:.*Simon\")))
+        (\"my2server\"    (\"INBOX\" nnimap-split-fancy))
+        (\"my[34]server\" (\".*\"    ((\"private\" \"To:.*Simon\")
+                                  (\"junk\"    my-junk-func)))))
+
+The virtual server name is in fact a regexp, so that the same rules
+may apply to several servers.  In the example, the servers
+\"my3server\" and \"my4server\" both use the same rules.  Similarly,
+the inbox string is also a regexp.  The actual splitting rules are as
+before, either a function, or a list with group/regexp or
+group/function elements.")
 
 (defvar nnimap-split-predicate "UNSEEN UNDELETED"
   "The predicate used to find articles to split.
@@ -267,7 +281,9 @@ restrict visible folders.")
 
 ;; Internal variables:
 
-(defvar nnimap-debug nil);; "*nnimap-debug*")
+(defvar nnimap-debug nil
+  "Name of buffer to record debugging info.
+For example: (setq nnimap-debug \"*nnimap-debug*\")")
 (defvar nnimap-current-move-server nil)
 (defvar nnimap-current-move-group nil)
 (defvar nnimap-current-move-article nil)
@@ -314,6 +330,14 @@ If SERVER is nil, uses the current server."
       (gnus-group-add-parameter gnusgroup (cons 'uidvalidity new-uidvalidity))
       t)))
 
+(defun nnimap-before-find-minmax-bugworkaround ()
+  "Function called before iterating through mailboxes with
+`nnimap-find-minmax-uid'."
+  ;; XXX this is for UoW imapd problem, it doesn't notice new mail in
+  ;; currently selected mailbox without a re-select/examine.
+  (or (null (imap-current-mailbox nnimap-server-buffer))
+      (imap-mailbox-unselect nnimap-server-buffer)))
+
 (defun nnimap-find-minmax-uid (group &optional examine)
   "Find lowest and highest active article nummber in GROUP.
 If EXAMINE is non-nil the group is selected read-only."
@@ -401,7 +425,7 @@ If EXAMINE is non-nil the group is selected read-only."
     (if (numberp (car-safe articles))
        (imap-search
         (concat "UID "
-                (nnimap-range-to-string
+                (imap-range-to-message-set
                  (gnus-compress-sequence
                   (append (gnus-uncompress-sequence
                            (and fetch-old
@@ -454,7 +478,7 @@ If EXAMINE is non-nil the group is selected read-only."
     (let ((imap-fetch-data-hook '(nnimap-retrieve-headers-progress))
          (nnimap-length (gnus-range-length articles))
          (nnimap-counter 0))
-      (imap-fetch (nnimap-range-to-string articles)
+      (imap-fetch (imap-range-to-message-set articles)
                  (concat "(UID RFC822.SIZE BODY "
                          (let ((headers
                                 (append '(Subject From Date Message-Id
@@ -525,15 +549,15 @@ If EXAMINE is non-nil the group is selected read-only."
                (imap-capability 'IMAP4rev1 nnimap-server-buffer))
       (imap-close nnimap-server-buffer)
       (nnheader-report 'nnimap "Server %s is not IMAP4 compliant" server))
-    (let (list alist user passwd)
-      (and (fboundp 'gnus-parse-netrc)
-          (setq list (gnus-parse-netrc nnimap-authinfo-file)
-                alist (or (and (gnus-netrc-get
-                                (gnus-netrc-machine list server) "machine")
-                               (gnus-netrc-machine list server))
-                          (gnus-netrc-machine list nnimap-address))
-                user (gnus-netrc-get alist "login")
-                passwd (gnus-netrc-get alist "password")))
+    (let* ((list (gnus-parse-netrc nnimap-authinfo-file))
+          (port (if nnimap-server-port
+                    (int-to-string nnimap-server-port)
+                  "imap"))
+          (alist (gnus-netrc-machine list (or nnimap-server-address 
+                                               nnimap-address server)
+                                      port "imap"))
+          (user (gnus-netrc-get alist "login"))
+          (passwd (gnus-netrc-get alist "password")))
       (if (imap-authenticate user passwd nnimap-server-buffer)
          (prog1
              (push (list server nnimap-server-buffer)
@@ -611,12 +635,11 @@ function is generally only called when Gnus is shutting down."
   (with-current-buffer nnimap-callback-buffer
     (insert
      (with-current-buffer nnimap-server-buffer
-       (nnimap-demule
-        (if (imap-capability 'IMAP4rev1) 
-            ;; xxx don't just use car? alist doesn't contain
-            ;; anything else now, but it might...
-            (nth 2 (car (imap-message-get (imap-current-message) 'BODYDETAIL)))
-          (imap-message-get (imap-current-message) 'RFC822)))))
+       (if (imap-capability 'IMAP4rev1) 
+          ;; xxx don't just use car? alist doesn't contain
+          ;; anything else now, but it might...
+          (nth 2 (car (imap-message-get (imap-current-message) 'BODYDETAIL)))
+        (imap-message-get (imap-current-message) 'RFC822))))
     (nnheader-ms-strip-cr)
     (funcall nnimap-callback-callback-function t)))
 
@@ -629,18 +652,16 @@ function is generally only called when Gnus is shutting down."
                                  nnimap-server-buffer))
                     article)))
       (when article
-       (gnus-message 9 "nnimap: Fetching (part of) article %d..." article)
+       (gnus-message 10 "nnimap: Fetching (part of) article %d..." article)
        (if (not nnheader-callback-function)
            (with-current-buffer (or to-buffer nntp-server-buffer)
              (erase-buffer)
              (let ((data (imap-fetch article part prop nil
                                      nnimap-server-buffer)))
                (when data
-                 (insert (nnimap-demule (if detail
-                                            (nth 2 (car data))
-                                          data)))
+                 (insert (if detail (nth 2 (car data)) data))
                  (nnheader-ms-strip-cr)
-                 (gnus-message 9
+                 (gnus-message 10
                                "nnimap: Fetching (part of) article %d...done"
                                article)
                  (if (bobp)
@@ -684,6 +705,7 @@ function is generally only called when Gnus is shutting down."
                   group (gnus-server-to-method (format "nnimap:%s" server))))
    server)
   (when (nnimap-possibly-change-group group server)
+    (nnimap-before-find-minmax-bugworkaround)
     (let (info)
       (cond (fast group)
            ((null (setq info (nnimap-find-minmax-uid group t)))
@@ -727,6 +749,7 @@ function is generally only called when Gnus is shutting down."
       (erase-buffer))
     (gnus-message 5 "nnimap: Generating active list%s..."
                  (if (> (length server) 0) (concat " for " server) ""))
+    (nnimap-before-find-minmax-bugworkaround)
     (with-current-buffer nnimap-server-buffer
       (dolist (pattern (nnimap-pattern-to-list-arguments nnimap-list-pattern))
        (dolist (mbx (funcall nnimap-request-list-method
@@ -768,6 +791,7 @@ function is generally only called when Gnus is shutting down."
     (gnus-message 5 "nnimap: Checking mailboxes...")
     (with-current-buffer nntp-server-buffer
       (erase-buffer)
+      (nnimap-before-find-minmax-bugworkaround)
       (dolist (group groups)
        (gnus-message 7 "nnimap: Checking mailbox %s" group)
        (or (member "\\NoSelect"
@@ -856,15 +880,15 @@ function is generally only called when Gnus is shutting down."
            (when (and range marks)
              (cond ((eq what 'del)
                     (imap-message-flags-del
-                     (nnimap-range-to-string range)
+                     (imap-range-to-message-set range)
                      (nnimap-mark-to-flag marks nil t)))
                    ((eq what 'add)
                     (imap-message-flags-add
-                     (nnimap-range-to-string range)
+                     (imap-range-to-message-set range)
                      (nnimap-mark-to-flag marks nil t)))
                    ((eq what 'set)
                     (imap-message-flags-set
-                     (nnimap-range-to-string range)
+                     (imap-range-to-message-set range)
                      (nnimap-mark-to-flag marks nil t)))))))
        (gnus-message 7 "nnimap: Setting marks in %s...done" group))))
   nil)
@@ -904,8 +928,21 @@ function is generally only called when Gnus is shutting down."
                (or nnimap-split-crosspost
                    (throw 'split-done to-groups))))))))))
   
+(defun nnimap-assoc-match (key alist)
+  (let (element)
+    (while (and alist (not element))
+      (if (string-match (car (car alist)) key)
+         (setq element (car alist)))
+      (setq alist (cdr alist)))
+    element))
+
 (defun nnimap-split-find-rule (server inbox)
-  nnimap-split-rule)
+  (if (and (listp nnimap-split-rule) (listp (car nnimap-split-rule))
+           (list (cdar nnimap-split-rule)) (listp (cadar nnimap-split-rule)))
+      ;; extended format
+      (cadr (nnimap-assoc-match inbox (cdr (nnimap-assoc-match 
+                                           server nnimap-split-rule))))
+    nnimap-split-rule))
 
 (defun nnimap-split-find-inbox (server)
   (if (listp nnimap-split-inbox)
@@ -956,6 +993,7 @@ function is generally only called when Gnus is shutting down."
       (gnus-message 5 "nnimap: Listing subscribed mailboxes%s%s..."
                    (if (> (length server) 0) " on " "") server)
       (erase-buffer)
+      (nnimap-before-find-minmax-bugworkaround)
       (dolist (pattern (nnimap-pattern-to-list-arguments
                        nnimap-list-pattern))
        (dolist (mbx (imap-mailbox-lsub "*" (car pattern) nil 
@@ -1007,25 +1045,25 @@ function is generally only called when Gnus is shutting down."
       (with-current-buffer nnimap-server-buffer
        (if force
            (and (imap-message-flags-add
-                 (nnimap-range-to-string artseq) "\\Deleted")
+                 (imap-range-to-message-set artseq) "\\Deleted")
                 (setq articles nil))
          (let ((days (or (and nnmail-expiry-wait-function
                               (funcall nnmail-expiry-wait-function group))
                          nnmail-expiry-wait)))
            (cond ((eq days 'immediate)
                   (and (imap-message-flags-add
-                        (nnimap-range-to-string artseq) "\\Deleted")
+                        (imap-range-to-message-set artseq) "\\Deleted")
                        (setq articles nil)))
                  ((numberp days)
                   (let ((oldarts (imap-search
                                   (format "UID %s NOT SINCE %s"
-                                          (nnimap-range-to-string artseq)
+                                          (imap-range-to-message-set artseq)
                                           (nnimap-date-days-ago days))))
                         (imap-fetch-data-hook
                          '(nnimap-request-expire-articles-progress)))
                     (and oldarts
                          (imap-message-flags-add
-                          (nnimap-range-to-string
+                          (imap-range-to-message-set
                            (gnus-compress-sequence oldarts))
                           "\\Deleted")
                          (setq articles (gnus-set-difference
@@ -1210,18 +1248,6 @@ sure of changing the value of `foo'."
       (cons (cons key value) (nnimap-remassoc key alist))
     (nnimap-remassoc key alist)))
 
-(defun nnimap-range-to-string (range)
-  (mapconcat
-   (lambda (item)
-     (if (consp item)
-         (format "%d:%d"
-                 (car item) (cdr item))
-       (format "%d" item)))
-   (if (and (listp range) (not (listp (cdr range))))
-       (list range);; make (1 . 2) into ((1 . 2))
-     range)
-   ","))
-
 (when nnimap-debug
   (require 'trace)
   (buffer-disable-undo (get-buffer-create nnimap-debug))
@@ -1230,6 +1256,7 @@ sure of changing the value of `foo'."
          nnimap-possibly-change-server
          nnimap-verify-uidvalidity
          nnimap-find-minmax-uid
+         nnimap-before-find-minmax-bugworkaround
          nnimap-possibly-change-group
          ;;nnimap-replace-whitespace
          nnimap-retrieve-headers-progress
@@ -1283,7 +1310,6 @@ sure of changing the value of `foo'."
          nnimap-mark-permanent-p
          nnimap-remassoc
          nnimap-update-alist-soft
-         nnimap-range-to-string
           )))
 
 (provide 'nnimap)