pop3.el (pop3-open-server, pop3-read-response, pop3-list, pop3-retr, pop3-quit):...
[gnus] / lisp / mail-source.el
index 1e7260d..662b999 100644 (file)
@@ -1,17 +1,17 @@
 ;;; mail-source.el --- functions for fetching mail
 
-;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004,
-;;   2005, 2006, 2007 Free Software Foundation, Inc.
+;; Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007,
+;;   2008, 2009, 2010  Free Software Foundation, Inc.
 
 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
 ;; Keywords: news, mail
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 ;;; Code:
 
-(eval-when-compile
-  (require 'cl)
-  (require 'imap)
-  (eval-when-compile (defvar display-time-mail-function)))
+;; For Emacs < 22.2.
 (eval-and-compile
-  (autoload 'pop3-movemail "pop3")
-  (autoload 'pop3-get-message-count "pop3")
-  (autoload 'nnheader-cancel-timer "nnheader"))
+  (unless (fboundp 'declare-function) (defmacro declare-function (&rest r))))
+
 (require 'format-spec)
+(eval-when-compile
+  (require 'cl)
+  (require 'imap))
+(autoload 'auth-source-user-or-password "auth-source")
+(autoload 'pop3-movemail "pop3")
+(autoload 'pop3-get-message-count "pop3")
+(autoload 'nnheader-cancel-timer "nnheader")
 (require 'mm-util)
 (require 'message) ;; for `message-directory'
 
+(defvar display-time-mail-function)
+
 (defgroup mail-source nil
   "The mail-fetching library."
   :version "21.1"
              (list 'const (car a)))
      imap-stream-alist)))
 
-(defcustom mail-sources nil
-  "*Where the mail backends will look for incoming mail.
+(defcustom mail-sources '((file))
+  "Where the mail backends will look for incoming mail.
 This variable is a list of mail source specifiers.
 See Info node `(gnus)Mail Source Specifiers'."
   :group 'mail-source
+  :version "23.1" ;; No Gnus
   :link '(custom-manual "(gnus)Mail Source Specifiers")
   :type `(choice
-         (const nil)
-         (repeat
+         (const :tag "None" nil)
+         (repeat :tag "List"
           (choice :format "%[Value Menu%] %v"
                   :value (file)
+                  (cons :tag "Group parameter `mail-source'"
+                        (const :format "" group))
                   (cons :tag "Spool file"
                         (const :format "" file)
                         (checklist :tag "Options" :greedy t
@@ -277,25 +283,28 @@ If non-nil, this maildrop will be checked periodically for new mail."
   :group 'mail-source
   :type 'integer)
 
-(defcustom mail-source-delete-incoming nil
-  "*If non-nil, delete incoming files after handling.
+(defcustom mail-source-delete-incoming
+  10 ;; development versions
+  ;; 2 ;; released versions
+  "If non-nil, delete incoming files after handling.
 If t, delete immediately, if nil, never delete.  If a positive number, delete
-files older than number of days."
-  ;; Note: The removing happens in `mail-source-callback', i.e. no old
-  ;; incoming files will be deleted, unless you receive new mail.
-  ;;
-  ;; You may also set this to `nil' and call `mail-source-delete-old-incoming'
-  ;; from a hook or interactively.
+files older than number of days.
+
+Removing of old files happens in `mail-source-callback', i.e. no
+old incoming files will be deleted unless you receive new mail.
+You may also set this variable to nil and call
+`mail-source-delete-old-incoming' interactively."
   :group 'mail-source
+  :version "22.2" ;; No Gnus / Gnus 5.10.10 (default changed)
   :type '(choice (const :tag "immediately" t)
                 (const :tag "never" nil)
                 (integer :tag "days")))
 
-(defcustom mail-source-delete-old-incoming-confirm t
-  "*If non-nil, ask for confirmation before deleting old incoming files.
+(defcustom mail-source-delete-old-incoming-confirm nil
+  "If non-nil, ask for confirmation before deleting old incoming files.
 This variable only applies when `mail-source-delete-incoming' is a positive
 number."
-  :version "22.1"
+  :version "22.2" ;; No Gnus / Gnus 5.10.10 (default changed)
   :group 'mail-source
   :type 'boolean)
 
@@ -408,6 +417,8 @@ All keywords that can be used must be listed here."))
     "Strip the leading colon off the KEYWORD."
     (intern (substring (symbol-name keyword) 1))))
 
+;; generate a list of variable names paired with nil values
+;; suitable for usage in a `let' form
 (eval-and-compile
   (defun mail-source-bind-1 (type)
     (let* ((defaults (cdr (assq type mail-source-keyword-map)))
@@ -426,27 +437,58 @@ At run time, the mail source specifier SOURCE will be inspected,
 and the variables will be set according to it.  Variables not
 specified will be given default values.
 
+The user and password will be loaded from the auth-source values
+if those are available.  They override the original user and
+password in a second `let' form.
+
 After this is done, BODY will be executed in the scope
-of the `let' form.
+of the second `let' form.
 
 The variables bound and their default values are described by
 the `mail-source-keyword-map' variable."
-  `(let ,(mail-source-bind-1 (car type-source))
+  `(let* ,(mail-source-bind-1 (car type-source))
      (mail-source-set-1 ,(cadr type-source))
      ,@body))
 
 (put 'mail-source-bind 'lisp-indent-function 1)
 (put 'mail-source-bind 'edebug-form-spec '(sexp body))
 
+;; TODO: use the list format for auth-source-user-or-password modes
 (defun mail-source-set-1 (source)
   (let* ((type (pop source))
         (defaults (cdr (assq type mail-source-keyword-map)))
-        default value keyword)
+        default value keyword auth-info user-auth pass-auth)
     (while (setq default (pop defaults))
+      ;; for each default :SYMBOL, set SYMBOL to the plist value for :SYMBOL
+      ;; using `mail-source-value' to evaluate the plist value
       (set (mail-source-strip-keyword (setq keyword (car default)))
-          (if (setq value (plist-get source keyword))
-              (mail-source-value value)
-            (mail-source-value (cadr default)))))))
+          ;; note the following reasons for this structure:
+          ;; 1) the auth-sources user and password override everything
+          ;; 2) it avoids macros, so it's cleaner
+          ;; 3) it falls through to the mail-sources and then default values
+          (cond
+           ((and
+            (eq keyword :user)
+            (setq user-auth
+                  (nth 0 (auth-source-user-or-password
+                          '("login" "password")
+                          ;; this is "host" in auth-sources
+                          (if (boundp 'server) (symbol-value 'server) "")
+                          type))))
+            user-auth)
+           ((and
+             (eq keyword :password)
+             (setq pass-auth
+                   (nth 1
+                        (auth-source-user-or-password
+                         '("login" "password")
+                         ;; this is "host" in auth-sources
+                         (if (boundp 'server) (symbol-value 'server) "")
+                         type))))
+            pass-auth)
+           (t (if (setq value (plist-get source keyword))
+                (mail-source-value value)
+              (mail-source-value (cadr default)))))))))
 
 (eval-and-compile
   (defun mail-source-bind-common-1 ()
@@ -488,14 +530,13 @@ See `mail-source-bind'."
    ((stringp value)
     value)
    ;; Function
-   ((and (listp value)
-        (functionp (car value)))
+   ((and (listp value) (symbolp (car value)) (fboundp (car value)))
     (eval value))
    ;; Just return the value.
    (t
     value)))
 
-(defun mail-source-fetch (source callback)
+(defun mail-source-fetch (source callback &optional method)
   "Fetch mail from SOURCE and call CALLBACK zero or more times.
 CALLBACK will be called with the name of the file where (some of)
 the mail from SOURCE is put.
@@ -503,6 +544,16 @@ Return the number of files that were found."
   (mail-source-bind-common source
     (if (or mail-source-plugged plugged)
        (save-excursion
+         ;; Special-case the `file' handler since it's so common and
+         ;; just adds noise.
+         (when (or (not (eq (car source) 'file))
+                   (mail-source-bind (file source)
+                     (file-exists-p path)))
+           (nnheader-message 4 "%sReading incoming mail from %s..."
+                             (if method
+                                 (format "%s: " method)
+                               "")
+                             (car source)))
          (let ((function (cadr (assq (car source) mail-source-fetcher-alist)))
                (found 0))
            (unless function
@@ -533,17 +584,21 @@ Return the number of files that were found."
                      (error "Cannot get new mail"))
                    0)))))))))
 
+(declare-function gnus-message "gnus-util" (level &rest args))
+
 (defun mail-source-delete-old-incoming (&optional age confirm)
   "Remove incoming files older than AGE days.
 If CONFIRM is non-nil, ask for confirmation before removing a file."
   (interactive "P")
+  (require 'gnus-util)
   (let* ((high2days (/ 65536.0 60 60 24));; convert high bits to days
         (low2days  (/ 1.0 65536.0))     ;; convert low bits to days
         (diff (if (natnump age) age 30));; fallback, if no valid AGE given
         currday files)
     (setq files (directory-files
                 mail-source-directory t
-                (concat mail-source-incoming-file-prefix "*"))
+                (concat "\\`"
+                        (regexp-quote mail-source-incoming-file-prefix)))
          currday (* (car (current-time)) high2days)
          currday (+ currday (* low2days (nth 1 (current-time)))))
     (while files
@@ -555,10 +610,13 @@ If CONFIRM is non-nil, ask for confirmation before removing a file."
             (fileday (+ fileday (* low2days (nth 1 filetime)))))
        (setq files (cdr files))
        (when (and (> (- currday fileday) diff)
-                  (gnus-message 8 "File `%s' is older than %s day(s)"
-                                bfile diff)
-                  (or (not confirm)
-                      (y-or-n-p (concat "Remove file `" bfile "'? "))))
+                  (if confirm
+                      (y-or-n-p
+                       (format "\
+Delete old (> %s day(s)) incoming mail file `%s'? " diff bfile))
+                    (gnus-message 8 "\
+Deleting old (> %s day(s)) incoming mail file `%s'." diff bfile)
+                    t))
          (delete-file ffile))))))
 
 (defun mail-source-callback (callback info)
@@ -571,6 +629,8 @@ If CONFIRM is non-nil, ask for confirmation before removing a file."
        0)
     (funcall callback mail-source-crash-box info)))
 
+(defvar mail-source-incoming-last-checked-time nil)
+
 (defun mail-source-delete-crash-box ()
   (when (file-exists-p mail-source-crash-box)
     ;; Delete or move the incoming mail out of the way.
@@ -586,9 +646,16 @@ If CONFIRM is non-nil, ask for confirmation before removing a file."
        (rename-file mail-source-crash-box incoming t)
        ;; remove old incoming files?
        (when (natnump mail-source-delete-incoming)
-         (mail-source-delete-old-incoming
-          mail-source-delete-incoming
-          mail-source-delete-old-incoming-confirm))))))
+         ;; Don't check for old incoming files more than once per day to
+         ;; save a lot of file accesses.
+         (when (or (null mail-source-incoming-last-checked-time)
+                   (> (time-to-seconds
+                       (time-since mail-source-incoming-last-checked-time))
+                      (* 24 60 60)))
+           (setq mail-source-incoming-last-checked-time (current-time))
+           (mail-source-delete-old-incoming
+            mail-source-delete-incoming
+            mail-source-delete-old-incoming-confirm)))))))
 
 (defun mail-source-movemail (from to)
   "Move FROM to TO using movemail."
@@ -979,24 +1046,28 @@ This only works when `display-time' is enabled."
              (mail-source-delete-crash-box)))))
       found)))
 
-(eval-and-compile
-  (autoload 'imap-open "imap")
-  (autoload 'imap-authenticate "imap")
-  (autoload 'imap-mailbox-select "imap")
-  (autoload 'imap-mailbox-unselect "imap")
-  (autoload 'imap-mailbox-close "imap")
-  (autoload 'imap-search "imap")
-  (autoload 'imap-fetch "imap")
-  (autoload 'imap-close "imap")
-  (autoload 'imap-error-text "imap")
-  (autoload 'imap-message-flags-add "imap")
-  (autoload 'imap-list-to-message-set "imap")
-  (autoload 'imap-range-to-message-set "imap")
-  (autoload 'nnheader-ms-strip-cr "nnheader"))
+(autoload 'imap-open "imap")
+(autoload 'imap-authenticate "imap")
+(autoload 'imap-mailbox-select "imap")
+(autoload 'imap-mailbox-unselect "imap")
+(autoload 'imap-mailbox-close "imap")
+(autoload 'imap-search "imap")
+(autoload 'imap-fetch "imap")
+(autoload 'imap-close "imap")
+(autoload 'imap-error-text "imap")
+(autoload 'imap-message-flags-add "imap")
+(autoload 'imap-list-to-message-set "imap")
+(autoload 'imap-range-to-message-set "imap")
+(autoload 'nnheader-ms-strip-cr "nnheader")
+
+(autoload 'gnus-compress-sequence "gnus-range")
 
 (defvar mail-source-imap-file-coding-system 'binary
   "Coding system for the crashbox made by `mail-source-fetch-imap'.")
 
+;; Autoloads will bring in imap before this is called.
+(declare-function imap-capability "imap" (&optional identifier buffer))
+
 (defun mail-source-fetch-imap (source callback)
   "Fetcher for imap sources."
   (mail-source-bind (imap source)
@@ -1067,8 +1138,7 @@ This only works when `display-time' is enabled."
                         ?s server ?P port ?u user))
       found)))
 
-(eval-and-compile
-  (autoload 'webmail-fetch "webmail"))
+(autoload 'webmail-fetch "webmail")
 
 (defun mail-source-fetch-webmail (source callback)
   "Fetch for webmail source."
@@ -1094,5 +1164,4 @@ This only works when `display-time' is enabled."
 
 (provide 'mail-source)
 
-;;; arch-tag: 72948025-1d17-4d6c-bb12-ef1aa2c490fd
 ;;; mail-source.el ends here