]> git.eshelyaron.com Git - emacs.git/commitdiff
Move low-level library files from the lisp/gnus directory
authorLars Ingebrigtsen <larsi@gnus.org>
Wed, 24 Feb 2016 02:04:03 +0000 (13:04 +1100)
committerLars Ingebrigtsen <larsi@gnus.org>
Wed, 24 Feb 2016 02:04:03 +0000 (13:04 +1100)
The files moved from lisp/gnus are:

auth-source.el -> /
compface.el -> /image
ecomplete.el -> /
flow-fill.el -> /mail
gravatar.el -> /image
gssapi.el -> /net
html2text.el -> /net
ietf-drums.el -> /mail
mail-parse.el -> /mail
mail-prsvr.el -> /mail
mailcap.el -> /net
plstore.el -> /
pop3.el -> /net
qp.el -> /mail
registry.el -> /
rfc1843.el -> /international
rfc2045.el -> /mail
rfc2047.el -> /mail
rfc2231.el -> /mail
rtree.el -> /
sieve-manage.el -> /net
sieve-mode.el -> /net
sieve.el -> /net
starttls.el -> /net
utf7.el -> /international
yenc.el -> /mail

52 files changed:
lisp/auth-source.el [new file with mode: 0644]
lisp/ecomplete.el [new file with mode: 0644]
lisp/gnus/auth-source.el [deleted file]
lisp/gnus/compface.el [deleted file]
lisp/gnus/ecomplete.el [deleted file]
lisp/gnus/flow-fill.el [deleted file]
lisp/gnus/gravatar.el [deleted file]
lisp/gnus/html2text.el [deleted file]
lisp/gnus/ietf-drums.el [deleted file]
lisp/gnus/mail-parse.el [deleted file]
lisp/gnus/mail-prsvr.el [deleted file]
lisp/gnus/mailcap.el [deleted file]
lisp/gnus/plstore.el [deleted file]
lisp/gnus/pop3.el [deleted file]
lisp/gnus/qp.el [deleted file]
lisp/gnus/registry.el [deleted file]
lisp/gnus/rfc1843.el [deleted file]
lisp/gnus/rfc2045.el [deleted file]
lisp/gnus/rfc2047.el [deleted file]
lisp/gnus/rfc2231.el [deleted file]
lisp/gnus/rtree.el [deleted file]
lisp/gnus/sieve-manage.el [deleted file]
lisp/gnus/sieve-mode.el [deleted file]
lisp/gnus/sieve.el [deleted file]
lisp/gnus/starttls.el [deleted file]
lisp/gnus/utf7.el [deleted file]
lisp/gnus/yenc.el [deleted file]
lisp/image/compface.el [new file with mode: 0644]
lisp/image/gravatar.el [new file with mode: 0644]
lisp/international/rfc1843.el [new file with mode: 0644]
lisp/international/utf7.el [new file with mode: 0644]
lisp/mail/flow-fill.el [new file with mode: 0644]
lisp/mail/ietf-drums.el [new file with mode: 0644]
lisp/mail/mail-parse.el [new file with mode: 0644]
lisp/mail/mail-prsvr.el [new file with mode: 0644]
lisp/mail/qp.el [new file with mode: 0644]
lisp/mail/rfc2045.el [new file with mode: 0644]
lisp/mail/rfc2047.el [new file with mode: 0644]
lisp/mail/rfc2231.el [new file with mode: 0644]
lisp/mail/yenc.el [new file with mode: 0644]
lisp/net/html2text.el [new file with mode: 0644]
lisp/net/mailcap.el [new file with mode: 0644]
lisp/net/pop3.el [new file with mode: 0644]
lisp/net/sieve-manage.el [new file with mode: 0644]
lisp/net/sieve-mode.el [new file with mode: 0644]
lisp/net/sieve.el [new file with mode: 0644]
lisp/net/starttls.el [new file with mode: 0644]
lisp/plstore.el [new file with mode: 0644]
lisp/registry.el [new file with mode: 0644]
lisp/rtree.el [new file with mode: 0644]
test/lisp/auth-source-tests.el [new file with mode: 0644]
test/lisp/gnus/auth-source-tests.el [deleted file]

diff --git a/lisp/auth-source.el b/lisp/auth-source.el
new file mode 100644 (file)
index 0000000..cfd21a5
--- /dev/null
@@ -0,0 +1,2145 @@
+;;; auth-source.el --- authentication sources for Gnus and Emacs
+
+;; Copyright (C) 2008-2016 Free Software Foundation, Inc.
+
+;; Author: Ted Zlatanov <tzz@lifelogs.com>
+;; Keywords: news
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This is the auth-source.el package.  It lets users tell Gnus how to
+;; authenticate in a single place.  Simplicity is the goal.  Instead
+;; of providing 5000 options, we'll stick to simple, easy to
+;; understand options.
+
+;; See the auth.info Info documentation for details.
+
+;; TODO:
+
+;; - never decode the backend file unless it's necessary
+;; - a more generic way to match backends and search backend contents
+;; - absorb netrc.el and simplify it
+;; - protect passwords better
+;; - allow creating and changing netrc lines (not files) e.g. change a password
+
+;;; Code:
+
+(require 'password-cache)
+
+(eval-when-compile (require 'cl))
+(require 'eieio)
+
+(autoload 'secrets-create-item "secrets")
+(autoload 'secrets-delete-item "secrets")
+(autoload 'secrets-get-alias "secrets")
+(autoload 'secrets-get-attributes "secrets")
+(autoload 'secrets-get-secret "secrets")
+(autoload 'secrets-list-collections "secrets")
+(autoload 'secrets-search-items "secrets")
+
+(autoload 'rfc2104-hash "rfc2104")
+
+(autoload 'plstore-open "plstore")
+(autoload 'plstore-find "plstore")
+(autoload 'plstore-put "plstore")
+(autoload 'plstore-delete "plstore")
+(autoload 'plstore-save "plstore")
+(autoload 'plstore-get-file "plstore")
+
+(eval-when-compile (require 'epg)) ;; setf-method for `epg-context-armor'
+(autoload 'epg-make-context "epg")
+(autoload 'epg-context-set-passphrase-callback "epg")
+(autoload 'epg-decrypt-string "epg")
+(autoload 'epg-encrypt-string "epg")
+
+(autoload 'help-mode "help-mode" nil t)
+
+(defvar secrets-enabled)
+
+(defgroup auth-source nil
+  "Authentication sources."
+  :version "23.1" ;; No Gnus
+  :group 'gnus)
+
+;;;###autoload
+(defcustom auth-source-cache-expiry 7200
+  "How many seconds passwords are cached, or nil to disable
+expiring.  Overrides `password-cache-expiry' through a
+let-binding."
+  :version "24.1"
+  :group 'auth-source
+  :type '(choice (const :tag "Never" nil)
+                 (const :tag "All Day" 86400)
+                 (const :tag "2 Hours" 7200)
+                 (const :tag "30 Minutes" 1800)
+                 (integer :tag "Seconds")))
+
+;; The slots below correspond with the `auth-source-search' spec,
+;; so a backend with :host set, for instance, would match only
+;; searches for that host.  Normally they are nil.
+(defclass auth-source-backend ()
+  ((type :initarg :type
+         :initform 'netrc
+         :type symbol
+         :custom symbol
+         :documentation "The backend type.")
+   (source :initarg :source
+           :type string
+           :custom string
+           :documentation "The backend source.")
+   (host :initarg :host
+         :initform t
+         :type t
+         :custom string
+         :documentation "The backend host.")
+   (user :initarg :user
+         :initform t
+         :type t
+         :custom string
+         :documentation "The backend user.")
+   (port :initarg :port
+         :initform t
+         :type t
+         :custom string
+         :documentation "The backend protocol.")
+   (data :initarg :data
+         :initform nil
+         :documentation "Internal backend data.")
+   (create-function :initarg :create-function
+                    :initform ignore
+                    :type function
+                    :custom function
+                    :documentation "The create function.")
+   (search-function :initarg :search-function
+                    :initform ignore
+                    :type function
+                    :custom function
+                    :documentation "The search function.")))
+
+(defcustom auth-source-protocols '((imap "imap" "imaps" "143" "993")
+                                   (pop3 "pop3" "pop" "pop3s" "110" "995")
+                                   (ssh  "ssh" "22")
+                                   (sftp "sftp" "115")
+                                   (smtp "smtp" "25"))
+  "List of authentication protocols and their names"
+
+  :group 'auth-source
+  :version "23.2" ;; No Gnus
+  :type '(repeat :tag "Authentication Protocols"
+                 (cons :tag "Protocol Entry"
+                       (symbol :tag "Protocol")
+                       (repeat :tag "Names"
+                               (string :tag "Name")))))
+
+;; Generate all the protocols in a format Customize can use.
+;; TODO: generate on the fly from auth-source-protocols
+(defconst auth-source-protocols-customize
+  (mapcar (lambda (a)
+            (let ((p (car-safe a)))
+              (list 'const
+                    :tag (upcase (symbol-name p))
+                    p)))
+          auth-source-protocols))
+
+(defvar auth-source-creation-defaults nil
+  ;; FIXME: AFAICT this is not set (or let-bound) anywhere!
+  "Defaults for creating token values.  Usually let-bound.")
+
+(defvar auth-source-creation-prompts nil
+  "Default prompts for token values.  Usually let-bound.")
+
+(make-obsolete 'auth-source-hide-passwords nil "Emacs 24.1")
+
+(defcustom auth-source-save-behavior 'ask
+  "If set, auth-source will respect it for save behavior."
+  :group 'auth-source
+  :version "23.2" ;; No Gnus
+  :type `(choice
+          :tag "auth-source new token save behavior"
+          (const :tag "Always save" t)
+          (const :tag "Never save" nil)
+          (const :tag "Ask" ask)))
+
+;; TODO: make the default (setq auth-source-netrc-use-gpg-tokens `((,(if (boundp 'epa-file-auto-mode-alist-entry) (car epa-file-auto-mode-alist-entry) "\\.gpg\\'") never) (t gpg)))
+;; TODO: or maybe leave as (setq auth-source-netrc-use-gpg-tokens 'never)
+
+(defcustom auth-source-netrc-use-gpg-tokens 'never
+  "Set this to tell auth-source when to create GPG password
+tokens in netrc files.  It's either an alist or `never'.
+Note that if EPA/EPG is not available, this should NOT be used."
+  :group 'auth-source
+  :version "23.2" ;; No Gnus
+  :type `(choice
+          (const :tag "Always use GPG password tokens" (t gpg))
+          (const :tag "Never use GPG password tokens" never)
+          (repeat :tag "Use a lookup list"
+                  (list
+                   (choice :tag "Matcher"
+                           (const :tag "Match anything" t)
+                           (const :tag "The EPA encrypted file extensions"
+                                  ,(if (boundp 'epa-file-auto-mode-alist-entry)
+                                       (car epa-file-auto-mode-alist-entry)
+                                     "\\.gpg\\'"))
+                           (regexp :tag "Regular expression"))
+                   (choice :tag "What to do"
+                           (const :tag "Save GPG-encrypted password tokens" gpg)
+                           (const :tag "Don't encrypt tokens" never))))))
+
+(defvar auth-source-magic "auth-source-magic ")
+
+(defcustom auth-source-do-cache t
+  "Whether auth-source should cache information with `password-cache'."
+  :group 'auth-source
+  :version "23.2" ;; No Gnus
+  :type `boolean)
+
+(defcustom auth-source-debug nil
+  "Whether auth-source should log debug messages.
+
+If the value is nil, debug messages are not logged.
+
+If the value is t, debug messages are logged with `message'.  In
+that case, your authentication data will be in the clear (except
+for passwords).
+
+If the value is a function, debug messages are logged by calling
+ that function using the same arguments as `message'."
+  :group 'auth-source
+  :version "23.2" ;; No Gnus
+  :type `(choice
+          :tag "auth-source debugging mode"
+          (const :tag "Log using `message' to the *Messages* buffer" t)
+          (const :tag "Log all trivia with `message' to the *Messages* buffer"
+                 trivia)
+          (function :tag "Function that takes arguments like `message'")
+          (const :tag "Don't log anything" nil)))
+
+(defcustom auth-sources '("~/.authinfo" "~/.authinfo.gpg" "~/.netrc")
+  "List of authentication sources.
+Each entry is the authentication type with optional properties.
+Entries are tried in the order in which they appear.
+See Info node `(auth)Help for users' for details.
+
+If an entry names a file with the \".gpg\" extension and you have
+EPA/EPG set up, the file will be encrypted and decrypted
+automatically.  See Info node `(epa)Encrypting/decrypting gpg files'
+for details.
+
+It's best to customize this with `\\[customize-variable]' because the choices
+can get pretty complex."
+  :group 'auth-source
+  :version "24.1" ;; No Gnus
+  :type `(repeat :tag "Authentication Sources"
+                 (choice
+                  (string :tag "Just a file")
+                  (const :tag "Default Secrets API Collection" default)
+                  (const :tag "Login Secrets API Collection" "secrets:Login")
+                  (const :tag "Temp Secrets API Collection" "secrets:session")
+
+                  (const :tag "Default internet Mac OS Keychain"
+                         macos-keychain-internet)
+
+                  (const :tag "Default generic Mac OS Keychain"
+                         macos-keychain-generic)
+
+                  (list :tag "Source definition"
+                        (const :format "" :value :source)
+                        (choice :tag "Authentication backend choice"
+                                (string :tag "Authentication Source (file)")
+                                (list
+                                 :tag "Secret Service API/KWallet/GNOME Keyring"
+                                 (const :format "" :value :secrets)
+                                 (choice :tag "Collection to use"
+                                         (string :tag "Collection name")
+                                         (const :tag "Default" default)
+                                         (const :tag "Login" "Login")
+                                         (const
+                                          :tag "Temporary" "session")))
+                                (list
+                                 :tag "Mac OS internet Keychain"
+                                 (const :format ""
+                                        :value :macos-keychain-internet)
+                                 (choice :tag "Collection to use"
+                                         (string :tag "internet Keychain path")
+                                         (const :tag "default" default)))
+                                (list
+                                 :tag "Mac OS generic Keychain"
+                                 (const :format ""
+                                        :value :macos-keychain-generic)
+                                 (choice :tag "Collection to use"
+                                         (string :tag "generic Keychain path")
+                                         (const :tag "default" default))))
+                        (repeat :tag "Extra Parameters" :inline t
+                                (choice :tag "Extra parameter"
+                                        (list
+                                         :tag "Host"
+                                         (const :format "" :value :host)
+                                         (choice :tag "Host (machine) choice"
+                                                 (const :tag "Any" t)
+                                                 (regexp
+                                                  :tag "Regular expression")))
+                                        (list
+                                         :tag "Protocol"
+                                         (const :format "" :value :port)
+                                         (choice
+                                          :tag "Protocol"
+                                          (const :tag "Any" t)
+                                          ,@auth-source-protocols-customize))
+                                        (list :tag "User" :inline t
+                                              (const :format "" :value :user)
+                                              (choice
+                                               :tag "Personality/Username"
+                                               (const :tag "Any" t)
+                                               (string
+                                                :tag "Name")))))))))
+
+(defcustom auth-source-gpg-encrypt-to t
+  "List of recipient keys that `authinfo.gpg' encrypted to.
+If the value is not a list, symmetric encryption will be used."
+  :group 'auth-source
+  :version "24.1" ;; No Gnus
+  :type '(choice (const :tag "Symmetric encryption" t)
+                 (repeat :tag "Recipient public keys"
+                         (string :tag "Recipient public key"))))
+
+;; temp for debugging
+;; (unintern 'auth-source-protocols)
+;; (unintern 'auth-sources)
+;; (customize-variable 'auth-sources)
+;; (setq auth-sources nil)
+;; (format "%S" auth-sources)
+;; (customize-variable 'auth-source-protocols)
+;; (setq auth-source-protocols nil)
+;; (format "%S" auth-source-protocols)
+;; (auth-source-pick nil :host "a" :port 'imap)
+;; (auth-source-user-or-password "login" "imap.myhost.com" 'imap)
+;; (auth-source-user-or-password "password" "imap.myhost.com" 'imap)
+;; (auth-source-user-or-password-imap "login" "imap.myhost.com")
+;; (auth-source-user-or-password-imap "password" "imap.myhost.com")
+;; (auth-source-protocol-defaults 'imap)
+
+;; (let ((auth-source-debug 'debug)) (auth-source-do-debug "hello"))
+;; (let ((auth-source-debug t)) (auth-source-do-debug "hello"))
+;; (let ((auth-source-debug nil)) (auth-source-do-debug "hello"))
+(defun auth-source-do-debug (&rest msg)
+  (when auth-source-debug
+    (apply #'auth-source-do-warn msg)))
+
+(defun auth-source-do-trivia (&rest msg)
+  (when (or (eq auth-source-debug 'trivia)
+            (functionp auth-source-debug))
+    (apply #'auth-source-do-warn msg)))
+
+(defun auth-source-do-warn (&rest msg)
+  (apply
+   ;; set logger to either the function in auth-source-debug or 'message
+   ;; note that it will be 'message if auth-source-debug is nil
+   (if (functionp auth-source-debug)
+       auth-source-debug
+     'message)
+   msg))
+
+
+;; (auth-source-read-char-choice "enter choice? " '(?a ?b ?q))
+(defun auth-source-read-char-choice (prompt choices)
+  "Read one of CHOICES by `read-char-choice', or `read-char'.
+`dropdown-list' support is disabled because it doesn't work reliably.
+Only one of CHOICES will be returned.  The PROMPT is augmented
+with \"[a/b/c] \" if CHOICES is \(?a ?b ?c)."
+  (when choices
+    (let* ((prompt-choices
+            (apply #'concat (loop for c in choices
+                                 collect (format "%c/" c))))
+           (prompt-choices (concat "[" (substring prompt-choices 0 -1) "] "))
+           (full-prompt (concat prompt prompt-choices))
+           k)
+
+      (while (not (memq k choices))
+        (setq k (read-char-choice full-prompt choices)))
+      k)))
+
+;; (auth-source-pick nil :host "any" :port 'imap :user "joe")
+;; (auth-source-pick t :host "any" :port 'imap :user "joe")
+;; (setq auth-sources '((:source (:secrets default) :host t :port t :user "joe")
+;;                   (:source (:secrets "session") :host t :port t :user "joe")
+;;                   (:source (:secrets "Login") :host t :port t)
+;;                   (:source "~/.authinfo.gpg" :host t :port t)))
+
+;; (setq auth-sources '((:source (:secrets default) :host t :port t :user "joe")
+;;                   (:source (:secrets "session") :host t :port t :user "joe")
+;;                   (:source (:secrets "Login") :host t :port t)
+;;                   ))
+
+;; (setq auth-sources '((:source "~/.authinfo.gpg" :host t :port t)))
+
+;; (auth-source-backend-parse "myfile.gpg")
+;; (auth-source-backend-parse 'default)
+;; (auth-source-backend-parse "secrets:Login")
+;; (auth-source-backend-parse 'macos-keychain-internet)
+;; (auth-source-backend-parse 'macos-keychain-generic)
+;; (auth-source-backend-parse "macos-keychain-internet:/path/here.keychain")
+;; (auth-source-backend-parse "macos-keychain-generic:/path/here.keychain")
+
+(defun auth-source-backend-parse (entry)
+  "Creates an auth-source-backend from an ENTRY in `auth-sources'."
+  (auth-source-backend-parse-parameters
+   entry
+   (cond
+    ;; take 'default and recurse to get it as a Secrets API default collection
+    ;; matching any user, host, and protocol
+    ((eq entry 'default)
+     (auth-source-backend-parse '(:source (:secrets default))))
+    ;; take secrets:XYZ and recurse to get it as Secrets API collection "XYZ"
+    ;; matching any user, host, and protocol
+    ((and (stringp entry) (string-match "^secrets:\\(.+\\)" entry))
+     (auth-source-backend-parse `(:source (:secrets ,(match-string 1 entry)))))
+
+    ;; take 'macos-keychain-internet and recurse to get it as a Mac OS
+    ;; Keychain collection matching any user, host, and protocol
+    ((eq entry 'macos-keychain-internet)
+     (auth-source-backend-parse '(:source (:macos-keychain-internet default))))
+    ;; take 'macos-keychain-generic and recurse to get it as a Mac OS
+    ;; Keychain collection matching any user, host, and protocol
+    ((eq entry 'macos-keychain-generic)
+     (auth-source-backend-parse '(:source (:macos-keychain-generic default))))
+    ;; take macos-keychain-internet:XYZ and recurse to get it as MacOS
+    ;; Keychain "XYZ" matching any user, host, and protocol
+    ((and (stringp entry) (string-match "^macos-keychain-internet:\\(.+\\)"
+                                        entry))
+     (auth-source-backend-parse `(:source (:macos-keychain-internet
+                                           ,(match-string 1 entry)))))
+    ;; take macos-keychain-generic:XYZ and recurse to get it as MacOS
+    ;; Keychain "XYZ" matching any user, host, and protocol
+    ((and (stringp entry) (string-match "^macos-keychain-generic:\\(.+\\)"
+                                        entry))
+     (auth-source-backend-parse `(:source (:macos-keychain-generic
+                                           ,(match-string 1 entry)))))
+
+    ;; take just a file name and recurse to get it as a netrc file
+    ;; matching any user, host, and protocol
+    ((stringp entry)
+     (auth-source-backend-parse `(:source ,entry)))
+
+    ;; a file name with parameters
+    ((stringp (plist-get entry :source))
+     (if (equal (file-name-extension (plist-get entry :source)) "plist")
+         (auth-source-backend
+          (plist-get entry :source)
+          :source (plist-get entry :source)
+          :type 'plstore
+          :search-function #'auth-source-plstore-search
+          :create-function #'auth-source-plstore-create
+          :data (plstore-open (plist-get entry :source)))
+       (auth-source-backend
+        (plist-get entry :source)
+        :source (plist-get entry :source)
+        :type 'netrc
+        :search-function #'auth-source-netrc-search
+        :create-function #'auth-source-netrc-create)))
+
+    ;; the MacOS Keychain
+    ((and
+      (not (null (plist-get entry :source))) ; the source must not be nil
+      (listp (plist-get entry :source))      ; and it must be a list
+      (or
+       (plist-get (plist-get entry :source) :macos-keychain-generic)
+       (plist-get (plist-get entry :source) :macos-keychain-internet)))
+
+     (let* ((source-spec (plist-get entry :source))
+            (keychain-generic (plist-get source-spec :macos-keychain-generic))
+            (keychain-type (if keychain-generic
+                               'macos-keychain-generic
+                             'macos-keychain-internet))
+            (source (plist-get source-spec (if keychain-generic
+                                               :macos-keychain-generic
+                                             :macos-keychain-internet))))
+
+       (when (symbolp source)
+         (setq source (symbol-name source)))
+
+       (auth-source-backend
+        (format "Mac OS Keychain (%s)" source)
+        :source source
+        :type keychain-type
+        :search-function #'auth-source-macos-keychain-search
+        :create-function #'auth-source-macos-keychain-create)))
+
+    ;; the Secrets API.  We require the package, in order to have a
+    ;; defined value for `secrets-enabled'.
+    ((and
+      (not (null (plist-get entry :source))) ; the source must not be nil
+      (listp (plist-get entry :source))      ; and it must be a list
+      (require 'secrets nil t)               ; and we must load the Secrets API
+      secrets-enabled)                       ; and that API must be enabled
+
+     ;; the source is either the :secrets key in ENTRY or
+     ;; if that's missing or nil, it's "session"
+     (let ((source (or (plist-get (plist-get entry :source) :secrets)
+                       "session")))
+
+       ;; if the source is a symbol, we look for the alias named so,
+       ;; and if that alias is missing, we use "Login"
+       (when (symbolp source)
+         (setq source (or (secrets-get-alias (symbol-name source))
+                          "Login")))
+
+       (if (featurep 'secrets)
+           (auth-source-backend
+            (format "Secrets API (%s)" source)
+            :source source
+            :type 'secrets
+            :search-function #'auth-source-secrets-search
+            :create-function #'auth-source-secrets-create)
+         (auth-source-do-warn
+          "auth-source-backend-parse: no Secrets API, ignoring spec: %S" entry)
+         (auth-source-backend
+          (format "Ignored Secrets API (%s)" source)
+          :source ""
+          :type 'ignore))))
+
+    ;; none of them
+    (t
+     (auth-source-do-warn
+      "auth-source-backend-parse: invalid backend spec: %S" entry)
+     (make-instance 'auth-source-backend
+      :source ""
+      :type 'ignore)))))
+
+(defun auth-source-backend-parse-parameters (entry backend)
+  "Fills in the extra auth-source-backend parameters of ENTRY.
+Using the plist ENTRY, get the :host, :port, and :user search
+parameters."
+  (let ((entry (if (stringp entry)
+                   nil
+                 entry))
+        val)
+    (when (setq val (plist-get entry :host))
+      (oset backend host val))
+    (when (setq val (plist-get entry :user))
+      (oset backend user val))
+    (when (setq val (plist-get entry :port))
+      (oset backend port val)))
+  backend)
+
+;; (mapcar 'auth-source-backend-parse auth-sources)
+
+(defun* auth-source-search (&rest spec
+                                  &key max
+                                  require create delete
+                                  &allow-other-keys)
+  "Search or modify authentication backends according to SPEC.
+
+This function parses `auth-sources' for matches of the SPEC
+plist.  It can optionally create or update an authentication
+token if requested.  A token is just a standard Emacs property
+list with a :secret property that can be a function; all the
+other properties will always hold scalar values.
+
+Typically the :secret property, if present, contains a password.
+
+Common search keys are :max, :host, :port, and :user.  In
+addition, :create specifies if and how tokens will be created.
+Finally, :type can specify which backend types you want to check.
+
+A string value is always matched literally.  A symbol is matched
+as its string value, literally.  All the SPEC values can be
+single values (symbol or string) or lists thereof (in which case
+any of the search terms matches).
+
+:create t means to create a token if possible.
+
+A new token will be created if no matching tokens were found.
+The new token will have only the keys the backend requires.  For
+the netrc backend, for instance, that's the user, host, and
+port keys.
+
+Here's an example:
+
+\(let ((auth-source-creation-defaults \\='((user . \"defaultUser\")
+                                        (A    . \"default A\"))))
+  (auth-source-search :host \"mine\" :type \\='netrc :max 1
+                      :P \"pppp\" :Q \"qqqq\"
+                      :create t))
+
+which says:
+
+\"Search for any entry matching host `mine' in backends of type
+ `netrc', maximum one result.
+
+ Create a new entry if you found none.  The netrc backend will
+ automatically require host, user, and port.  The host will be
+ `mine'.  We prompt for the user with default `defaultUser' and
+ for the port without a default.  We will not prompt for A, Q,
+ or P.  The resulting token will only have keys user, host, and
+ port.\"
+
+:create \\='(A B C) also means to create a token if possible.
+
+The behavior is like :create t but if the list contains any
+parameter, that parameter will be required in the resulting
+token.  The value for that parameter will be obtained from the
+search parameters or from user input.  If any queries are needed,
+the alist `auth-source-creation-defaults' will be checked for the
+default value.  If the user, host, or port are missing, the alist
+`auth-source-creation-prompts' will be used to look up the
+prompts IN THAT ORDER (so the `user' prompt will be queried first,
+then `host', then `port', and finally `secret').  Each prompt string
+can use %u, %h, and %p to show the user, host, and port.
+
+Here's an example:
+
+\(let ((auth-source-creation-defaults \\='((user . \"defaultUser\")
+                                        (A    . \"default A\")))
+       (auth-source-creation-prompts
+        \\='((password . \"Enter IMAP password for %h:%p: \"))))
+  (auth-source-search :host \\='(\"nonesuch\" \"twosuch\") :type \\='netrc :max 1
+                      :P \"pppp\" :Q \"qqqq\"
+                      :create \\='(A B Q)))
+
+which says:
+
+\"Search for any entry matching host `nonesuch'
+ or `twosuch' in backends of type `netrc', maximum one result.
+
+ Create a new entry if you found none.  The netrc backend will
+ automatically require host, user, and port.  The host will be
+ `nonesuch' and Q will be `qqqq'.  We prompt for the password
+ with the shown prompt.  We will not prompt for Q.  The resulting
+ token will have keys user, host, port, A, B, and Q.  It will not
+ have P with any value, even though P is used in the search to
+ find only entries that have P set to `pppp'.\"
+
+When multiple values are specified in the search parameter, the
+user is prompted for which one.  So :host (X Y Z) would ask the
+user to choose between X, Y, and Z.
+
+This creation can fail if the search was not specific enough to
+create a new token (it's up to the backend to decide that).  You
+should `catch' the backend-specific error as usual.  Some
+backends (netrc, at least) will prompt the user rather than throw
+an error.
+
+:require (A B C) means that only results that contain those
+tokens will be returned.  Thus for instance requiring :secret
+will ensure that any results will actually have a :secret
+property.
+
+:delete t means to delete any found entries.  nil by default.
+Use `auth-source-delete' in ELisp code instead of calling
+`auth-source-search' directly with this parameter.
+
+:type (X Y Z) will check only those backend types.  `netrc' and
+`secrets' are the only ones supported right now.
+
+:max N means to try to return at most N items (defaults to 1).
+More than N items may be returned, depending on the search and
+the backend.
+
+When :max is 0 the function will return just t or nil to indicate
+if any matches were found.
+
+:host (X Y Z) means to match only hosts X, Y, or Z according to
+the match rules above.  Defaults to t.
+
+:user (X Y Z) means to match only users X, Y, or Z according to
+the match rules above.  Defaults to t.
+
+:port (P Q R) means to match only protocols P, Q, or R.
+Defaults to t.
+
+:K (V1 V2 V3) for any other key K will match values V1, V2, or
+V3 (note the match rules above).
+
+The return value is a list with at most :max tokens.  Each token
+is a plist with keys :backend :host :port :user, plus any other
+keys provided by the backend (notably :secret).  But note the
+exception for :max 0, which see above.
+
+The token can hold a :save-function key.  If you call that, the
+user will be prompted to save the data to the backend.  You can't
+request that this should happen right after creation, because
+`auth-source-search' has no way of knowing if the token is
+actually useful.  So the caller must arrange to call this function.
+
+The token's :secret key can hold a function.  In that case you
+must call it to obtain the actual value."
+  (let* ((backends (mapcar #'auth-source-backend-parse auth-sources))
+         (max (or max 1))
+         (ignored-keys '(:require :create :delete :max))
+         (keys (loop for i below (length spec) by 2
+                     unless (memq (nth i spec) ignored-keys)
+                     collect (nth i spec)))
+         (cached (auth-source-remembered-p spec))
+         ;; note that we may have cached results but found is still nil
+         ;; (there were no results from the search)
+         (found (auth-source-recall spec))
+         filtered-backends)
+
+    (if (and cached auth-source-do-cache)
+        (auth-source-do-debug
+         "auth-source-search: found %d CACHED results matching %S"
+         (length found) spec)
+
+      (assert
+       (or (eq t create) (listp create)) t
+       "Invalid auth-source :create parameter (must be t or a list): %s %s")
+
+      (assert
+       (listp require) t
+       "Invalid auth-source :require parameter (must be a list): %s")
+
+      (setq filtered-backends (copy-sequence backends))
+      (dolist (backend backends)
+        (dolist (key keys)
+          ;; ignore invalid slots
+          (condition-case nil
+              (unless (auth-source-search-collection
+                       (plist-get spec key)
+                       (slot-value backend key))
+                (setq filtered-backends (delq backend filtered-backends))
+                (return))
+            (invalid-slot-name nil))))
+
+      (auth-source-do-trivia
+       "auth-source-search: found %d backends matching %S"
+       (length filtered-backends) spec)
+
+      ;; (debug spec "filtered" filtered-backends)
+      ;; First go through all the backends without :create, so we can
+      ;; query them all.
+      (setq found (auth-source-search-backends filtered-backends
+                                               spec
+                                               ;; to exit early
+                                               max
+                                               ;; create is always nil here
+                                               nil delete
+                                               require))
+
+      (auth-source-do-debug
+       "auth-source-search: found %d results (max %d) matching %S"
+       (length found) max spec)
+
+      ;; If we didn't find anything, then we allow the backend(s) to
+      ;; create the entries.
+      (when (and create
+                 (not found))
+        (setq found (auth-source-search-backends filtered-backends
+                                                 spec
+                                                 ;; to exit early
+                                                 max
+                                                 create delete
+                                                 require))
+        (auth-source-do-debug
+         "auth-source-search: CREATED %d results (max %d) matching %S"
+         (length found) max spec))
+
+      ;; note we remember the lack of result too, if it's applicable
+      (when auth-source-do-cache
+        (auth-source-remember spec found)))
+
+    (if (zerop max)
+        (not (null found))
+      found)))
+
+(defun auth-source-search-backends (backends spec max create delete require)
+  (let ((max (if (zerop max) 1 max)) ; stop with 1 match if we're asked for zero
+        matches)
+    (dolist (backend backends)
+      (when (> max (length matches)) ; if we need more matches...
+        (let* ((bmatches (apply
+                          (slot-value backend 'search-function)
+                          :backend backend
+                          :type (slot-value backend 'type)
+                          ;; note we're overriding whatever the spec
+                          ;; has for :max, :require, :create, and :delete
+                          :max max
+                          :require require
+                          :create create
+                          :delete delete
+                          spec)))
+          (when bmatches
+            (auth-source-do-trivia
+             "auth-source-search-backend: got %d (max %d) in %s:%s matching %S"
+             (length bmatches) max
+             (slot-value backend 'type)
+             (slot-value backend 'source)
+             spec)
+            (setq matches (append matches bmatches))))))
+    matches))
+
+;; (auth-source-search :max 0)
+;; (auth-source-search :max 1)
+;; (funcall (plist-get (nth 0 (auth-source-search :max 1)) :secret))
+;; (auth-source-search :host "nonesuch" :type 'netrc :K 1)
+;; (auth-source-search :host "nonesuch" :type 'secrets)
+
+(defun auth-source-delete (&rest spec)
+  "Delete entries from the authentication backends according to SPEC.
+Calls `auth-source-search' with the :delete property in SPEC set to t.
+The backend may not actually delete the entries.
+
+Returns the deleted entries."
+  (auth-source-search (plist-put spec :delete t)))
+
+(defun auth-source-search-collection (collection value)
+  "Returns t is VALUE is t or COLLECTION is t or COLLECTION contains VALUE."
+  (when (and (atom collection) (not (eq t collection)))
+    (setq collection (list collection)))
+
+  ;; (debug :collection collection :value value)
+  (or (eq collection t)
+      (eq value t)
+      (equal collection value)
+      (member value collection)))
+
+(defvar auth-source-netrc-cache nil)
+
+(defun auth-source-forget-all-cached ()
+  "Forget all cached auth-source data."
+  (interactive)
+  (loop for sym being the symbols of password-data
+        ;; when the symbol name starts with auth-source-magic
+        when (string-match (concat "^" auth-source-magic)
+                           (symbol-name sym))
+        ;; remove that key
+        do (password-cache-remove (symbol-name sym)))
+  (setq auth-source-netrc-cache nil))
+
+(defun auth-source-format-cache-entry (spec)
+  "Format SPEC entry to put it in the password cache."
+  (concat auth-source-magic (format "%S" spec)))
+
+(defun auth-source-remember (spec found)
+  "Remember FOUND search results for SPEC."
+  (let ((password-cache-expiry auth-source-cache-expiry))
+    (password-cache-add
+     (auth-source-format-cache-entry spec) found)))
+
+(defun auth-source-recall (spec)
+  "Recall FOUND search results for SPEC."
+  (password-read-from-cache (auth-source-format-cache-entry spec)))
+
+(defun auth-source-remembered-p (spec)
+  "Check if SPEC is remembered."
+  (password-in-cache-p
+   (auth-source-format-cache-entry spec)))
+
+(defun auth-source-forget (spec)
+  "Forget any cached data matching SPEC exactly.
+
+This is the same SPEC you passed to `auth-source-search'.
+Returns t or nil for forgotten or not found."
+  (password-cache-remove (auth-source-format-cache-entry spec)))
+
+;; (loop for sym being the symbols of password-data when (string-match (concat "^" auth-source-magic) (symbol-name sym)) collect (symbol-name sym))
+
+;; (auth-source-remember '(:host "wedd") '(4 5 6))
+;; (auth-source-remembered-p '(:host "wedd"))
+;; (auth-source-remember '(:host "xedd") '(1 2 3))
+;; (auth-source-remembered-p '(:host "xedd"))
+;; (auth-source-remembered-p '(:host "zedd"))
+;; (auth-source-recall '(:host "xedd"))
+;; (auth-source-recall '(:host t))
+;; (auth-source-forget+ :host t)
+
+(defun auth-source-forget+ (&rest spec)
+  "Forget any cached data matching SPEC.  Returns forgotten count.
+
+This is not a full `auth-source-search' spec but works similarly.
+For instance, \(:host \"myhost\" \"yourhost\") would find all the
+cached data that was found with a search for those two hosts,
+while \(:host t) would find all host entries."
+  (let ((count 0)
+        sname)
+    (loop for sym being the symbols of password-data
+          ;; when the symbol name matches with auth-source-magic
+          when (and (setq sname (symbol-name sym))
+                    (string-match (concat "^" auth-source-magic "\\(.+\\)")
+                                  sname)
+                    ;; and the spec matches what was stored in the cache
+                    (auth-source-specmatchp spec (read (match-string 1 sname))))
+          ;; remove that key
+          do (progn
+               (password-cache-remove sname)
+               (incf count)))
+    count))
+
+(defun auth-source-specmatchp (spec stored)
+  (let ((keys (loop for i below (length spec) by 2
+                    collect (nth i spec))))
+    (not (eq
+          (dolist (key keys)
+            (unless (auth-source-search-collection (plist-get stored key)
+                                                   (plist-get spec key))
+              (return 'no)))
+          'no))))
+
+;; (auth-source-pick-first-password :host "z.lifelogs.com")
+;; (auth-source-pick-first-password :port "imap")
+(defun auth-source-pick-first-password (&rest spec)
+  "Pick the first secret found from applying SPEC to `auth-source-search'."
+  (let* ((result (nth 0 (apply #'auth-source-search (plist-put spec :max 1))))
+         (secret (plist-get result :secret)))
+
+    (if (functionp secret)
+        (funcall secret)
+      secret)))
+
+;; (auth-source-format-prompt "test %u %h %p" '((?u "user") (?h "host")))
+(defun auth-source-format-prompt (prompt alist)
+  "Format PROMPT using %x (for any character x) specifiers in ALIST."
+  (dolist (cell alist)
+    (let ((c (nth 0 cell))
+          (v (nth 1 cell)))
+      (when (and c v)
+        (setq prompt (replace-regexp-in-string (format "%%%c" c)
+                                               (format "%s" v)
+                                               prompt nil t)))))
+  prompt)
+
+(defun auth-source-ensure-strings (values)
+  (if (eq values t)
+      values
+    (unless (listp values)
+      (setq values (list values)))
+    (mapcar (lambda (value)
+             (if (numberp value)
+                 (format "%s" value)
+               value))
+           values)))
+
+;;; Backend specific parsing: netrc/authinfo backend
+
+(defun auth-source--aput-1 (alist key val)
+  (let ((seen ())
+        (rest alist))
+    (while (and (consp rest) (not (equal key (caar rest))))
+      (push (pop rest) seen))
+    (cons (cons key val)
+          (if (null rest) alist
+            (nconc (nreverse seen)
+                   (if (equal key (caar rest)) (cdr rest) rest))))))
+(defmacro auth-source--aput (var key val)
+  `(setq ,var (auth-source--aput-1 ,var ,key ,val)))
+
+(defun auth-source--aget (alist key)
+  (cdr (assoc key alist)))
+
+;; (auth-source-netrc-parse :file "~/.authinfo.gpg")
+(defun* auth-source-netrc-parse (&key file max host user port require
+                                 &allow-other-keys)
+  "Parse FILE and return a list of all entries in the file.
+Note that the MAX parameter is used so we can exit the parse early."
+  (if (listp file)
+      ;; We got already parsed contents; just return it.
+      file
+    (when (file-exists-p file)
+      (setq port (auth-source-ensure-strings port))
+      (with-temp-buffer
+        (let* ((max (or max 5000))       ; sanity check: default to stop at 5K
+               (modified 0)
+               (cached (cdr-safe (assoc file auth-source-netrc-cache)))
+               (cached-mtime (plist-get cached :mtime))
+               (cached-secrets (plist-get cached :secret))
+               (check (lambda(alist)
+                        (and alist
+                             (auth-source-search-collection
+                              host
+                              (or
+                               (auth-source--aget alist "machine")
+                               (auth-source--aget alist "host")
+                               t))
+                             (auth-source-search-collection
+                              user
+                              (or
+                               (auth-source--aget alist "login")
+                               (auth-source--aget alist "account")
+                               (auth-source--aget alist "user")
+                               t))
+                             (auth-source-search-collection
+                              port
+                              (or
+                               (auth-source--aget alist "port")
+                               (auth-source--aget alist "protocol")
+                               t))
+                             (or
+                              ;; the required list of keys is nil, or
+                              (null require)
+                              ;; every element of require is in n(ormalized)
+                              (let ((n (nth 0 (auth-source-netrc-normalize
+                                               (list alist) file))))
+                                (loop for req in require
+                                      always (plist-get n req)))))))
+               result)
+
+          (if (and (functionp cached-secrets)
+                   (equal cached-mtime
+                          (nth 5 (file-attributes file))))
+              (progn
+                (auth-source-do-trivia
+                 "auth-source-netrc-parse: using CACHED file data for %s"
+                 file)
+                (insert (funcall cached-secrets)))
+            (insert-file-contents file)
+            ;; cache all netrc files (used to be just .gpg files)
+            ;; Store the contents of the file heavily encrypted in memory.
+            ;; (note for the irony-impaired: they are just obfuscated)
+            (auth-source--aput
+             auth-source-netrc-cache file
+             (list :mtime (nth 5 (file-attributes file))
+                   :secret (lexical-let ((v (mapcar #'1+ (buffer-string))))
+                             (lambda () (apply #'string (mapcar #'1- v)))))))
+          (goto-char (point-min))
+          (let ((entries (auth-source-netrc-parse-entries check max))
+                alist)
+            (while (setq alist (pop entries))
+                (push (nreverse alist) result)))
+
+          (when (< 0 modified)
+            (when auth-source-gpg-encrypt-to
+              ;; (see bug#7487) making `epa-file-encrypt-to' local to
+              ;; this buffer lets epa-file skip the key selection query
+              ;; (see the `local-variable-p' check in
+              ;; `epa-file-write-region').
+              (unless (local-variable-p 'epa-file-encrypt-to (current-buffer))
+                (make-local-variable 'epa-file-encrypt-to))
+              (if (listp auth-source-gpg-encrypt-to)
+                  (setq epa-file-encrypt-to auth-source-gpg-encrypt-to)))
+
+            ;; ask AFTER we've successfully opened the file
+            (when (y-or-n-p (format "Save file %s? (%d deletions)"
+                                    file modified))
+              (write-region (point-min) (point-max) file nil 'silent)
+              (auth-source-do-debug
+               "auth-source-netrc-parse: modified %d lines in %s"
+               modified file)))
+
+          (nreverse result))))))
+
+(defun auth-source-netrc-parse-next-interesting ()
+  "Advance to the next interesting position in the current buffer."
+  ;; If we're looking at a comment or are at the end of the line, move forward
+  (while (or (looking-at "#")
+             (and (eolp)
+                  (not (eobp))))
+    (forward-line 1))
+  (skip-chars-forward "\t "))
+
+(defun auth-source-netrc-parse-one ()
+  "Read one thing from the current buffer."
+  (auth-source-netrc-parse-next-interesting)
+
+  (when (or (looking-at "'\\([^']*\\)'")
+            (looking-at "\"\\([^\"]*\\)\"")
+            (looking-at "\\([^ \t\n]+\\)"))
+    (forward-char (length (match-string 0)))
+    (auth-source-netrc-parse-next-interesting)
+    (match-string-no-properties 1)))
+
+;; with thanks to org-mode
+(defsubst auth-source-current-line (&optional pos)
+  (save-excursion
+    (and pos (goto-char pos))
+    ;; works also in narrowed buffer, because we start at 1, not point-min
+    (+ (if (bolp) 1 0) (count-lines 1 (point)))))
+
+(defun auth-source-netrc-parse-entries(check max)
+  "Parse up to MAX netrc entries, passed by CHECK, from the current buffer."
+  (let ((adder (lambda(check alist all)
+                 (when (and
+                        alist
+                        (> max (length all))
+                        (funcall check alist))
+                   (push alist all))
+                 all))
+        item item2 all alist default)
+    (while (setq item (auth-source-netrc-parse-one))
+      (setq default (equal item "default"))
+      ;; We're starting a new machine.  Save the old one.
+      (when (and alist
+                 (or default
+                     (equal item "machine")))
+        ;; (auth-source-do-trivia
+        ;;  "auth-source-netrc-parse-entries: got entry %S" alist)
+        (setq all (funcall adder check alist all)
+              alist nil))
+      ;; In default entries, we don't have a next token.
+      ;; We store them as ("machine" . t)
+      (if default
+          (push (cons "machine" t) alist)
+        ;; Not a default entry.  Grab the next item.
+        (when (setq item2 (auth-source-netrc-parse-one))
+          ;; Did we get a "machine" value?
+          (if (equal item2 "machine")
+             (error
+              "%s: Unexpected `machine' token at line %d"
+              "auth-source-netrc-parse-entries"
+              (auth-source-current-line))
+            (push (cons item item2) alist)))))
+
+    ;; Clean up: if there's an entry left over, use it.
+    (when alist
+      (setq all (funcall adder check alist all))
+      ;; (auth-source-do-trivia
+      ;;  "auth-source-netrc-parse-entries: got2 entry %S" alist)
+      )
+    (nreverse all)))
+
+(defvar auth-source-passphrase-alist nil)
+
+(defun auth-source-token-passphrase-callback-function (_context _key-id file)
+  (let* ((file (file-truename file))
+        (entry (assoc file auth-source-passphrase-alist))
+        passphrase)
+    ;; return the saved passphrase, calling a function if needed
+    (or (copy-sequence (if (functionp (cdr entry))
+                          (funcall (cdr entry))
+                        (cdr entry)))
+       (progn
+         (unless entry
+           (setq entry (list file))
+           (push entry auth-source-passphrase-alist))
+         (setq passphrase
+               (read-passwd
+                (format "Passphrase for %s tokens: " file)
+                t))
+         (setcdr entry (lexical-let ((p (copy-sequence passphrase)))
+                         (lambda () p)))
+         passphrase))))
+
+;; (auth-source-epa-extract-gpg-token "gpg:LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tClZlcnNpb246IEdudVBHIHYxLjQuMTEgKEdOVS9MaW51eCkKCmpBMEVBd01DT25qMjB1ak9rZnRneVI3K21iNm9aZWhuLzRad3cySkdlbnVaKzRpeEswWDY5di9icDI1U1dsQT0KPS9yc2wKLS0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo=" "~/.netrc")
+(defun auth-source-epa-extract-gpg-token (secret file)
+  "Pass either the decoded SECRET or the gpg:BASE64DATA version.
+FILE is the file from which we obtained this token."
+  (when (string-match "^gpg:\\(.+\\)" secret)
+    (setq secret (base64-decode-string (match-string 1 secret))))
+  (let ((context (epg-make-context 'OpenPGP)))
+    (epg-context-set-passphrase-callback
+     context
+     (cons #'auth-source-token-passphrase-callback-function
+           file))
+    (epg-decrypt-string context secret)))
+
+(defvar pp-escape-newlines)
+
+;; (insert (auth-source-epa-make-gpg-token "mysecret" "~/.netrc"))
+(defun auth-source-epa-make-gpg-token (secret file)
+  (let ((context (epg-make-context 'OpenPGP))
+        (pp-escape-newlines nil)
+        cipher)
+    (setf (epg-context-armor context) t)
+    (epg-context-set-passphrase-callback
+     context
+     (cons #'auth-source-token-passphrase-callback-function
+           file))
+    (setq cipher (epg-encrypt-string context secret nil))
+    (with-temp-buffer
+      (insert cipher)
+      (base64-encode-region (point-min) (point-max) t)
+      (concat "gpg:" (buffer-substring-no-properties
+                      (point-min)
+                      (point-max))))))
+
+(defun auto-source--symbol-keyword (symbol)
+  (intern (format ":%s" symbol)))
+
+(defun auth-source-netrc-normalize (alist filename)
+  (mapcar (lambda (entry)
+            (let (ret item)
+              (while (setq item (pop entry))
+                (let ((k (car item))
+                      (v (cdr item)))
+
+                  ;; apply key aliases
+                  (setq k (cond ((member k '("machine")) "host")
+                                ((member k '("login" "account")) "user")
+                                ((member k '("protocol")) "port")
+                                ((member k '("password")) "secret")
+                                (t k)))
+
+                  ;; send back the secret in a function (lexical binding)
+                  (when (equal k "secret")
+                    (setq v (lexical-let ((lexv v)
+                                          (token-decoder nil))
+                              (when (string-match "^gpg:" lexv)
+                                ;; it's a GPG token: create a token decoder
+                                ;; which unsets itself once
+                                (setq token-decoder
+                                      (lambda (val)
+                                        (prog1
+                                            (auth-source-epa-extract-gpg-token
+                                             val
+                                             filename)
+                                          (setq token-decoder nil)))))
+                              (lambda ()
+                                (when token-decoder
+                                  (setq lexv (funcall token-decoder lexv)))
+                                lexv))))
+                  (setq ret (plist-put ret
+                                       (auto-source--symbol-keyword k)
+                                       v))))
+              ret))
+          alist))
+
+;; (setq secret (plist-get (nth 0 (auth-source-search :host t :type 'netrc :K 1 :max 1)) :secret))
+;; (funcall secret)
+
+(defun* auth-source-netrc-search (&rest
+                                  spec
+                                  &key backend require create
+                                  type max host user port
+                                  &allow-other-keys)
+  "Given a property list SPEC, return search matches from the :backend.
+See `auth-source-search' for details on SPEC."
+  ;; just in case, check that the type is correct (null or same as the backend)
+  (assert (or (null type) (eq type (oref backend type)))
+          t "Invalid netrc search: %s %s")
+
+  (let ((results (auth-source-netrc-normalize
+                  (auth-source-netrc-parse
+                   :max max
+                   :require require
+                   :file (oref backend source)
+                   :host (or host t)
+                   :user (or user t)
+                   :port (or port t))
+                  (oref backend source))))
+
+    ;; if we need to create an entry AND none were found to match
+    (when (and create
+               (not results))
+
+      ;; create based on the spec and record the value
+      (setq results (or
+                     ;; if the user did not want to create the entry
+                     ;; in the file, it will be returned
+                     (apply (slot-value backend 'create-function) spec)
+                     ;; if not, we do the search again without :create
+                     ;; to get the updated data.
+
+                     ;; the result will be returned, even if the search fails
+                     (apply #'auth-source-netrc-search
+                            (plist-put spec :create nil)))))
+    results))
+
+(defun auth-source-netrc-element-or-first (v)
+  (if (listp v)
+      (nth 0 v)
+    v))
+
+;; (auth-source-search :host "nonesuch" :type 'netrc :max 1 :create t)
+;; (auth-source-search :host "nonesuch" :type 'netrc :max 1 :create t :create-extra-keys '((A "default A") (B)))
+
+(defun* auth-source-netrc-create (&rest spec
+                                        &key backend
+                                        host port create
+                                        &allow-other-keys)
+  (let* ((base-required '(host user port secret))
+         ;; we know (because of an assertion in auth-source-search) that the
+         ;; :create parameter is either t or a list (which includes nil)
+         (create-extra (if (eq t create) nil create))
+         (current-data (car (auth-source-search :max 1
+                                                :host host
+                                                :port port)))
+         (required (append base-required create-extra))
+         (file (oref backend source))
+         (add "")
+         ;; `valist' is an alist
+         valist
+         ;; `artificial' will be returned if no creation is needed
+         artificial)
+
+    ;; only for base required elements (defined as function parameters):
+    ;; fill in the valist with whatever data we may have from the search
+    ;; we complete the first value if it's a list and use the value otherwise
+    (dolist (br base-required)
+      (let ((val (plist-get spec (auto-source--symbol-keyword br))))
+        (when val
+          (let ((br-choice (cond
+                            ;; all-accepting choice (predicate is t)
+                            ((eq t val) nil)
+                            ;; just the value otherwise
+                            (t val))))
+            (when br-choice
+              (auth-source--aput valist br br-choice))))))
+
+    ;; for extra required elements, see if the spec includes a value for them
+    (dolist (er create-extra)
+      (let ((k (auto-source--symbol-keyword er))
+            (keys (loop for i below (length spec) by 2
+                        collect (nth i spec))))
+        (when (memq k keys)
+          (auth-source--aput valist er (plist-get spec k)))))
+
+    ;; for each required element
+    (dolist (r required)
+      (let* ((data (auth-source--aget valist r))
+             ;; take the first element if the data is a list
+             (data (or (auth-source-netrc-element-or-first data)
+                       (plist-get current-data
+                                  (auto-source--symbol-keyword r))))
+             ;; this is the default to be offered
+             (given-default (auth-source--aget
+                             auth-source-creation-defaults r))
+             ;; the default supplementals are simple:
+             ;; for the user, try `given-default' and then (user-login-name);
+             ;; otherwise take `given-default'
+             (default (cond
+                       ((and (not given-default) (eq r 'user))
+                        (user-login-name))
+                       (t given-default)))
+             (printable-defaults (list
+                                  (cons 'user
+                                        (or
+                                         (auth-source-netrc-element-or-first
+                                          (auth-source--aget valist 'user))
+                                         (plist-get artificial :user)
+                                         "[any user]"))
+                                  (cons 'host
+                                        (or
+                                         (auth-source-netrc-element-or-first
+                                          (auth-source--aget valist 'host))
+                                         (plist-get artificial :host)
+                                         "[any host]"))
+                                  (cons 'port
+                                        (or
+                                         (auth-source-netrc-element-or-first
+                                          (auth-source--aget valist 'port))
+                                         (plist-get artificial :port)
+                                         "[any port]"))))
+             (prompt (or (auth-source--aget auth-source-creation-prompts r)
+                         (case r
+                           (secret "%p password for %u@%h: ")
+                           (user "%p user name for %h: ")
+                           (host "%p host name for user %u: ")
+                           (port "%p port for %u@%h: "))
+                         (format "Enter %s (%%u@%%h:%%p): " r)))
+             (prompt (auth-source-format-prompt
+                      prompt
+                      `((?u ,(auth-source--aget printable-defaults 'user))
+                        (?h ,(auth-source--aget printable-defaults 'host))
+                        (?p ,(auth-source--aget printable-defaults 'port))))))
+
+        ;; Store the data, prompting for the password if needed.
+        (setq data (or data
+                       (if (eq r 'secret)
+                           ;; Special case prompt for passwords.
+                           ;; TODO: make the default (setq auth-source-netrc-use-gpg-tokens `((,(if (boundp 'epa-file-auto-mode-alist-entry) (car epa-file-auto-mode-alist-entry) "\\.gpg\\'") nil) (t gpg)))
+                           ;; TODO: or maybe leave as (setq auth-source-netrc-use-gpg-tokens 'never)
+                           (let* ((ep (format "Use GPG password tokens in %s?" file))
+                                  (gpg-encrypt
+                                   (cond
+                                    ((eq auth-source-netrc-use-gpg-tokens 'never)
+                                     'never)
+                                    ((listp auth-source-netrc-use-gpg-tokens)
+                                     (let ((check (copy-sequence
+                                                   auth-source-netrc-use-gpg-tokens))
+                                           item ret)
+                                       (while check
+                                         (setq item (pop check))
+                                         (when (or (eq (car item) t)
+                                                   (string-match (car item) file))
+                                           (setq ret (cdr item))
+                                           (setq check nil)))
+                                       ;; FIXME: `ret' unused.
+                                       ;; Should we return it here?
+                                       ))
+                                    (t 'never)))
+                                  (plain (or (eval default) (read-passwd prompt))))
+                             ;; ask if we don't know what to do (in which case
+                             ;; auth-source-netrc-use-gpg-tokens must be a list)
+                             (unless gpg-encrypt
+                               (setq gpg-encrypt (if (y-or-n-p ep) 'gpg 'never))
+                               ;; TODO: save the defcustom now? or ask?
+                               (setq auth-source-netrc-use-gpg-tokens
+                                     (cons `(,file ,gpg-encrypt)
+                                           auth-source-netrc-use-gpg-tokens)))
+                             (if (eq gpg-encrypt 'gpg)
+                                 (auth-source-epa-make-gpg-token plain file)
+                               plain))
+                         (if (stringp default)
+                             (read-string (if (string-match ": *\\'" prompt)
+                                              (concat (substring prompt 0 (match-beginning 0))
+                                                      " (default " default "): ")
+                                            (concat prompt "(default " default ") "))
+                                          nil nil default)
+                           (eval default)))))
+
+        (when data
+          (setq artificial (plist-put artificial
+                                      (auto-source--symbol-keyword r)
+                                      (if (eq r 'secret)
+                                          (lexical-let ((data data))
+                                            (lambda () data))
+                                        data))))
+
+        ;; When r is not an empty string...
+        (when (and (stringp data)
+                   (< 0 (length data)))
+          ;; this function is not strictly necessary but I think it
+          ;; makes the code clearer -tzz
+          (let ((printer (lambda ()
+                           ;; append the key (the symbol name of r)
+                           ;; and the value in r
+                           (format "%s%s %s"
+                                   ;; prepend a space
+                                   (if (zerop (length add)) "" " ")
+                                   ;; remap auth-source tokens to netrc
+                                   (case r
+                                     (user   "login")
+                                     (host   "machine")
+                                     (secret "password")
+                                     (port   "port") ; redundant but clearer
+                                     (t (symbol-name r)))
+                                   (if (string-match "[\"# ]" data)
+                                       (format "%S" data)
+                                     data)))))
+            (setq add (concat add (funcall printer)))))))
+
+    (plist-put
+     artificial
+     :save-function
+     (lexical-let ((file file)
+                   (add add))
+       (lambda () (auth-source-netrc-saver file add))))
+
+    (list artificial)))
+
+;;(funcall (plist-get (nth 0 (auth-source-search :host '("nonesuch2") :user "tzz" :port "imap" :create t :max 1)) :save-function))
+(defun auth-source-netrc-saver (file add)
+  "Save a line ADD in FILE, prompting along the way.
+Respects `auth-source-save-behavior'.  Uses
+`auth-source-netrc-cache' to avoid prompting more than once."
+  (let* ((key (format "%s %s" file (rfc2104-hash 'md5 64 16 file add)))
+         (cached (assoc key auth-source-netrc-cache)))
+
+    (if cached
+        (auth-source-do-trivia
+         "auth-source-netrc-saver: found previous run for key %s, returning"
+         key)
+      (with-temp-buffer
+        (when (file-exists-p file)
+          (insert-file-contents file))
+        (when auth-source-gpg-encrypt-to
+          ;; (see bug#7487) making `epa-file-encrypt-to' local to
+          ;; this buffer lets epa-file skip the key selection query
+          ;; (see the `local-variable-p' check in
+          ;; `epa-file-write-region').
+          (unless (local-variable-p 'epa-file-encrypt-to (current-buffer))
+            (make-local-variable 'epa-file-encrypt-to))
+          (if (listp auth-source-gpg-encrypt-to)
+              (setq epa-file-encrypt-to auth-source-gpg-encrypt-to)))
+        ;; we want the new data to be found first, so insert at beginning
+        (goto-char (point-min))
+
+        ;; Ask AFTER we've successfully opened the file.
+        (let ((prompt (format "Save auth info to file %s? " file))
+              (done (not (eq auth-source-save-behavior 'ask)))
+              (bufname "*auth-source Help*")
+              k)
+          (while (not done)
+            (setq k (auth-source-read-char-choice prompt '(?y ?n ?N ?e ??)))
+            (case k
+              (?y (setq done t))
+              (?? (save-excursion
+                    (with-output-to-temp-buffer bufname
+                      (princ
+                       (concat "(y)es, save\n"
+                               "(n)o but use the info\n"
+                               "(N)o and don't ask to save again\n"
+                               "(e)dit the line\n"
+                               "(?) for help as you can see.\n"))
+                      ;; Why?  Doesn't with-output-to-temp-buffer already do
+                      ;; the exact same thing anyway?  --Stef
+                      (set-buffer standard-output)
+                      (help-mode))))
+              (?n (setq add ""
+                        done t))
+              (?N
+               (setq add ""
+                     done t)
+               (customize-save-variable 'auth-source-save-behavior nil))
+              (?e (setq add (read-string "Line to add: " add)))
+              (t nil)))
+
+          (when (get-buffer-window bufname)
+            (delete-window (get-buffer-window bufname)))
+
+          ;; Make sure the info is not saved.
+          (when (null auth-source-save-behavior)
+            (setq add ""))
+
+          (when (< 0 (length add))
+            (progn
+              (unless (bolp)
+                (insert "\n"))
+              (insert add "\n")
+              (write-region (point-min) (point-max) file nil 'silent)
+             ;; Make the .authinfo file non-world-readable.
+             (set-file-modes file #o600)
+              (auth-source-do-debug
+               "auth-source-netrc-create: wrote 1 new line to %s"
+               file)
+              (message "Saved new authentication information to %s" file)
+              nil))))
+      (auth-source--aput auth-source-netrc-cache key "ran"))))
+
+;;; Backend specific parsing: Secrets API backend
+
+;; (let ((auth-sources '(default))) (auth-source-search :max 1 :create t))
+;; (let ((auth-sources '(default))) (auth-source-search :max 1 :delete t))
+;; (let ((auth-sources '(default))) (auth-source-search :max 1))
+;; (let ((auth-sources '(default))) (auth-source-search))
+;; (let ((auth-sources '("secrets:Login"))) (auth-source-search :max 1))
+;; (let ((auth-sources '("secrets:Login"))) (auth-source-search :max 1 :signon_realm "https://git.gnus.org/Git"))
+
+(defun auth-source-secrets-listify-pattern (pattern)
+  "Convert a pattern with lists to a list of string patterns.
+
+auth-source patterns can have values of the form :foo (\"bar\"
+\"qux\"), which means to match any secret with :foo equal to
+\"bar\" or :foo equal to \"qux\".  The secrets backend supports
+only string values for patterns, so this routine returns a list
+of patterns that is equivalent to the single original pattern
+when interpreted such that if a secret matches any pattern in the
+list, it matches the original pattern."
+  (if (null pattern)
+      '(nil)
+    (let* ((key (pop pattern))
+           (value (pop pattern))
+           (tails (auth-source-secrets-listify-pattern pattern))
+           (heads (if (stringp value)
+                      (list (list key value))
+                    (mapcar (lambda (v) (list key v)) value))))
+      (loop
+         for h in heads
+         nconc
+           (loop
+              for tl in tails
+              collect (append h tl))))))
+
+(defun* auth-source-secrets-search (&rest
+                                    spec
+                                    &key backend create delete label max
+                                    &allow-other-keys)
+  "Search the Secrets API; spec is like `auth-source'.
+
+The :label key specifies the item's label.  It is the only key
+that can specify a substring.  Any :label value besides a string
+will allow any label.
+
+All other search keys must match exactly.  If you need substring
+matching, do a wider search and narrow it down yourself.
+
+You'll get back all the properties of the token as a plist.
+
+Here's an example that looks for the first item in the `Login'
+Secrets collection:
+
+ (let ((auth-sources \\='(\"secrets:Login\")))
+    (auth-source-search :max 1)
+
+Here's another that looks for the first item in the `Login'
+Secrets collection whose label contains `gnus':
+
+ (let ((auth-sources \\='(\"secrets:Login\")))
+    (auth-source-search :max 1 :label \"gnus\")
+
+And this one looks for the first item in the `Login' Secrets
+collection that's a Google Chrome entry for the git.gnus.org site
+authentication tokens:
+
+ (let ((auth-sources \\='(\"secrets:Login\")))
+    (auth-source-search :max 1 :signon_realm \"https://git.gnus.org/Git\"))
+"
+
+  ;; TODO
+  (assert (not create) nil
+          "The Secrets API auth-source backend doesn't support creation yet")
+  ;; TODO
+  ;; (secrets-delete-item coll elt)
+  (assert (not delete) nil
+          "The Secrets API auth-source backend doesn't support deletion yet")
+
+  (let* ((coll (oref backend source))
+         (max (or max 5000))     ; sanity check: default to stop at 5K
+         (ignored-keys '(:create :delete :max :backend :label :require :type))
+         (search-keys (loop for i below (length spec) by 2
+                            unless (memq (nth i spec) ignored-keys)
+                            collect (nth i spec)))
+         ;; build a search spec without the ignored keys
+         ;; if a search key is nil or t (match anything), we skip it
+         (search-specs (auth-source-secrets-listify-pattern
+                        (apply #'append (mapcar
+                                      (lambda (k)
+                                        (if (or (null (plist-get spec k))
+                                                (eq t (plist-get spec k)))
+                                            nil
+                                          (list k (plist-get spec k))))
+                                      search-keys))))
+         ;; needed keys (always including host, login, port, and secret)
+         (returned-keys (delete-dups (append
+                                     '(:host :login :port :secret)
+                                     search-keys)))
+         (items
+          (loop for search-spec in search-specs
+               nconc
+               (loop for item in (apply #'secrets-search-items coll search-spec)
+                  unless (and (stringp label)
+                              (not (string-match label item)))
+                  collect item)))
+         ;; TODO: respect max in `secrets-search-items', not after the fact
+         (items (butlast items (- (length items) max)))
+         ;; convert the item name to a full plist
+         (items (mapcar (lambda (item)
+                          (append
+                           ;; make an entry for the secret (password) element
+                           (list
+                            :secret
+                            (lexical-let ((v (secrets-get-secret coll item)))
+                              (lambda () v)))
+                           ;; rewrite the entry from ((k1 v1) (k2 v2)) to plist
+                           (apply #'append
+                                  (mapcar (lambda (entry)
+                                            (list (car entry) (cdr entry)))
+                                          (secrets-get-attributes coll item)))))
+                        items))
+         ;; ensure each item has each key in `returned-keys'
+         (items (mapcar (lambda (plist)
+                          (append
+                           (apply #'append
+                                  (mapcar (lambda (req)
+                                            (if (plist-get plist req)
+                                                nil
+                                              (list req nil)))
+                                          returned-keys))
+                           plist))
+                        items)))
+    items))
+
+(defun auth-source-secrets-create (&rest spec)
+  ;; TODO
+  ;; (apply 'secrets-create-item (auth-get-source entry) name passwd spec)
+  (debug spec))
+
+;;; Backend specific parsing: Mac OS Keychain (using /usr/bin/security) backend
+
+;; (let ((auth-sources '(macos-keychain-internet))) (auth-source-search :max 1 :create t))
+;; (let ((auth-sources '(macos-keychain-internet))) (auth-source-search :max 1 :delete t))
+;; (let ((auth-sources '(macos-keychain-internet))) (auth-source-search :max 1))
+;; (let ((auth-sources '(macos-keychain-internet))) (auth-source-search))
+
+;; (let ((auth-sources '(macos-keychain-generic))) (auth-source-search :max 1 :create t))
+;; (let ((auth-sources '(macos-keychain-generic))) (auth-source-search :max 1 :delete t))
+;; (let ((auth-sources '(macos-keychain-generic))) (auth-source-search :max 1))
+;; (let ((auth-sources '(macos-keychain-generic))) (auth-source-search))
+
+;; (let ((auth-sources '("macos-keychain-internet:/Users/tzz/Library/Keychains/login.keychain"))) (auth-source-search :max 1))
+;; (let ((auth-sources '("macos-keychain-generic:Login"))) (auth-source-search :max 1 :host "git.gnus.org"))
+;; (let ((auth-sources '("macos-keychain-generic:Login"))) (auth-source-search :max 1))
+
+(defun* auth-source-macos-keychain-search (&rest
+                                    spec
+                                    &key backend create delete
+                                    type max
+                                    &allow-other-keys)
+  "Search the MacOS Keychain; spec is like `auth-source'.
+
+All search keys must match exactly.  If you need substring
+matching, do a wider search and narrow it down yourself.
+
+You'll get back all the properties of the token as a plist.
+
+The :type key is either `macos-keychain-internet' or
+`macos-keychain-generic'.
+
+For the internet keychain type, the :label key searches the
+item's labels (\"-l LABEL\" passed to \"/usr/bin/security\").
+Similarly, :host maps to \"-s HOST\", :user maps to \"-a USER\",
+and :port maps to \"-P PORT\" or \"-r PROT\"
+\(note PROT has to be a 4-character string).
+
+For the generic keychain type, the :label key searches the item's
+labels (\"-l LABEL\" passed to \"/usr/bin/security\").
+Similarly, :host maps to \"-c HOST\" (the \"creator\" keychain
+field), :user maps to \"-a USER\", and :port maps to \"-s PORT\".
+
+Here's an example that looks for the first item in the default
+generic MacOS Keychain:
+
+ (let ((auth-sources \\='(macos-keychain-generic)))
+    (auth-source-search :max 1)
+
+Here's another that looks for the first item in the internet
+MacOS Keychain collection whose label is `gnus':
+
+ (let ((auth-sources \\='(macos-keychain-internet)))
+    (auth-source-search :max 1 :label \"gnus\")
+
+And this one looks for the first item in the internet keychain
+entries for git.gnus.org:
+
+ (let ((auth-sources \\='(macos-keychain-internet\")))
+    (auth-source-search :max 1 :host \"git.gnus.org\"))
+"
+  ;; TODO
+  (assert (not create) nil
+          "The MacOS Keychain auth-source backend doesn't support creation yet")
+  ;; TODO
+  ;; (macos-keychain-delete-item coll elt)
+  (assert (not delete) nil
+          "The MacOS Keychain auth-source backend doesn't support deletion yet")
+
+  (let* ((coll (oref backend source))
+         (max (or max 5000))     ; sanity check: default to stop at 5K
+         ;; Filter out ignored keys from the spec
+         (ignored-keys '(:create :delete :max :backend :label :host :port))
+         ;; Build a search spec without the ignored keys
+         (search-keys (loop for i below (length spec) by 2
+                            unless (memq (nth i spec) ignored-keys)
+                            collect (nth i spec)))
+         ;; If a search key value is nil or t (match anything), we skip it
+         (search-spec (apply #'append (mapcar
+                                      (lambda (k)
+                                        (if (or (null (plist-get spec k))
+                                                (eq t (plist-get spec k)))
+                                            nil
+                                          (list k (plist-get spec k))))
+                                      search-keys)))
+         ;; needed keys (always including host, login, port, and secret)
+         (returned-keys (delete-dups (append
+                                     '(:host :login :port :secret)
+                                     search-keys)))
+         ;; Extract host and port from spec
+         (hosts (plist-get spec :host))
+         (hosts (if (and hosts (listp hosts)) hosts `(,hosts)))
+         (ports (plist-get spec :port))
+         (ports (if (and ports (listp ports)) ports `(,ports)))
+         ;; Loop through all combinations of host/port and pass each of these to
+         ;; auth-source-macos-keychain-search-items
+         (items (catch 'match
+                  (dolist (host hosts)
+                    (dolist (port ports)
+                      (let* ((port (format "%S" port))
+                             (items (apply #'auth-source-macos-keychain-search-items
+                                           coll
+                                           type
+                                           max
+                                           host port
+                                           search-spec)))
+                        (when items
+                          (throw 'match items)))))))
+
+         ;; ensure each item has each key in `returned-keys'
+         (items (mapcar (lambda (plist)
+                          (append
+                           (apply #'append
+                                  (mapcar (lambda (req)
+                                            (if (plist-get plist req)
+                                                nil
+                                              (list req nil)))
+                                          returned-keys))
+                           plist))
+                        items)))
+    items))
+
+(defun* auth-source-macos-keychain-search-items (coll _type _max
+                                                      host port
+                                                      &key label type
+                                                      user
+                                                      &allow-other-keys)
+
+  (let* ((keychain-generic (eq type 'macos-keychain-generic))
+         (args `(,(if keychain-generic
+                      "find-generic-password"
+                    "find-internet-password")
+                 "-g"))
+         (ret (list :type type)))
+    (when label
+      (setq args (append args (list "-l" label))))
+    (when host
+      (setq args (append args (list (if keychain-generic "-c" "-s") host))))
+    (when user
+      (setq args (append args (list "-a" user))))
+
+    (when port
+      (if keychain-generic
+          (setq args (append args (list "-s" port)))
+        (setq args (append args (list
+                                 (if (string-match "[0-9]+" port) "-P" "-r")
+                                 port)))))
+
+      (unless (equal coll "default")
+        (setq args (append args (list coll))))
+
+      (with-temp-buffer
+        (apply #'call-process "/usr/bin/security" nil t nil args)
+        (goto-char (point-min))
+        (while (not (eobp))
+          (cond
+           ((looking-at "^password: \"\\(.+\\)\"$")
+            (setq ret (auth-source-macos-keychain-result-append
+                       ret
+                       keychain-generic
+                       "secret"
+                       (lexical-let ((v (match-string 1)))
+                         (lambda () v)))))
+           ;; TODO: check if this is really the label
+           ;; match 0x00000007 <blob>="AppleID"
+           ((looking-at "^[ ]+0x00000007 <blob>=\"\\(.+\\)\"")
+            (setq ret (auth-source-macos-keychain-result-append
+                       ret
+                       keychain-generic
+                       "label"
+                       (match-string 1))))
+           ;; match "crtr"<uint32>="aapl"
+           ;; match "svce"<blob>="AppleID"
+           ((looking-at "^[ ]+\"\\([a-z]+\\)\"[^=]+=\"\\(.+\\)\"")
+            (setq ret (auth-source-macos-keychain-result-append
+                       ret
+                       keychain-generic
+                       (match-string 1)
+                       (match-string 2)))))
+          (forward-line)))
+      ;; return `ret' iff it has the :secret key
+      (and (plist-get ret :secret) (list ret))))
+
+(defun auth-source-macos-keychain-result-append (result generic k v)
+  (push v result)
+  (push (auto-source--symbol-keyword
+         (cond
+          ((equal k "acct") "user")
+          ;; for generic keychains, creator is host, service is port
+          ((and generic (equal k "crtr")) "host")
+          ((and generic (equal k "svce")) "port")
+          ;; for internet keychains, protocol is port, server is host
+          ((and (not generic) (equal k "ptcl")) "port")
+          ((and (not generic) (equal k "srvr")) "host")
+          (t k)))
+        result))
+
+(defun auth-source-macos-keychain-create (&rest spec)
+  ;; TODO
+  (debug spec))
+
+;;; Backend specific parsing: PLSTORE backend
+
+(defun* auth-source-plstore-search (&rest
+                                    spec
+                                    &key backend create delete
+                                    max
+                                    &allow-other-keys)
+  "Search the PLSTORE; spec is like `auth-source'."
+  (let* ((store (oref backend data))
+         (max (or max 5000))     ; sanity check: default to stop at 5K
+         (ignored-keys '(:create :delete :max :backend :label :require :type))
+         (search-keys (loop for i below (length spec) by 2
+                            unless (memq (nth i spec) ignored-keys)
+                            collect (nth i spec)))
+         ;; build a search spec without the ignored keys
+         ;; if a search key is nil or t (match anything), we skip it
+         (search-spec (apply #'append (mapcar
+                                      (lambda (k)
+                                        (let ((v (plist-get spec k)))
+                                          (if (or (null v)
+                                                  (eq t v))
+                                              nil
+                                            (if (stringp v)
+                                                (setq v (list v)))
+                                            (list k v))))
+                                      search-keys)))
+         ;; needed keys (always including host, login, port, and secret)
+         (returned-keys (delete-dups (append
+                                     '(:host :login :port :secret)
+                                     search-keys)))
+         (items (plstore-find store search-spec))
+         (item-names (mapcar #'car items))
+         (items (butlast items (- (length items) max)))
+         ;; convert the item to a full plist
+         (items (mapcar (lambda (item)
+                          (let* ((plist (copy-tree (cdr item)))
+                                 (secret (plist-member plist :secret)))
+                            (if secret
+                                (setcar
+                                 (cdr secret)
+                                 (lexical-let ((v (car (cdr secret))))
+                                   (lambda () v))))
+                            plist))
+                        items))
+         ;; ensure each item has each key in `returned-keys'
+         (items (mapcar (lambda (plist)
+                          (append
+                           (apply #'append
+                                  (mapcar (lambda (req)
+                                            (if (plist-get plist req)
+                                                nil
+                                              (list req nil)))
+                                          returned-keys))
+                           plist))
+                        items)))
+    (cond
+     ;; if we need to create an entry AND none were found to match
+     ((and create
+           (not items))
+
+      ;; create based on the spec and record the value
+      (setq items (or
+                   ;; if the user did not want to create the entry
+                   ;; in the file, it will be returned
+                   (apply (slot-value backend 'create-function) spec)
+                   ;; if not, we do the search again without :create
+                   ;; to get the updated data.
+
+                   ;; the result will be returned, even if the search fails
+                   (apply #'auth-source-plstore-search
+                          (plist-put spec :create nil)))))
+     ((and delete
+           item-names)
+      (dolist (item-name item-names)
+        (plstore-delete store item-name))
+      (plstore-save store)))
+    items))
+
+(defun* auth-source-plstore-create (&rest spec
+                                          &key backend
+                                          host port create
+                                          &allow-other-keys)
+  (let* ((base-required '(host user port secret))
+         (base-secret '(secret))
+         ;; we know (because of an assertion in auth-source-search) that the
+         ;; :create parameter is either t or a list (which includes nil)
+         (create-extra (if (eq t create) nil create))
+         (current-data (car (auth-source-search :max 1
+                                                :host host
+                                                :port port)))
+         (required (append base-required create-extra))
+         ;; `valist' is an alist
+         valist
+         ;; `artificial' will be returned if no creation is needed
+         artificial
+         secret-artificial)
+
+    ;; only for base required elements (defined as function parameters):
+    ;; fill in the valist with whatever data we may have from the search
+    ;; we complete the first value if it's a list and use the value otherwise
+    (dolist (br base-required)
+      (let ((val (plist-get spec (auto-source--symbol-keyword br))))
+        (when val
+          (let ((br-choice (cond
+                            ;; all-accepting choice (predicate is t)
+                            ((eq t val) nil)
+                            ;; just the value otherwise
+                            (t val))))
+            (when br-choice
+              (auth-source--aput valist br br-choice))))))
+
+    ;; for extra required elements, see if the spec includes a value for them
+    (dolist (er create-extra)
+      (let ((k (auto-source--symbol-keyword er))
+            (keys (loop for i below (length spec) by 2
+                        collect (nth i spec))))
+        (when (memq k keys)
+          (auth-source--aput valist er (plist-get spec k)))))
+
+    ;; for each required element
+    (dolist (r required)
+      (let* ((data (auth-source--aget valist r))
+             ;; take the first element if the data is a list
+             (data (or (auth-source-netrc-element-or-first data)
+                       (plist-get current-data
+                                  (auto-source--symbol-keyword r))))
+             ;; this is the default to be offered
+             (given-default (auth-source--aget
+                             auth-source-creation-defaults r))
+             ;; the default supplementals are simple:
+             ;; for the user, try `given-default' and then (user-login-name);
+             ;; otherwise take `given-default'
+             (default (cond
+                       ((and (not given-default) (eq r 'user))
+                        (user-login-name))
+                       (t given-default)))
+             (printable-defaults (list
+                                  (cons 'user
+                                        (or
+                                         (auth-source-netrc-element-or-first
+                                          (auth-source--aget valist 'user))
+                                         (plist-get artificial :user)
+                                         "[any user]"))
+                                  (cons 'host
+                                        (or
+                                         (auth-source-netrc-element-or-first
+                                          (auth-source--aget valist 'host))
+                                         (plist-get artificial :host)
+                                         "[any host]"))
+                                  (cons 'port
+                                        (or
+                                         (auth-source-netrc-element-or-first
+                                          (auth-source--aget valist 'port))
+                                         (plist-get artificial :port)
+                                         "[any port]"))))
+             (prompt (or (auth-source--aget auth-source-creation-prompts r)
+                         (case r
+                           (secret "%p password for %u@%h: ")
+                           (user "%p user name for %h: ")
+                           (host "%p host name for user %u: ")
+                           (port "%p port for %u@%h: "))
+                         (format "Enter %s (%%u@%%h:%%p): " r)))
+             (prompt (auth-source-format-prompt
+                      prompt
+                      `((?u ,(auth-source--aget printable-defaults 'user))
+                        (?h ,(auth-source--aget printable-defaults 'host))
+                        (?p ,(auth-source--aget printable-defaults 'port))))))
+
+        ;; Store the data, prompting for the password if needed.
+        (setq data (or data
+                       (if (eq r 'secret)
+                           (or (eval default) (read-passwd prompt))
+                         (if (stringp default)
+                             (read-string
+                              (if (string-match ": *\\'" prompt)
+                                  (concat (substring prompt 0 (match-beginning 0))
+                                          " (default " default "): ")
+                                (concat prompt "(default " default ") "))
+                              nil nil default)
+                           (eval default)))))
+
+        (when data
+          (if (member r base-secret)
+              (setq secret-artificial
+                    (plist-put secret-artificial
+                               (auto-source--symbol-keyword r)
+                               data))
+            (setq artificial (plist-put artificial
+                                        (auto-source--symbol-keyword r)
+                                        data))))))
+    (plstore-put (oref backend data)
+                 (sha1 (format "%s@%s:%s"
+                               (plist-get artificial :user)
+                               (plist-get artificial :host)
+                               (plist-get artificial :port)))
+                 artificial secret-artificial)
+    (if (y-or-n-p (format "Save auth info to file %s? "
+                          (plstore-get-file (oref backend data))))
+        (plstore-save (oref backend data)))))
+
+;;; older API
+
+;; (auth-source-user-or-password '("login" "password") "imap.myhost.com" t "tzz")
+
+;; deprecate the old interface
+(make-obsolete 'auth-source-user-or-password
+               'auth-source-search "Emacs 24.1")
+(make-obsolete 'auth-source-forget-user-or-password
+               'auth-source-forget "Emacs 24.1")
+
+(defun auth-source-user-or-password
+  (mode host port &optional username create-missing delete-existing)
+  "Find MODE (string or list of strings) matching HOST and PORT.
+
+DEPRECATED in favor of `auth-source-search'!
+
+USERNAME is optional and will be used as \"login\" in a search
+across the Secret Service API (see secrets.el) if the resulting
+items don't have a username.  This means that if you search for
+username \"joe\" and it matches an item but the item doesn't have
+a :user attribute, the username \"joe\" will be returned.
+
+A non nil DELETE-EXISTING means deleting any matching password
+entry in the respective sources.  This is useful only when
+CREATE-MISSING is non nil as well; the intended use case is to
+remove wrong password entries.
+
+If no matching entry is found, and CREATE-MISSING is non nil,
+the password will be retrieved interactively, and it will be
+stored in the password database which matches best (see
+`auth-sources').
+
+MODE can be \"login\" or \"password\"."
+  (auth-source-do-debug
+   "auth-source-user-or-password: DEPRECATED get %s for %s (%s) + user=%s"
+   mode host port username)
+
+  (let* ((listy (listp mode))
+         (mode (if listy mode (list mode)))
+         ;; (cname (if username
+         ;;            (format "%s %s:%s %s" mode host port username)
+         ;;          (format "%s %s:%s" mode host port)))
+         (search (list :host host :port port))
+         (search (if username (append search (list :user username)) search))
+         (search (if create-missing
+                     (append search (list :create t))
+                   search))
+         (search (if delete-existing
+                     (append search (list :delete t))
+                   search))
+         ;; (found (if (not delete-existing)
+         ;;            (gethash cname auth-source-cache)
+         ;;          (remhash cname auth-source-cache)
+         ;;          nil)))
+         (found nil))
+    (if found
+        (progn
+          (auth-source-do-debug
+           "auth-source-user-or-password: DEPRECATED cached %s=%s for %s (%s) + %s"
+           mode
+           ;; don't show the password
+           (if (and (member "password" mode) t)
+               "SECRET"
+             found)
+           host port username)
+          found)                        ; return the found data
+      ;; else, if not found, search with a max of 1
+      (let ((choice (nth 0 (apply #'auth-source-search
+                                  (append '(:max 1) search)))))
+        (when choice
+          (dolist (m mode)
+            (cond
+             ((equal "password" m)
+              (push (if (plist-get choice :secret)
+                        (funcall (plist-get choice :secret))
+                      nil) found))
+             ((equal "login" m)
+              (push (plist-get choice :user) found)))))
+        (setq found (nreverse found))
+        (setq found (if listy found (car-safe found)))))
+
+    found))
+
+(defun auth-source-user-and-password (host &optional user)
+  (let* ((auth-info (car
+                     (if user
+                         (auth-source-search
+                          :host host
+                          :user "yourusername"
+                          :max 1
+                          :require '(:user :secret)
+                          :create nil)
+                       (auth-source-search
+                        :host host
+                        :max 1
+                        :require '(:user :secret)
+                        :create nil))))
+         (user (plist-get auth-info :user))
+         (password (plist-get auth-info :secret)))
+    (when (functionp password)
+      (setq password (funcall password)))
+    (list user password auth-info)))
+
+(provide 'auth-source)
+
+;;; auth-source.el ends here
diff --git a/lisp/ecomplete.el b/lisp/ecomplete.el
new file mode 100644 (file)
index 0000000..cb50cce
--- /dev/null
@@ -0,0 +1,158 @@
+;;; ecomplete.el --- electric completion of addresses and the like
+
+;; Copyright (C) 2006-2016 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+;; Keywords: mail
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(eval-when-compile
+  (require 'cl))
+
+(defgroup ecomplete nil
+  "Electric completion of email addresses and the like."
+  :group 'mail)
+
+(defcustom ecomplete-database-file "~/.ecompleterc"
+  "*The name of the file to store the ecomplete data."
+  :group 'ecomplete
+  :type 'file)
+
+(defcustom ecomplete-database-file-coding-system 'iso-2022-7bit
+  "Coding system used for writing the ecomplete database file."
+  :type '(symbol :tag "Coding system")
+  :group 'ecomplete)
+
+;;; Internal variables.
+
+(defvar ecomplete-database nil)
+
+;;;###autoload
+(defun ecomplete-setup ()
+  (when (file-exists-p ecomplete-database-file)
+    (with-temp-buffer
+      (let ((coding-system-for-read ecomplete-database-file-coding-system))
+       (insert-file-contents ecomplete-database-file)
+       (setq ecomplete-database (read (current-buffer)))))))
+
+(defun ecomplete-add-item (type key text)
+  (let ((elems (assq type ecomplete-database))
+       (now (string-to-number (format "%.0f" (float-time))))
+       entry)
+    (unless elems
+      (push (setq elems (list type)) ecomplete-database))
+    (if (setq entry (assoc key (cdr elems)))
+       (setcdr entry (list (1+ (cadr entry)) now text))
+      (nconc elems (list (list key 1 now text))))))
+
+(defun ecomplete-get-item (type key)
+  (assoc key (cdr (assq type ecomplete-database))))
+
+(defun ecomplete-save ()
+  (with-temp-buffer
+    (let ((coding-system-for-write ecomplete-database-file-coding-system))
+      (insert "(")
+      (loop for (type . elems) in ecomplete-database
+           do
+           (insert (format "(%s\n" type))
+           (dolist (entry elems)
+             (prin1 entry (current-buffer))
+             (insert "\n"))
+           (insert ")\n"))
+      (insert ")")
+      (write-region (point-min) (point-max)
+                   ecomplete-database-file nil 'silent))))
+
+(defun ecomplete-get-matches (type match)
+  (let* ((elems (cdr (assq type ecomplete-database)))
+        (match (regexp-quote match))
+        (candidates
+         (sort
+          (loop for (key count time text) in elems
+                when (string-match match text)
+                collect (list count time text))
+          (lambda (l1 l2)
+            (> (car l1) (car l2))))))
+    (when (> (length candidates) 10)
+      (setcdr (nthcdr 10 candidates) nil))
+    (unless (zerop (length candidates))
+      (with-temp-buffer
+       (dolist (candidate candidates)
+         (insert (caddr candidate) "\n"))
+       (goto-char (point-min))
+       (put-text-property (point) (1+ (point)) 'ecomplete t)
+       (while (re-search-forward match nil t)
+         (put-text-property (match-beginning 0) (match-end 0)
+                            'face 'isearch))
+       (buffer-string)))))
+
+(defun ecomplete-display-matches (type word &optional choose)
+  (let* ((matches (ecomplete-get-matches type word))
+        (line 0)
+        (max-lines (when matches (- (length (split-string matches "\n")) 2)))
+        (message-log-max nil)
+        command highlight)
+    (if (not matches)
+       (progn
+         (message "No ecomplete matches")
+         nil)
+      (if (not choose)
+         (progn
+           (message "%s" matches)
+           nil)
+       (setq highlight (ecomplete-highlight-match-line matches line))
+       (let ((local-map (make-sparse-keymap))
+             selected)
+         (define-key local-map (kbd "RET")
+           (lambda () (setq selected (nth line (split-string matches "\n")))))
+         (define-key local-map (kbd "M-n")
+           (lambda () (setq line (min (1+ line) max-lines))))
+         (define-key local-map (kbd "M-p")
+           (lambda () (setq line (max (1- line) 0))))
+         (let ((overriding-local-map local-map))
+           (while (and (null selected)
+                       (setq command (read-key-sequence highlight))
+                       (lookup-key local-map command))
+             (apply (key-binding command) nil)
+             (setq highlight (ecomplete-highlight-match-line matches line))))
+         (if selected
+             (message selected)
+           (message "Abort"))
+         selected)))))
+
+(defun ecomplete-highlight-match-line (matches line)
+  (with-temp-buffer
+    (insert matches)
+    (goto-char (point-min))
+    (forward-line line)
+    (save-restriction
+      (narrow-to-region (point) (point-at-eol))
+      (while (not (eobp))
+       ;; Put the 'region face on any characters on this line that
+       ;; aren't already highlighted.
+       (unless (get-text-property (point) 'face)
+         (put-text-property (point) (1+ (point)) 'face 'highlight))
+       (forward-char 1)))
+    (buffer-string)))
+
+(provide 'ecomplete)
+
+;;; ecomplete.el ends here
diff --git a/lisp/gnus/auth-source.el b/lisp/gnus/auth-source.el
deleted file mode 100644 (file)
index cfd21a5..0000000
+++ /dev/null
@@ -1,2145 +0,0 @@
-;;; auth-source.el --- authentication sources for Gnus and Emacs
-
-;; Copyright (C) 2008-2016 Free Software Foundation, Inc.
-
-;; Author: Ted Zlatanov <tzz@lifelogs.com>
-;; Keywords: news
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This is the auth-source.el package.  It lets users tell Gnus how to
-;; authenticate in a single place.  Simplicity is the goal.  Instead
-;; of providing 5000 options, we'll stick to simple, easy to
-;; understand options.
-
-;; See the auth.info Info documentation for details.
-
-;; TODO:
-
-;; - never decode the backend file unless it's necessary
-;; - a more generic way to match backends and search backend contents
-;; - absorb netrc.el and simplify it
-;; - protect passwords better
-;; - allow creating and changing netrc lines (not files) e.g. change a password
-
-;;; Code:
-
-(require 'password-cache)
-
-(eval-when-compile (require 'cl))
-(require 'eieio)
-
-(autoload 'secrets-create-item "secrets")
-(autoload 'secrets-delete-item "secrets")
-(autoload 'secrets-get-alias "secrets")
-(autoload 'secrets-get-attributes "secrets")
-(autoload 'secrets-get-secret "secrets")
-(autoload 'secrets-list-collections "secrets")
-(autoload 'secrets-search-items "secrets")
-
-(autoload 'rfc2104-hash "rfc2104")
-
-(autoload 'plstore-open "plstore")
-(autoload 'plstore-find "plstore")
-(autoload 'plstore-put "plstore")
-(autoload 'plstore-delete "plstore")
-(autoload 'plstore-save "plstore")
-(autoload 'plstore-get-file "plstore")
-
-(eval-when-compile (require 'epg)) ;; setf-method for `epg-context-armor'
-(autoload 'epg-make-context "epg")
-(autoload 'epg-context-set-passphrase-callback "epg")
-(autoload 'epg-decrypt-string "epg")
-(autoload 'epg-encrypt-string "epg")
-
-(autoload 'help-mode "help-mode" nil t)
-
-(defvar secrets-enabled)
-
-(defgroup auth-source nil
-  "Authentication sources."
-  :version "23.1" ;; No Gnus
-  :group 'gnus)
-
-;;;###autoload
-(defcustom auth-source-cache-expiry 7200
-  "How many seconds passwords are cached, or nil to disable
-expiring.  Overrides `password-cache-expiry' through a
-let-binding."
-  :version "24.1"
-  :group 'auth-source
-  :type '(choice (const :tag "Never" nil)
-                 (const :tag "All Day" 86400)
-                 (const :tag "2 Hours" 7200)
-                 (const :tag "30 Minutes" 1800)
-                 (integer :tag "Seconds")))
-
-;; The slots below correspond with the `auth-source-search' spec,
-;; so a backend with :host set, for instance, would match only
-;; searches for that host.  Normally they are nil.
-(defclass auth-source-backend ()
-  ((type :initarg :type
-         :initform 'netrc
-         :type symbol
-         :custom symbol
-         :documentation "The backend type.")
-   (source :initarg :source
-           :type string
-           :custom string
-           :documentation "The backend source.")
-   (host :initarg :host
-         :initform t
-         :type t
-         :custom string
-         :documentation "The backend host.")
-   (user :initarg :user
-         :initform t
-         :type t
-         :custom string
-         :documentation "The backend user.")
-   (port :initarg :port
-         :initform t
-         :type t
-         :custom string
-         :documentation "The backend protocol.")
-   (data :initarg :data
-         :initform nil
-         :documentation "Internal backend data.")
-   (create-function :initarg :create-function
-                    :initform ignore
-                    :type function
-                    :custom function
-                    :documentation "The create function.")
-   (search-function :initarg :search-function
-                    :initform ignore
-                    :type function
-                    :custom function
-                    :documentation "The search function.")))
-
-(defcustom auth-source-protocols '((imap "imap" "imaps" "143" "993")
-                                   (pop3 "pop3" "pop" "pop3s" "110" "995")
-                                   (ssh  "ssh" "22")
-                                   (sftp "sftp" "115")
-                                   (smtp "smtp" "25"))
-  "List of authentication protocols and their names"
-
-  :group 'auth-source
-  :version "23.2" ;; No Gnus
-  :type '(repeat :tag "Authentication Protocols"
-                 (cons :tag "Protocol Entry"
-                       (symbol :tag "Protocol")
-                       (repeat :tag "Names"
-                               (string :tag "Name")))))
-
-;; Generate all the protocols in a format Customize can use.
-;; TODO: generate on the fly from auth-source-protocols
-(defconst auth-source-protocols-customize
-  (mapcar (lambda (a)
-            (let ((p (car-safe a)))
-              (list 'const
-                    :tag (upcase (symbol-name p))
-                    p)))
-          auth-source-protocols))
-
-(defvar auth-source-creation-defaults nil
-  ;; FIXME: AFAICT this is not set (or let-bound) anywhere!
-  "Defaults for creating token values.  Usually let-bound.")
-
-(defvar auth-source-creation-prompts nil
-  "Default prompts for token values.  Usually let-bound.")
-
-(make-obsolete 'auth-source-hide-passwords nil "Emacs 24.1")
-
-(defcustom auth-source-save-behavior 'ask
-  "If set, auth-source will respect it for save behavior."
-  :group 'auth-source
-  :version "23.2" ;; No Gnus
-  :type `(choice
-          :tag "auth-source new token save behavior"
-          (const :tag "Always save" t)
-          (const :tag "Never save" nil)
-          (const :tag "Ask" ask)))
-
-;; TODO: make the default (setq auth-source-netrc-use-gpg-tokens `((,(if (boundp 'epa-file-auto-mode-alist-entry) (car epa-file-auto-mode-alist-entry) "\\.gpg\\'") never) (t gpg)))
-;; TODO: or maybe leave as (setq auth-source-netrc-use-gpg-tokens 'never)
-
-(defcustom auth-source-netrc-use-gpg-tokens 'never
-  "Set this to tell auth-source when to create GPG password
-tokens in netrc files.  It's either an alist or `never'.
-Note that if EPA/EPG is not available, this should NOT be used."
-  :group 'auth-source
-  :version "23.2" ;; No Gnus
-  :type `(choice
-          (const :tag "Always use GPG password tokens" (t gpg))
-          (const :tag "Never use GPG password tokens" never)
-          (repeat :tag "Use a lookup list"
-                  (list
-                   (choice :tag "Matcher"
-                           (const :tag "Match anything" t)
-                           (const :tag "The EPA encrypted file extensions"
-                                  ,(if (boundp 'epa-file-auto-mode-alist-entry)
-                                       (car epa-file-auto-mode-alist-entry)
-                                     "\\.gpg\\'"))
-                           (regexp :tag "Regular expression"))
-                   (choice :tag "What to do"
-                           (const :tag "Save GPG-encrypted password tokens" gpg)
-                           (const :tag "Don't encrypt tokens" never))))))
-
-(defvar auth-source-magic "auth-source-magic ")
-
-(defcustom auth-source-do-cache t
-  "Whether auth-source should cache information with `password-cache'."
-  :group 'auth-source
-  :version "23.2" ;; No Gnus
-  :type `boolean)
-
-(defcustom auth-source-debug nil
-  "Whether auth-source should log debug messages.
-
-If the value is nil, debug messages are not logged.
-
-If the value is t, debug messages are logged with `message'.  In
-that case, your authentication data will be in the clear (except
-for passwords).
-
-If the value is a function, debug messages are logged by calling
- that function using the same arguments as `message'."
-  :group 'auth-source
-  :version "23.2" ;; No Gnus
-  :type `(choice
-          :tag "auth-source debugging mode"
-          (const :tag "Log using `message' to the *Messages* buffer" t)
-          (const :tag "Log all trivia with `message' to the *Messages* buffer"
-                 trivia)
-          (function :tag "Function that takes arguments like `message'")
-          (const :tag "Don't log anything" nil)))
-
-(defcustom auth-sources '("~/.authinfo" "~/.authinfo.gpg" "~/.netrc")
-  "List of authentication sources.
-Each entry is the authentication type with optional properties.
-Entries are tried in the order in which they appear.
-See Info node `(auth)Help for users' for details.
-
-If an entry names a file with the \".gpg\" extension and you have
-EPA/EPG set up, the file will be encrypted and decrypted
-automatically.  See Info node `(epa)Encrypting/decrypting gpg files'
-for details.
-
-It's best to customize this with `\\[customize-variable]' because the choices
-can get pretty complex."
-  :group 'auth-source
-  :version "24.1" ;; No Gnus
-  :type `(repeat :tag "Authentication Sources"
-                 (choice
-                  (string :tag "Just a file")
-                  (const :tag "Default Secrets API Collection" default)
-                  (const :tag "Login Secrets API Collection" "secrets:Login")
-                  (const :tag "Temp Secrets API Collection" "secrets:session")
-
-                  (const :tag "Default internet Mac OS Keychain"
-                         macos-keychain-internet)
-
-                  (const :tag "Default generic Mac OS Keychain"
-                         macos-keychain-generic)
-
-                  (list :tag "Source definition"
-                        (const :format "" :value :source)
-                        (choice :tag "Authentication backend choice"
-                                (string :tag "Authentication Source (file)")
-                                (list
-                                 :tag "Secret Service API/KWallet/GNOME Keyring"
-                                 (const :format "" :value :secrets)
-                                 (choice :tag "Collection to use"
-                                         (string :tag "Collection name")
-                                         (const :tag "Default" default)
-                                         (const :tag "Login" "Login")
-                                         (const
-                                          :tag "Temporary" "session")))
-                                (list
-                                 :tag "Mac OS internet Keychain"
-                                 (const :format ""
-                                        :value :macos-keychain-internet)
-                                 (choice :tag "Collection to use"
-                                         (string :tag "internet Keychain path")
-                                         (const :tag "default" default)))
-                                (list
-                                 :tag "Mac OS generic Keychain"
-                                 (const :format ""
-                                        :value :macos-keychain-generic)
-                                 (choice :tag "Collection to use"
-                                         (string :tag "generic Keychain path")
-                                         (const :tag "default" default))))
-                        (repeat :tag "Extra Parameters" :inline t
-                                (choice :tag "Extra parameter"
-                                        (list
-                                         :tag "Host"
-                                         (const :format "" :value :host)
-                                         (choice :tag "Host (machine) choice"
-                                                 (const :tag "Any" t)
-                                                 (regexp
-                                                  :tag "Regular expression")))
-                                        (list
-                                         :tag "Protocol"
-                                         (const :format "" :value :port)
-                                         (choice
-                                          :tag "Protocol"
-                                          (const :tag "Any" t)
-                                          ,@auth-source-protocols-customize))
-                                        (list :tag "User" :inline t
-                                              (const :format "" :value :user)
-                                              (choice
-                                               :tag "Personality/Username"
-                                               (const :tag "Any" t)
-                                               (string
-                                                :tag "Name")))))))))
-
-(defcustom auth-source-gpg-encrypt-to t
-  "List of recipient keys that `authinfo.gpg' encrypted to.
-If the value is not a list, symmetric encryption will be used."
-  :group 'auth-source
-  :version "24.1" ;; No Gnus
-  :type '(choice (const :tag "Symmetric encryption" t)
-                 (repeat :tag "Recipient public keys"
-                         (string :tag "Recipient public key"))))
-
-;; temp for debugging
-;; (unintern 'auth-source-protocols)
-;; (unintern 'auth-sources)
-;; (customize-variable 'auth-sources)
-;; (setq auth-sources nil)
-;; (format "%S" auth-sources)
-;; (customize-variable 'auth-source-protocols)
-;; (setq auth-source-protocols nil)
-;; (format "%S" auth-source-protocols)
-;; (auth-source-pick nil :host "a" :port 'imap)
-;; (auth-source-user-or-password "login" "imap.myhost.com" 'imap)
-;; (auth-source-user-or-password "password" "imap.myhost.com" 'imap)
-;; (auth-source-user-or-password-imap "login" "imap.myhost.com")
-;; (auth-source-user-or-password-imap "password" "imap.myhost.com")
-;; (auth-source-protocol-defaults 'imap)
-
-;; (let ((auth-source-debug 'debug)) (auth-source-do-debug "hello"))
-;; (let ((auth-source-debug t)) (auth-source-do-debug "hello"))
-;; (let ((auth-source-debug nil)) (auth-source-do-debug "hello"))
-(defun auth-source-do-debug (&rest msg)
-  (when auth-source-debug
-    (apply #'auth-source-do-warn msg)))
-
-(defun auth-source-do-trivia (&rest msg)
-  (when (or (eq auth-source-debug 'trivia)
-            (functionp auth-source-debug))
-    (apply #'auth-source-do-warn msg)))
-
-(defun auth-source-do-warn (&rest msg)
-  (apply
-   ;; set logger to either the function in auth-source-debug or 'message
-   ;; note that it will be 'message if auth-source-debug is nil
-   (if (functionp auth-source-debug)
-       auth-source-debug
-     'message)
-   msg))
-
-
-;; (auth-source-read-char-choice "enter choice? " '(?a ?b ?q))
-(defun auth-source-read-char-choice (prompt choices)
-  "Read one of CHOICES by `read-char-choice', or `read-char'.
-`dropdown-list' support is disabled because it doesn't work reliably.
-Only one of CHOICES will be returned.  The PROMPT is augmented
-with \"[a/b/c] \" if CHOICES is \(?a ?b ?c)."
-  (when choices
-    (let* ((prompt-choices
-            (apply #'concat (loop for c in choices
-                                 collect (format "%c/" c))))
-           (prompt-choices (concat "[" (substring prompt-choices 0 -1) "] "))
-           (full-prompt (concat prompt prompt-choices))
-           k)
-
-      (while (not (memq k choices))
-        (setq k (read-char-choice full-prompt choices)))
-      k)))
-
-;; (auth-source-pick nil :host "any" :port 'imap :user "joe")
-;; (auth-source-pick t :host "any" :port 'imap :user "joe")
-;; (setq auth-sources '((:source (:secrets default) :host t :port t :user "joe")
-;;                   (:source (:secrets "session") :host t :port t :user "joe")
-;;                   (:source (:secrets "Login") :host t :port t)
-;;                   (:source "~/.authinfo.gpg" :host t :port t)))
-
-;; (setq auth-sources '((:source (:secrets default) :host t :port t :user "joe")
-;;                   (:source (:secrets "session") :host t :port t :user "joe")
-;;                   (:source (:secrets "Login") :host t :port t)
-;;                   ))
-
-;; (setq auth-sources '((:source "~/.authinfo.gpg" :host t :port t)))
-
-;; (auth-source-backend-parse "myfile.gpg")
-;; (auth-source-backend-parse 'default)
-;; (auth-source-backend-parse "secrets:Login")
-;; (auth-source-backend-parse 'macos-keychain-internet)
-;; (auth-source-backend-parse 'macos-keychain-generic)
-;; (auth-source-backend-parse "macos-keychain-internet:/path/here.keychain")
-;; (auth-source-backend-parse "macos-keychain-generic:/path/here.keychain")
-
-(defun auth-source-backend-parse (entry)
-  "Creates an auth-source-backend from an ENTRY in `auth-sources'."
-  (auth-source-backend-parse-parameters
-   entry
-   (cond
-    ;; take 'default and recurse to get it as a Secrets API default collection
-    ;; matching any user, host, and protocol
-    ((eq entry 'default)
-     (auth-source-backend-parse '(:source (:secrets default))))
-    ;; take secrets:XYZ and recurse to get it as Secrets API collection "XYZ"
-    ;; matching any user, host, and protocol
-    ((and (stringp entry) (string-match "^secrets:\\(.+\\)" entry))
-     (auth-source-backend-parse `(:source (:secrets ,(match-string 1 entry)))))
-
-    ;; take 'macos-keychain-internet and recurse to get it as a Mac OS
-    ;; Keychain collection matching any user, host, and protocol
-    ((eq entry 'macos-keychain-internet)
-     (auth-source-backend-parse '(:source (:macos-keychain-internet default))))
-    ;; take 'macos-keychain-generic and recurse to get it as a Mac OS
-    ;; Keychain collection matching any user, host, and protocol
-    ((eq entry 'macos-keychain-generic)
-     (auth-source-backend-parse '(:source (:macos-keychain-generic default))))
-    ;; take macos-keychain-internet:XYZ and recurse to get it as MacOS
-    ;; Keychain "XYZ" matching any user, host, and protocol
-    ((and (stringp entry) (string-match "^macos-keychain-internet:\\(.+\\)"
-                                        entry))
-     (auth-source-backend-parse `(:source (:macos-keychain-internet
-                                           ,(match-string 1 entry)))))
-    ;; take macos-keychain-generic:XYZ and recurse to get it as MacOS
-    ;; Keychain "XYZ" matching any user, host, and protocol
-    ((and (stringp entry) (string-match "^macos-keychain-generic:\\(.+\\)"
-                                        entry))
-     (auth-source-backend-parse `(:source (:macos-keychain-generic
-                                           ,(match-string 1 entry)))))
-
-    ;; take just a file name and recurse to get it as a netrc file
-    ;; matching any user, host, and protocol
-    ((stringp entry)
-     (auth-source-backend-parse `(:source ,entry)))
-
-    ;; a file name with parameters
-    ((stringp (plist-get entry :source))
-     (if (equal (file-name-extension (plist-get entry :source)) "plist")
-         (auth-source-backend
-          (plist-get entry :source)
-          :source (plist-get entry :source)
-          :type 'plstore
-          :search-function #'auth-source-plstore-search
-          :create-function #'auth-source-plstore-create
-          :data (plstore-open (plist-get entry :source)))
-       (auth-source-backend
-        (plist-get entry :source)
-        :source (plist-get entry :source)
-        :type 'netrc
-        :search-function #'auth-source-netrc-search
-        :create-function #'auth-source-netrc-create)))
-
-    ;; the MacOS Keychain
-    ((and
-      (not (null (plist-get entry :source))) ; the source must not be nil
-      (listp (plist-get entry :source))      ; and it must be a list
-      (or
-       (plist-get (plist-get entry :source) :macos-keychain-generic)
-       (plist-get (plist-get entry :source) :macos-keychain-internet)))
-
-     (let* ((source-spec (plist-get entry :source))
-            (keychain-generic (plist-get source-spec :macos-keychain-generic))
-            (keychain-type (if keychain-generic
-                               'macos-keychain-generic
-                             'macos-keychain-internet))
-            (source (plist-get source-spec (if keychain-generic
-                                               :macos-keychain-generic
-                                             :macos-keychain-internet))))
-
-       (when (symbolp source)
-         (setq source (symbol-name source)))
-
-       (auth-source-backend
-        (format "Mac OS Keychain (%s)" source)
-        :source source
-        :type keychain-type
-        :search-function #'auth-source-macos-keychain-search
-        :create-function #'auth-source-macos-keychain-create)))
-
-    ;; the Secrets API.  We require the package, in order to have a
-    ;; defined value for `secrets-enabled'.
-    ((and
-      (not (null (plist-get entry :source))) ; the source must not be nil
-      (listp (plist-get entry :source))      ; and it must be a list
-      (require 'secrets nil t)               ; and we must load the Secrets API
-      secrets-enabled)                       ; and that API must be enabled
-
-     ;; the source is either the :secrets key in ENTRY or
-     ;; if that's missing or nil, it's "session"
-     (let ((source (or (plist-get (plist-get entry :source) :secrets)
-                       "session")))
-
-       ;; if the source is a symbol, we look for the alias named so,
-       ;; and if that alias is missing, we use "Login"
-       (when (symbolp source)
-         (setq source (or (secrets-get-alias (symbol-name source))
-                          "Login")))
-
-       (if (featurep 'secrets)
-           (auth-source-backend
-            (format "Secrets API (%s)" source)
-            :source source
-            :type 'secrets
-            :search-function #'auth-source-secrets-search
-            :create-function #'auth-source-secrets-create)
-         (auth-source-do-warn
-          "auth-source-backend-parse: no Secrets API, ignoring spec: %S" entry)
-         (auth-source-backend
-          (format "Ignored Secrets API (%s)" source)
-          :source ""
-          :type 'ignore))))
-
-    ;; none of them
-    (t
-     (auth-source-do-warn
-      "auth-source-backend-parse: invalid backend spec: %S" entry)
-     (make-instance 'auth-source-backend
-      :source ""
-      :type 'ignore)))))
-
-(defun auth-source-backend-parse-parameters (entry backend)
-  "Fills in the extra auth-source-backend parameters of ENTRY.
-Using the plist ENTRY, get the :host, :port, and :user search
-parameters."
-  (let ((entry (if (stringp entry)
-                   nil
-                 entry))
-        val)
-    (when (setq val (plist-get entry :host))
-      (oset backend host val))
-    (when (setq val (plist-get entry :user))
-      (oset backend user val))
-    (when (setq val (plist-get entry :port))
-      (oset backend port val)))
-  backend)
-
-;; (mapcar 'auth-source-backend-parse auth-sources)
-
-(defun* auth-source-search (&rest spec
-                                  &key max
-                                  require create delete
-                                  &allow-other-keys)
-  "Search or modify authentication backends according to SPEC.
-
-This function parses `auth-sources' for matches of the SPEC
-plist.  It can optionally create or update an authentication
-token if requested.  A token is just a standard Emacs property
-list with a :secret property that can be a function; all the
-other properties will always hold scalar values.
-
-Typically the :secret property, if present, contains a password.
-
-Common search keys are :max, :host, :port, and :user.  In
-addition, :create specifies if and how tokens will be created.
-Finally, :type can specify which backend types you want to check.
-
-A string value is always matched literally.  A symbol is matched
-as its string value, literally.  All the SPEC values can be
-single values (symbol or string) or lists thereof (in which case
-any of the search terms matches).
-
-:create t means to create a token if possible.
-
-A new token will be created if no matching tokens were found.
-The new token will have only the keys the backend requires.  For
-the netrc backend, for instance, that's the user, host, and
-port keys.
-
-Here's an example:
-
-\(let ((auth-source-creation-defaults \\='((user . \"defaultUser\")
-                                        (A    . \"default A\"))))
-  (auth-source-search :host \"mine\" :type \\='netrc :max 1
-                      :P \"pppp\" :Q \"qqqq\"
-                      :create t))
-
-which says:
-
-\"Search for any entry matching host `mine' in backends of type
- `netrc', maximum one result.
-
- Create a new entry if you found none.  The netrc backend will
- automatically require host, user, and port.  The host will be
- `mine'.  We prompt for the user with default `defaultUser' and
- for the port without a default.  We will not prompt for A, Q,
- or P.  The resulting token will only have keys user, host, and
- port.\"
-
-:create \\='(A B C) also means to create a token if possible.
-
-The behavior is like :create t but if the list contains any
-parameter, that parameter will be required in the resulting
-token.  The value for that parameter will be obtained from the
-search parameters or from user input.  If any queries are needed,
-the alist `auth-source-creation-defaults' will be checked for the
-default value.  If the user, host, or port are missing, the alist
-`auth-source-creation-prompts' will be used to look up the
-prompts IN THAT ORDER (so the `user' prompt will be queried first,
-then `host', then `port', and finally `secret').  Each prompt string
-can use %u, %h, and %p to show the user, host, and port.
-
-Here's an example:
-
-\(let ((auth-source-creation-defaults \\='((user . \"defaultUser\")
-                                        (A    . \"default A\")))
-       (auth-source-creation-prompts
-        \\='((password . \"Enter IMAP password for %h:%p: \"))))
-  (auth-source-search :host \\='(\"nonesuch\" \"twosuch\") :type \\='netrc :max 1
-                      :P \"pppp\" :Q \"qqqq\"
-                      :create \\='(A B Q)))
-
-which says:
-
-\"Search for any entry matching host `nonesuch'
- or `twosuch' in backends of type `netrc', maximum one result.
-
- Create a new entry if you found none.  The netrc backend will
- automatically require host, user, and port.  The host will be
- `nonesuch' and Q will be `qqqq'.  We prompt for the password
- with the shown prompt.  We will not prompt for Q.  The resulting
- token will have keys user, host, port, A, B, and Q.  It will not
- have P with any value, even though P is used in the search to
- find only entries that have P set to `pppp'.\"
-
-When multiple values are specified in the search parameter, the
-user is prompted for which one.  So :host (X Y Z) would ask the
-user to choose between X, Y, and Z.
-
-This creation can fail if the search was not specific enough to
-create a new token (it's up to the backend to decide that).  You
-should `catch' the backend-specific error as usual.  Some
-backends (netrc, at least) will prompt the user rather than throw
-an error.
-
-:require (A B C) means that only results that contain those
-tokens will be returned.  Thus for instance requiring :secret
-will ensure that any results will actually have a :secret
-property.
-
-:delete t means to delete any found entries.  nil by default.
-Use `auth-source-delete' in ELisp code instead of calling
-`auth-source-search' directly with this parameter.
-
-:type (X Y Z) will check only those backend types.  `netrc' and
-`secrets' are the only ones supported right now.
-
-:max N means to try to return at most N items (defaults to 1).
-More than N items may be returned, depending on the search and
-the backend.
-
-When :max is 0 the function will return just t or nil to indicate
-if any matches were found.
-
-:host (X Y Z) means to match only hosts X, Y, or Z according to
-the match rules above.  Defaults to t.
-
-:user (X Y Z) means to match only users X, Y, or Z according to
-the match rules above.  Defaults to t.
-
-:port (P Q R) means to match only protocols P, Q, or R.
-Defaults to t.
-
-:K (V1 V2 V3) for any other key K will match values V1, V2, or
-V3 (note the match rules above).
-
-The return value is a list with at most :max tokens.  Each token
-is a plist with keys :backend :host :port :user, plus any other
-keys provided by the backend (notably :secret).  But note the
-exception for :max 0, which see above.
-
-The token can hold a :save-function key.  If you call that, the
-user will be prompted to save the data to the backend.  You can't
-request that this should happen right after creation, because
-`auth-source-search' has no way of knowing if the token is
-actually useful.  So the caller must arrange to call this function.
-
-The token's :secret key can hold a function.  In that case you
-must call it to obtain the actual value."
-  (let* ((backends (mapcar #'auth-source-backend-parse auth-sources))
-         (max (or max 1))
-         (ignored-keys '(:require :create :delete :max))
-         (keys (loop for i below (length spec) by 2
-                     unless (memq (nth i spec) ignored-keys)
-                     collect (nth i spec)))
-         (cached (auth-source-remembered-p spec))
-         ;; note that we may have cached results but found is still nil
-         ;; (there were no results from the search)
-         (found (auth-source-recall spec))
-         filtered-backends)
-
-    (if (and cached auth-source-do-cache)
-        (auth-source-do-debug
-         "auth-source-search: found %d CACHED results matching %S"
-         (length found) spec)
-
-      (assert
-       (or (eq t create) (listp create)) t
-       "Invalid auth-source :create parameter (must be t or a list): %s %s")
-
-      (assert
-       (listp require) t
-       "Invalid auth-source :require parameter (must be a list): %s")
-
-      (setq filtered-backends (copy-sequence backends))
-      (dolist (backend backends)
-        (dolist (key keys)
-          ;; ignore invalid slots
-          (condition-case nil
-              (unless (auth-source-search-collection
-                       (plist-get spec key)
-                       (slot-value backend key))
-                (setq filtered-backends (delq backend filtered-backends))
-                (return))
-            (invalid-slot-name nil))))
-
-      (auth-source-do-trivia
-       "auth-source-search: found %d backends matching %S"
-       (length filtered-backends) spec)
-
-      ;; (debug spec "filtered" filtered-backends)
-      ;; First go through all the backends without :create, so we can
-      ;; query them all.
-      (setq found (auth-source-search-backends filtered-backends
-                                               spec
-                                               ;; to exit early
-                                               max
-                                               ;; create is always nil here
-                                               nil delete
-                                               require))
-
-      (auth-source-do-debug
-       "auth-source-search: found %d results (max %d) matching %S"
-       (length found) max spec)
-
-      ;; If we didn't find anything, then we allow the backend(s) to
-      ;; create the entries.
-      (when (and create
-                 (not found))
-        (setq found (auth-source-search-backends filtered-backends
-                                                 spec
-                                                 ;; to exit early
-                                                 max
-                                                 create delete
-                                                 require))
-        (auth-source-do-debug
-         "auth-source-search: CREATED %d results (max %d) matching %S"
-         (length found) max spec))
-
-      ;; note we remember the lack of result too, if it's applicable
-      (when auth-source-do-cache
-        (auth-source-remember spec found)))
-
-    (if (zerop max)
-        (not (null found))
-      found)))
-
-(defun auth-source-search-backends (backends spec max create delete require)
-  (let ((max (if (zerop max) 1 max)) ; stop with 1 match if we're asked for zero
-        matches)
-    (dolist (backend backends)
-      (when (> max (length matches)) ; if we need more matches...
-        (let* ((bmatches (apply
-                          (slot-value backend 'search-function)
-                          :backend backend
-                          :type (slot-value backend 'type)
-                          ;; note we're overriding whatever the spec
-                          ;; has for :max, :require, :create, and :delete
-                          :max max
-                          :require require
-                          :create create
-                          :delete delete
-                          spec)))
-          (when bmatches
-            (auth-source-do-trivia
-             "auth-source-search-backend: got %d (max %d) in %s:%s matching %S"
-             (length bmatches) max
-             (slot-value backend 'type)
-             (slot-value backend 'source)
-             spec)
-            (setq matches (append matches bmatches))))))
-    matches))
-
-;; (auth-source-search :max 0)
-;; (auth-source-search :max 1)
-;; (funcall (plist-get (nth 0 (auth-source-search :max 1)) :secret))
-;; (auth-source-search :host "nonesuch" :type 'netrc :K 1)
-;; (auth-source-search :host "nonesuch" :type 'secrets)
-
-(defun auth-source-delete (&rest spec)
-  "Delete entries from the authentication backends according to SPEC.
-Calls `auth-source-search' with the :delete property in SPEC set to t.
-The backend may not actually delete the entries.
-
-Returns the deleted entries."
-  (auth-source-search (plist-put spec :delete t)))
-
-(defun auth-source-search-collection (collection value)
-  "Returns t is VALUE is t or COLLECTION is t or COLLECTION contains VALUE."
-  (when (and (atom collection) (not (eq t collection)))
-    (setq collection (list collection)))
-
-  ;; (debug :collection collection :value value)
-  (or (eq collection t)
-      (eq value t)
-      (equal collection value)
-      (member value collection)))
-
-(defvar auth-source-netrc-cache nil)
-
-(defun auth-source-forget-all-cached ()
-  "Forget all cached auth-source data."
-  (interactive)
-  (loop for sym being the symbols of password-data
-        ;; when the symbol name starts with auth-source-magic
-        when (string-match (concat "^" auth-source-magic)
-                           (symbol-name sym))
-        ;; remove that key
-        do (password-cache-remove (symbol-name sym)))
-  (setq auth-source-netrc-cache nil))
-
-(defun auth-source-format-cache-entry (spec)
-  "Format SPEC entry to put it in the password cache."
-  (concat auth-source-magic (format "%S" spec)))
-
-(defun auth-source-remember (spec found)
-  "Remember FOUND search results for SPEC."
-  (let ((password-cache-expiry auth-source-cache-expiry))
-    (password-cache-add
-     (auth-source-format-cache-entry spec) found)))
-
-(defun auth-source-recall (spec)
-  "Recall FOUND search results for SPEC."
-  (password-read-from-cache (auth-source-format-cache-entry spec)))
-
-(defun auth-source-remembered-p (spec)
-  "Check if SPEC is remembered."
-  (password-in-cache-p
-   (auth-source-format-cache-entry spec)))
-
-(defun auth-source-forget (spec)
-  "Forget any cached data matching SPEC exactly.
-
-This is the same SPEC you passed to `auth-source-search'.
-Returns t or nil for forgotten or not found."
-  (password-cache-remove (auth-source-format-cache-entry spec)))
-
-;; (loop for sym being the symbols of password-data when (string-match (concat "^" auth-source-magic) (symbol-name sym)) collect (symbol-name sym))
-
-;; (auth-source-remember '(:host "wedd") '(4 5 6))
-;; (auth-source-remembered-p '(:host "wedd"))
-;; (auth-source-remember '(:host "xedd") '(1 2 3))
-;; (auth-source-remembered-p '(:host "xedd"))
-;; (auth-source-remembered-p '(:host "zedd"))
-;; (auth-source-recall '(:host "xedd"))
-;; (auth-source-recall '(:host t))
-;; (auth-source-forget+ :host t)
-
-(defun auth-source-forget+ (&rest spec)
-  "Forget any cached data matching SPEC.  Returns forgotten count.
-
-This is not a full `auth-source-search' spec but works similarly.
-For instance, \(:host \"myhost\" \"yourhost\") would find all the
-cached data that was found with a search for those two hosts,
-while \(:host t) would find all host entries."
-  (let ((count 0)
-        sname)
-    (loop for sym being the symbols of password-data
-          ;; when the symbol name matches with auth-source-magic
-          when (and (setq sname (symbol-name sym))
-                    (string-match (concat "^" auth-source-magic "\\(.+\\)")
-                                  sname)
-                    ;; and the spec matches what was stored in the cache
-                    (auth-source-specmatchp spec (read (match-string 1 sname))))
-          ;; remove that key
-          do (progn
-               (password-cache-remove sname)
-               (incf count)))
-    count))
-
-(defun auth-source-specmatchp (spec stored)
-  (let ((keys (loop for i below (length spec) by 2
-                    collect (nth i spec))))
-    (not (eq
-          (dolist (key keys)
-            (unless (auth-source-search-collection (plist-get stored key)
-                                                   (plist-get spec key))
-              (return 'no)))
-          'no))))
-
-;; (auth-source-pick-first-password :host "z.lifelogs.com")
-;; (auth-source-pick-first-password :port "imap")
-(defun auth-source-pick-first-password (&rest spec)
-  "Pick the first secret found from applying SPEC to `auth-source-search'."
-  (let* ((result (nth 0 (apply #'auth-source-search (plist-put spec :max 1))))
-         (secret (plist-get result :secret)))
-
-    (if (functionp secret)
-        (funcall secret)
-      secret)))
-
-;; (auth-source-format-prompt "test %u %h %p" '((?u "user") (?h "host")))
-(defun auth-source-format-prompt (prompt alist)
-  "Format PROMPT using %x (for any character x) specifiers in ALIST."
-  (dolist (cell alist)
-    (let ((c (nth 0 cell))
-          (v (nth 1 cell)))
-      (when (and c v)
-        (setq prompt (replace-regexp-in-string (format "%%%c" c)
-                                               (format "%s" v)
-                                               prompt nil t)))))
-  prompt)
-
-(defun auth-source-ensure-strings (values)
-  (if (eq values t)
-      values
-    (unless (listp values)
-      (setq values (list values)))
-    (mapcar (lambda (value)
-             (if (numberp value)
-                 (format "%s" value)
-               value))
-           values)))
-
-;;; Backend specific parsing: netrc/authinfo backend
-
-(defun auth-source--aput-1 (alist key val)
-  (let ((seen ())
-        (rest alist))
-    (while (and (consp rest) (not (equal key (caar rest))))
-      (push (pop rest) seen))
-    (cons (cons key val)
-          (if (null rest) alist
-            (nconc (nreverse seen)
-                   (if (equal key (caar rest)) (cdr rest) rest))))))
-(defmacro auth-source--aput (var key val)
-  `(setq ,var (auth-source--aput-1 ,var ,key ,val)))
-
-(defun auth-source--aget (alist key)
-  (cdr (assoc key alist)))
-
-;; (auth-source-netrc-parse :file "~/.authinfo.gpg")
-(defun* auth-source-netrc-parse (&key file max host user port require
-                                 &allow-other-keys)
-  "Parse FILE and return a list of all entries in the file.
-Note that the MAX parameter is used so we can exit the parse early."
-  (if (listp file)
-      ;; We got already parsed contents; just return it.
-      file
-    (when (file-exists-p file)
-      (setq port (auth-source-ensure-strings port))
-      (with-temp-buffer
-        (let* ((max (or max 5000))       ; sanity check: default to stop at 5K
-               (modified 0)
-               (cached (cdr-safe (assoc file auth-source-netrc-cache)))
-               (cached-mtime (plist-get cached :mtime))
-               (cached-secrets (plist-get cached :secret))
-               (check (lambda(alist)
-                        (and alist
-                             (auth-source-search-collection
-                              host
-                              (or
-                               (auth-source--aget alist "machine")
-                               (auth-source--aget alist "host")
-                               t))
-                             (auth-source-search-collection
-                              user
-                              (or
-                               (auth-source--aget alist "login")
-                               (auth-source--aget alist "account")
-                               (auth-source--aget alist "user")
-                               t))
-                             (auth-source-search-collection
-                              port
-                              (or
-                               (auth-source--aget alist "port")
-                               (auth-source--aget alist "protocol")
-                               t))
-                             (or
-                              ;; the required list of keys is nil, or
-                              (null require)
-                              ;; every element of require is in n(ormalized)
-                              (let ((n (nth 0 (auth-source-netrc-normalize
-                                               (list alist) file))))
-                                (loop for req in require
-                                      always (plist-get n req)))))))
-               result)
-
-          (if (and (functionp cached-secrets)
-                   (equal cached-mtime
-                          (nth 5 (file-attributes file))))
-              (progn
-                (auth-source-do-trivia
-                 "auth-source-netrc-parse: using CACHED file data for %s"
-                 file)
-                (insert (funcall cached-secrets)))
-            (insert-file-contents file)
-            ;; cache all netrc files (used to be just .gpg files)
-            ;; Store the contents of the file heavily encrypted in memory.
-            ;; (note for the irony-impaired: they are just obfuscated)
-            (auth-source--aput
-             auth-source-netrc-cache file
-             (list :mtime (nth 5 (file-attributes file))
-                   :secret (lexical-let ((v (mapcar #'1+ (buffer-string))))
-                             (lambda () (apply #'string (mapcar #'1- v)))))))
-          (goto-char (point-min))
-          (let ((entries (auth-source-netrc-parse-entries check max))
-                alist)
-            (while (setq alist (pop entries))
-                (push (nreverse alist) result)))
-
-          (when (< 0 modified)
-            (when auth-source-gpg-encrypt-to
-              ;; (see bug#7487) making `epa-file-encrypt-to' local to
-              ;; this buffer lets epa-file skip the key selection query
-              ;; (see the `local-variable-p' check in
-              ;; `epa-file-write-region').
-              (unless (local-variable-p 'epa-file-encrypt-to (current-buffer))
-                (make-local-variable 'epa-file-encrypt-to))
-              (if (listp auth-source-gpg-encrypt-to)
-                  (setq epa-file-encrypt-to auth-source-gpg-encrypt-to)))
-
-            ;; ask AFTER we've successfully opened the file
-            (when (y-or-n-p (format "Save file %s? (%d deletions)"
-                                    file modified))
-              (write-region (point-min) (point-max) file nil 'silent)
-              (auth-source-do-debug
-               "auth-source-netrc-parse: modified %d lines in %s"
-               modified file)))
-
-          (nreverse result))))))
-
-(defun auth-source-netrc-parse-next-interesting ()
-  "Advance to the next interesting position in the current buffer."
-  ;; If we're looking at a comment or are at the end of the line, move forward
-  (while (or (looking-at "#")
-             (and (eolp)
-                  (not (eobp))))
-    (forward-line 1))
-  (skip-chars-forward "\t "))
-
-(defun auth-source-netrc-parse-one ()
-  "Read one thing from the current buffer."
-  (auth-source-netrc-parse-next-interesting)
-
-  (when (or (looking-at "'\\([^']*\\)'")
-            (looking-at "\"\\([^\"]*\\)\"")
-            (looking-at "\\([^ \t\n]+\\)"))
-    (forward-char (length (match-string 0)))
-    (auth-source-netrc-parse-next-interesting)
-    (match-string-no-properties 1)))
-
-;; with thanks to org-mode
-(defsubst auth-source-current-line (&optional pos)
-  (save-excursion
-    (and pos (goto-char pos))
-    ;; works also in narrowed buffer, because we start at 1, not point-min
-    (+ (if (bolp) 1 0) (count-lines 1 (point)))))
-
-(defun auth-source-netrc-parse-entries(check max)
-  "Parse up to MAX netrc entries, passed by CHECK, from the current buffer."
-  (let ((adder (lambda(check alist all)
-                 (when (and
-                        alist
-                        (> max (length all))
-                        (funcall check alist))
-                   (push alist all))
-                 all))
-        item item2 all alist default)
-    (while (setq item (auth-source-netrc-parse-one))
-      (setq default (equal item "default"))
-      ;; We're starting a new machine.  Save the old one.
-      (when (and alist
-                 (or default
-                     (equal item "machine")))
-        ;; (auth-source-do-trivia
-        ;;  "auth-source-netrc-parse-entries: got entry %S" alist)
-        (setq all (funcall adder check alist all)
-              alist nil))
-      ;; In default entries, we don't have a next token.
-      ;; We store them as ("machine" . t)
-      (if default
-          (push (cons "machine" t) alist)
-        ;; Not a default entry.  Grab the next item.
-        (when (setq item2 (auth-source-netrc-parse-one))
-          ;; Did we get a "machine" value?
-          (if (equal item2 "machine")
-             (error
-              "%s: Unexpected `machine' token at line %d"
-              "auth-source-netrc-parse-entries"
-              (auth-source-current-line))
-            (push (cons item item2) alist)))))
-
-    ;; Clean up: if there's an entry left over, use it.
-    (when alist
-      (setq all (funcall adder check alist all))
-      ;; (auth-source-do-trivia
-      ;;  "auth-source-netrc-parse-entries: got2 entry %S" alist)
-      )
-    (nreverse all)))
-
-(defvar auth-source-passphrase-alist nil)
-
-(defun auth-source-token-passphrase-callback-function (_context _key-id file)
-  (let* ((file (file-truename file))
-        (entry (assoc file auth-source-passphrase-alist))
-        passphrase)
-    ;; return the saved passphrase, calling a function if needed
-    (or (copy-sequence (if (functionp (cdr entry))
-                          (funcall (cdr entry))
-                        (cdr entry)))
-       (progn
-         (unless entry
-           (setq entry (list file))
-           (push entry auth-source-passphrase-alist))
-         (setq passphrase
-               (read-passwd
-                (format "Passphrase for %s tokens: " file)
-                t))
-         (setcdr entry (lexical-let ((p (copy-sequence passphrase)))
-                         (lambda () p)))
-         passphrase))))
-
-;; (auth-source-epa-extract-gpg-token "gpg:LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tClZlcnNpb246IEdudVBHIHYxLjQuMTEgKEdOVS9MaW51eCkKCmpBMEVBd01DT25qMjB1ak9rZnRneVI3K21iNm9aZWhuLzRad3cySkdlbnVaKzRpeEswWDY5di9icDI1U1dsQT0KPS9yc2wKLS0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQo=" "~/.netrc")
-(defun auth-source-epa-extract-gpg-token (secret file)
-  "Pass either the decoded SECRET or the gpg:BASE64DATA version.
-FILE is the file from which we obtained this token."
-  (when (string-match "^gpg:\\(.+\\)" secret)
-    (setq secret (base64-decode-string (match-string 1 secret))))
-  (let ((context (epg-make-context 'OpenPGP)))
-    (epg-context-set-passphrase-callback
-     context
-     (cons #'auth-source-token-passphrase-callback-function
-           file))
-    (epg-decrypt-string context secret)))
-
-(defvar pp-escape-newlines)
-
-;; (insert (auth-source-epa-make-gpg-token "mysecret" "~/.netrc"))
-(defun auth-source-epa-make-gpg-token (secret file)
-  (let ((context (epg-make-context 'OpenPGP))
-        (pp-escape-newlines nil)
-        cipher)
-    (setf (epg-context-armor context) t)
-    (epg-context-set-passphrase-callback
-     context
-     (cons #'auth-source-token-passphrase-callback-function
-           file))
-    (setq cipher (epg-encrypt-string context secret nil))
-    (with-temp-buffer
-      (insert cipher)
-      (base64-encode-region (point-min) (point-max) t)
-      (concat "gpg:" (buffer-substring-no-properties
-                      (point-min)
-                      (point-max))))))
-
-(defun auto-source--symbol-keyword (symbol)
-  (intern (format ":%s" symbol)))
-
-(defun auth-source-netrc-normalize (alist filename)
-  (mapcar (lambda (entry)
-            (let (ret item)
-              (while (setq item (pop entry))
-                (let ((k (car item))
-                      (v (cdr item)))
-
-                  ;; apply key aliases
-                  (setq k (cond ((member k '("machine")) "host")
-                                ((member k '("login" "account")) "user")
-                                ((member k '("protocol")) "port")
-                                ((member k '("password")) "secret")
-                                (t k)))
-
-                  ;; send back the secret in a function (lexical binding)
-                  (when (equal k "secret")
-                    (setq v (lexical-let ((lexv v)
-                                          (token-decoder nil))
-                              (when (string-match "^gpg:" lexv)
-                                ;; it's a GPG token: create a token decoder
-                                ;; which unsets itself once
-                                (setq token-decoder
-                                      (lambda (val)
-                                        (prog1
-                                            (auth-source-epa-extract-gpg-token
-                                             val
-                                             filename)
-                                          (setq token-decoder nil)))))
-                              (lambda ()
-                                (when token-decoder
-                                  (setq lexv (funcall token-decoder lexv)))
-                                lexv))))
-                  (setq ret (plist-put ret
-                                       (auto-source--symbol-keyword k)
-                                       v))))
-              ret))
-          alist))
-
-;; (setq secret (plist-get (nth 0 (auth-source-search :host t :type 'netrc :K 1 :max 1)) :secret))
-;; (funcall secret)
-
-(defun* auth-source-netrc-search (&rest
-                                  spec
-                                  &key backend require create
-                                  type max host user port
-                                  &allow-other-keys)
-  "Given a property list SPEC, return search matches from the :backend.
-See `auth-source-search' for details on SPEC."
-  ;; just in case, check that the type is correct (null or same as the backend)
-  (assert (or (null type) (eq type (oref backend type)))
-          t "Invalid netrc search: %s %s")
-
-  (let ((results (auth-source-netrc-normalize
-                  (auth-source-netrc-parse
-                   :max max
-                   :require require
-                   :file (oref backend source)
-                   :host (or host t)
-                   :user (or user t)
-                   :port (or port t))
-                  (oref backend source))))
-
-    ;; if we need to create an entry AND none were found to match
-    (when (and create
-               (not results))
-
-      ;; create based on the spec and record the value
-      (setq results (or
-                     ;; if the user did not want to create the entry
-                     ;; in the file, it will be returned
-                     (apply (slot-value backend 'create-function) spec)
-                     ;; if not, we do the search again without :create
-                     ;; to get the updated data.
-
-                     ;; the result will be returned, even if the search fails
-                     (apply #'auth-source-netrc-search
-                            (plist-put spec :create nil)))))
-    results))
-
-(defun auth-source-netrc-element-or-first (v)
-  (if (listp v)
-      (nth 0 v)
-    v))
-
-;; (auth-source-search :host "nonesuch" :type 'netrc :max 1 :create t)
-;; (auth-source-search :host "nonesuch" :type 'netrc :max 1 :create t :create-extra-keys '((A "default A") (B)))
-
-(defun* auth-source-netrc-create (&rest spec
-                                        &key backend
-                                        host port create
-                                        &allow-other-keys)
-  (let* ((base-required '(host user port secret))
-         ;; we know (because of an assertion in auth-source-search) that the
-         ;; :create parameter is either t or a list (which includes nil)
-         (create-extra (if (eq t create) nil create))
-         (current-data (car (auth-source-search :max 1
-                                                :host host
-                                                :port port)))
-         (required (append base-required create-extra))
-         (file (oref backend source))
-         (add "")
-         ;; `valist' is an alist
-         valist
-         ;; `artificial' will be returned if no creation is needed
-         artificial)
-
-    ;; only for base required elements (defined as function parameters):
-    ;; fill in the valist with whatever data we may have from the search
-    ;; we complete the first value if it's a list and use the value otherwise
-    (dolist (br base-required)
-      (let ((val (plist-get spec (auto-source--symbol-keyword br))))
-        (when val
-          (let ((br-choice (cond
-                            ;; all-accepting choice (predicate is t)
-                            ((eq t val) nil)
-                            ;; just the value otherwise
-                            (t val))))
-            (when br-choice
-              (auth-source--aput valist br br-choice))))))
-
-    ;; for extra required elements, see if the spec includes a value for them
-    (dolist (er create-extra)
-      (let ((k (auto-source--symbol-keyword er))
-            (keys (loop for i below (length spec) by 2
-                        collect (nth i spec))))
-        (when (memq k keys)
-          (auth-source--aput valist er (plist-get spec k)))))
-
-    ;; for each required element
-    (dolist (r required)
-      (let* ((data (auth-source--aget valist r))
-             ;; take the first element if the data is a list
-             (data (or (auth-source-netrc-element-or-first data)
-                       (plist-get current-data
-                                  (auto-source--symbol-keyword r))))
-             ;; this is the default to be offered
-             (given-default (auth-source--aget
-                             auth-source-creation-defaults r))
-             ;; the default supplementals are simple:
-             ;; for the user, try `given-default' and then (user-login-name);
-             ;; otherwise take `given-default'
-             (default (cond
-                       ((and (not given-default) (eq r 'user))
-                        (user-login-name))
-                       (t given-default)))
-             (printable-defaults (list
-                                  (cons 'user
-                                        (or
-                                         (auth-source-netrc-element-or-first
-                                          (auth-source--aget valist 'user))
-                                         (plist-get artificial :user)
-                                         "[any user]"))
-                                  (cons 'host
-                                        (or
-                                         (auth-source-netrc-element-or-first
-                                          (auth-source--aget valist 'host))
-                                         (plist-get artificial :host)
-                                         "[any host]"))
-                                  (cons 'port
-                                        (or
-                                         (auth-source-netrc-element-or-first
-                                          (auth-source--aget valist 'port))
-                                         (plist-get artificial :port)
-                                         "[any port]"))))
-             (prompt (or (auth-source--aget auth-source-creation-prompts r)
-                         (case r
-                           (secret "%p password for %u@%h: ")
-                           (user "%p user name for %h: ")
-                           (host "%p host name for user %u: ")
-                           (port "%p port for %u@%h: "))
-                         (format "Enter %s (%%u@%%h:%%p): " r)))
-             (prompt (auth-source-format-prompt
-                      prompt
-                      `((?u ,(auth-source--aget printable-defaults 'user))
-                        (?h ,(auth-source--aget printable-defaults 'host))
-                        (?p ,(auth-source--aget printable-defaults 'port))))))
-
-        ;; Store the data, prompting for the password if needed.
-        (setq data (or data
-                       (if (eq r 'secret)
-                           ;; Special case prompt for passwords.
-                           ;; TODO: make the default (setq auth-source-netrc-use-gpg-tokens `((,(if (boundp 'epa-file-auto-mode-alist-entry) (car epa-file-auto-mode-alist-entry) "\\.gpg\\'") nil) (t gpg)))
-                           ;; TODO: or maybe leave as (setq auth-source-netrc-use-gpg-tokens 'never)
-                           (let* ((ep (format "Use GPG password tokens in %s?" file))
-                                  (gpg-encrypt
-                                   (cond
-                                    ((eq auth-source-netrc-use-gpg-tokens 'never)
-                                     'never)
-                                    ((listp auth-source-netrc-use-gpg-tokens)
-                                     (let ((check (copy-sequence
-                                                   auth-source-netrc-use-gpg-tokens))
-                                           item ret)
-                                       (while check
-                                         (setq item (pop check))
-                                         (when (or (eq (car item) t)
-                                                   (string-match (car item) file))
-                                           (setq ret (cdr item))
-                                           (setq check nil)))
-                                       ;; FIXME: `ret' unused.
-                                       ;; Should we return it here?
-                                       ))
-                                    (t 'never)))
-                                  (plain (or (eval default) (read-passwd prompt))))
-                             ;; ask if we don't know what to do (in which case
-                             ;; auth-source-netrc-use-gpg-tokens must be a list)
-                             (unless gpg-encrypt
-                               (setq gpg-encrypt (if (y-or-n-p ep) 'gpg 'never))
-                               ;; TODO: save the defcustom now? or ask?
-                               (setq auth-source-netrc-use-gpg-tokens
-                                     (cons `(,file ,gpg-encrypt)
-                                           auth-source-netrc-use-gpg-tokens)))
-                             (if (eq gpg-encrypt 'gpg)
-                                 (auth-source-epa-make-gpg-token plain file)
-                               plain))
-                         (if (stringp default)
-                             (read-string (if (string-match ": *\\'" prompt)
-                                              (concat (substring prompt 0 (match-beginning 0))
-                                                      " (default " default "): ")
-                                            (concat prompt "(default " default ") "))
-                                          nil nil default)
-                           (eval default)))))
-
-        (when data
-          (setq artificial (plist-put artificial
-                                      (auto-source--symbol-keyword r)
-                                      (if (eq r 'secret)
-                                          (lexical-let ((data data))
-                                            (lambda () data))
-                                        data))))
-
-        ;; When r is not an empty string...
-        (when (and (stringp data)
-                   (< 0 (length data)))
-          ;; this function is not strictly necessary but I think it
-          ;; makes the code clearer -tzz
-          (let ((printer (lambda ()
-                           ;; append the key (the symbol name of r)
-                           ;; and the value in r
-                           (format "%s%s %s"
-                                   ;; prepend a space
-                                   (if (zerop (length add)) "" " ")
-                                   ;; remap auth-source tokens to netrc
-                                   (case r
-                                     (user   "login")
-                                     (host   "machine")
-                                     (secret "password")
-                                     (port   "port") ; redundant but clearer
-                                     (t (symbol-name r)))
-                                   (if (string-match "[\"# ]" data)
-                                       (format "%S" data)
-                                     data)))))
-            (setq add (concat add (funcall printer)))))))
-
-    (plist-put
-     artificial
-     :save-function
-     (lexical-let ((file file)
-                   (add add))
-       (lambda () (auth-source-netrc-saver file add))))
-
-    (list artificial)))
-
-;;(funcall (plist-get (nth 0 (auth-source-search :host '("nonesuch2") :user "tzz" :port "imap" :create t :max 1)) :save-function))
-(defun auth-source-netrc-saver (file add)
-  "Save a line ADD in FILE, prompting along the way.
-Respects `auth-source-save-behavior'.  Uses
-`auth-source-netrc-cache' to avoid prompting more than once."
-  (let* ((key (format "%s %s" file (rfc2104-hash 'md5 64 16 file add)))
-         (cached (assoc key auth-source-netrc-cache)))
-
-    (if cached
-        (auth-source-do-trivia
-         "auth-source-netrc-saver: found previous run for key %s, returning"
-         key)
-      (with-temp-buffer
-        (when (file-exists-p file)
-          (insert-file-contents file))
-        (when auth-source-gpg-encrypt-to
-          ;; (see bug#7487) making `epa-file-encrypt-to' local to
-          ;; this buffer lets epa-file skip the key selection query
-          ;; (see the `local-variable-p' check in
-          ;; `epa-file-write-region').
-          (unless (local-variable-p 'epa-file-encrypt-to (current-buffer))
-            (make-local-variable 'epa-file-encrypt-to))
-          (if (listp auth-source-gpg-encrypt-to)
-              (setq epa-file-encrypt-to auth-source-gpg-encrypt-to)))
-        ;; we want the new data to be found first, so insert at beginning
-        (goto-char (point-min))
-
-        ;; Ask AFTER we've successfully opened the file.
-        (let ((prompt (format "Save auth info to file %s? " file))
-              (done (not (eq auth-source-save-behavior 'ask)))
-              (bufname "*auth-source Help*")
-              k)
-          (while (not done)
-            (setq k (auth-source-read-char-choice prompt '(?y ?n ?N ?e ??)))
-            (case k
-              (?y (setq done t))
-              (?? (save-excursion
-                    (with-output-to-temp-buffer bufname
-                      (princ
-                       (concat "(y)es, save\n"
-                               "(n)o but use the info\n"
-                               "(N)o and don't ask to save again\n"
-                               "(e)dit the line\n"
-                               "(?) for help as you can see.\n"))
-                      ;; Why?  Doesn't with-output-to-temp-buffer already do
-                      ;; the exact same thing anyway?  --Stef
-                      (set-buffer standard-output)
-                      (help-mode))))
-              (?n (setq add ""
-                        done t))
-              (?N
-               (setq add ""
-                     done t)
-               (customize-save-variable 'auth-source-save-behavior nil))
-              (?e (setq add (read-string "Line to add: " add)))
-              (t nil)))
-
-          (when (get-buffer-window bufname)
-            (delete-window (get-buffer-window bufname)))
-
-          ;; Make sure the info is not saved.
-          (when (null auth-source-save-behavior)
-            (setq add ""))
-
-          (when (< 0 (length add))
-            (progn
-              (unless (bolp)
-                (insert "\n"))
-              (insert add "\n")
-              (write-region (point-min) (point-max) file nil 'silent)
-             ;; Make the .authinfo file non-world-readable.
-             (set-file-modes file #o600)
-              (auth-source-do-debug
-               "auth-source-netrc-create: wrote 1 new line to %s"
-               file)
-              (message "Saved new authentication information to %s" file)
-              nil))))
-      (auth-source--aput auth-source-netrc-cache key "ran"))))
-
-;;; Backend specific parsing: Secrets API backend
-
-;; (let ((auth-sources '(default))) (auth-source-search :max 1 :create t))
-;; (let ((auth-sources '(default))) (auth-source-search :max 1 :delete t))
-;; (let ((auth-sources '(default))) (auth-source-search :max 1))
-;; (let ((auth-sources '(default))) (auth-source-search))
-;; (let ((auth-sources '("secrets:Login"))) (auth-source-search :max 1))
-;; (let ((auth-sources '("secrets:Login"))) (auth-source-search :max 1 :signon_realm "https://git.gnus.org/Git"))
-
-(defun auth-source-secrets-listify-pattern (pattern)
-  "Convert a pattern with lists to a list of string patterns.
-
-auth-source patterns can have values of the form :foo (\"bar\"
-\"qux\"), which means to match any secret with :foo equal to
-\"bar\" or :foo equal to \"qux\".  The secrets backend supports
-only string values for patterns, so this routine returns a list
-of patterns that is equivalent to the single original pattern
-when interpreted such that if a secret matches any pattern in the
-list, it matches the original pattern."
-  (if (null pattern)
-      '(nil)
-    (let* ((key (pop pattern))
-           (value (pop pattern))
-           (tails (auth-source-secrets-listify-pattern pattern))
-           (heads (if (stringp value)
-                      (list (list key value))
-                    (mapcar (lambda (v) (list key v)) value))))
-      (loop
-         for h in heads
-         nconc
-           (loop
-              for tl in tails
-              collect (append h tl))))))
-
-(defun* auth-source-secrets-search (&rest
-                                    spec
-                                    &key backend create delete label max
-                                    &allow-other-keys)
-  "Search the Secrets API; spec is like `auth-source'.
-
-The :label key specifies the item's label.  It is the only key
-that can specify a substring.  Any :label value besides a string
-will allow any label.
-
-All other search keys must match exactly.  If you need substring
-matching, do a wider search and narrow it down yourself.
-
-You'll get back all the properties of the token as a plist.
-
-Here's an example that looks for the first item in the `Login'
-Secrets collection:
-
- (let ((auth-sources \\='(\"secrets:Login\")))
-    (auth-source-search :max 1)
-
-Here's another that looks for the first item in the `Login'
-Secrets collection whose label contains `gnus':
-
- (let ((auth-sources \\='(\"secrets:Login\")))
-    (auth-source-search :max 1 :label \"gnus\")
-
-And this one looks for the first item in the `Login' Secrets
-collection that's a Google Chrome entry for the git.gnus.org site
-authentication tokens:
-
- (let ((auth-sources \\='(\"secrets:Login\")))
-    (auth-source-search :max 1 :signon_realm \"https://git.gnus.org/Git\"))
-"
-
-  ;; TODO
-  (assert (not create) nil
-          "The Secrets API auth-source backend doesn't support creation yet")
-  ;; TODO
-  ;; (secrets-delete-item coll elt)
-  (assert (not delete) nil
-          "The Secrets API auth-source backend doesn't support deletion yet")
-
-  (let* ((coll (oref backend source))
-         (max (or max 5000))     ; sanity check: default to stop at 5K
-         (ignored-keys '(:create :delete :max :backend :label :require :type))
-         (search-keys (loop for i below (length spec) by 2
-                            unless (memq (nth i spec) ignored-keys)
-                            collect (nth i spec)))
-         ;; build a search spec without the ignored keys
-         ;; if a search key is nil or t (match anything), we skip it
-         (search-specs (auth-source-secrets-listify-pattern
-                        (apply #'append (mapcar
-                                      (lambda (k)
-                                        (if (or (null (plist-get spec k))
-                                                (eq t (plist-get spec k)))
-                                            nil
-                                          (list k (plist-get spec k))))
-                                      search-keys))))
-         ;; needed keys (always including host, login, port, and secret)
-         (returned-keys (delete-dups (append
-                                     '(:host :login :port :secret)
-                                     search-keys)))
-         (items
-          (loop for search-spec in search-specs
-               nconc
-               (loop for item in (apply #'secrets-search-items coll search-spec)
-                  unless (and (stringp label)
-                              (not (string-match label item)))
-                  collect item)))
-         ;; TODO: respect max in `secrets-search-items', not after the fact
-         (items (butlast items (- (length items) max)))
-         ;; convert the item name to a full plist
-         (items (mapcar (lambda (item)
-                          (append
-                           ;; make an entry for the secret (password) element
-                           (list
-                            :secret
-                            (lexical-let ((v (secrets-get-secret coll item)))
-                              (lambda () v)))
-                           ;; rewrite the entry from ((k1 v1) (k2 v2)) to plist
-                           (apply #'append
-                                  (mapcar (lambda (entry)
-                                            (list (car entry) (cdr entry)))
-                                          (secrets-get-attributes coll item)))))
-                        items))
-         ;; ensure each item has each key in `returned-keys'
-         (items (mapcar (lambda (plist)
-                          (append
-                           (apply #'append
-                                  (mapcar (lambda (req)
-                                            (if (plist-get plist req)
-                                                nil
-                                              (list req nil)))
-                                          returned-keys))
-                           plist))
-                        items)))
-    items))
-
-(defun auth-source-secrets-create (&rest spec)
-  ;; TODO
-  ;; (apply 'secrets-create-item (auth-get-source entry) name passwd spec)
-  (debug spec))
-
-;;; Backend specific parsing: Mac OS Keychain (using /usr/bin/security) backend
-
-;; (let ((auth-sources '(macos-keychain-internet))) (auth-source-search :max 1 :create t))
-;; (let ((auth-sources '(macos-keychain-internet))) (auth-source-search :max 1 :delete t))
-;; (let ((auth-sources '(macos-keychain-internet))) (auth-source-search :max 1))
-;; (let ((auth-sources '(macos-keychain-internet))) (auth-source-search))
-
-;; (let ((auth-sources '(macos-keychain-generic))) (auth-source-search :max 1 :create t))
-;; (let ((auth-sources '(macos-keychain-generic))) (auth-source-search :max 1 :delete t))
-;; (let ((auth-sources '(macos-keychain-generic))) (auth-source-search :max 1))
-;; (let ((auth-sources '(macos-keychain-generic))) (auth-source-search))
-
-;; (let ((auth-sources '("macos-keychain-internet:/Users/tzz/Library/Keychains/login.keychain"))) (auth-source-search :max 1))
-;; (let ((auth-sources '("macos-keychain-generic:Login"))) (auth-source-search :max 1 :host "git.gnus.org"))
-;; (let ((auth-sources '("macos-keychain-generic:Login"))) (auth-source-search :max 1))
-
-(defun* auth-source-macos-keychain-search (&rest
-                                    spec
-                                    &key backend create delete
-                                    type max
-                                    &allow-other-keys)
-  "Search the MacOS Keychain; spec is like `auth-source'.
-
-All search keys must match exactly.  If you need substring
-matching, do a wider search and narrow it down yourself.
-
-You'll get back all the properties of the token as a plist.
-
-The :type key is either `macos-keychain-internet' or
-`macos-keychain-generic'.
-
-For the internet keychain type, the :label key searches the
-item's labels (\"-l LABEL\" passed to \"/usr/bin/security\").
-Similarly, :host maps to \"-s HOST\", :user maps to \"-a USER\",
-and :port maps to \"-P PORT\" or \"-r PROT\"
-\(note PROT has to be a 4-character string).
-
-For the generic keychain type, the :label key searches the item's
-labels (\"-l LABEL\" passed to \"/usr/bin/security\").
-Similarly, :host maps to \"-c HOST\" (the \"creator\" keychain
-field), :user maps to \"-a USER\", and :port maps to \"-s PORT\".
-
-Here's an example that looks for the first item in the default
-generic MacOS Keychain:
-
- (let ((auth-sources \\='(macos-keychain-generic)))
-    (auth-source-search :max 1)
-
-Here's another that looks for the first item in the internet
-MacOS Keychain collection whose label is `gnus':
-
- (let ((auth-sources \\='(macos-keychain-internet)))
-    (auth-source-search :max 1 :label \"gnus\")
-
-And this one looks for the first item in the internet keychain
-entries for git.gnus.org:
-
- (let ((auth-sources \\='(macos-keychain-internet\")))
-    (auth-source-search :max 1 :host \"git.gnus.org\"))
-"
-  ;; TODO
-  (assert (not create) nil
-          "The MacOS Keychain auth-source backend doesn't support creation yet")
-  ;; TODO
-  ;; (macos-keychain-delete-item coll elt)
-  (assert (not delete) nil
-          "The MacOS Keychain auth-source backend doesn't support deletion yet")
-
-  (let* ((coll (oref backend source))
-         (max (or max 5000))     ; sanity check: default to stop at 5K
-         ;; Filter out ignored keys from the spec
-         (ignored-keys '(:create :delete :max :backend :label :host :port))
-         ;; Build a search spec without the ignored keys
-         (search-keys (loop for i below (length spec) by 2
-                            unless (memq (nth i spec) ignored-keys)
-                            collect (nth i spec)))
-         ;; If a search key value is nil or t (match anything), we skip it
-         (search-spec (apply #'append (mapcar
-                                      (lambda (k)
-                                        (if (or (null (plist-get spec k))
-                                                (eq t (plist-get spec k)))
-                                            nil
-                                          (list k (plist-get spec k))))
-                                      search-keys)))
-         ;; needed keys (always including host, login, port, and secret)
-         (returned-keys (delete-dups (append
-                                     '(:host :login :port :secret)
-                                     search-keys)))
-         ;; Extract host and port from spec
-         (hosts (plist-get spec :host))
-         (hosts (if (and hosts (listp hosts)) hosts `(,hosts)))
-         (ports (plist-get spec :port))
-         (ports (if (and ports (listp ports)) ports `(,ports)))
-         ;; Loop through all combinations of host/port and pass each of these to
-         ;; auth-source-macos-keychain-search-items
-         (items (catch 'match
-                  (dolist (host hosts)
-                    (dolist (port ports)
-                      (let* ((port (format "%S" port))
-                             (items (apply #'auth-source-macos-keychain-search-items
-                                           coll
-                                           type
-                                           max
-                                           host port
-                                           search-spec)))
-                        (when items
-                          (throw 'match items)))))))
-
-         ;; ensure each item has each key in `returned-keys'
-         (items (mapcar (lambda (plist)
-                          (append
-                           (apply #'append
-                                  (mapcar (lambda (req)
-                                            (if (plist-get plist req)
-                                                nil
-                                              (list req nil)))
-                                          returned-keys))
-                           plist))
-                        items)))
-    items))
-
-(defun* auth-source-macos-keychain-search-items (coll _type _max
-                                                      host port
-                                                      &key label type
-                                                      user
-                                                      &allow-other-keys)
-
-  (let* ((keychain-generic (eq type 'macos-keychain-generic))
-         (args `(,(if keychain-generic
-                      "find-generic-password"
-                    "find-internet-password")
-                 "-g"))
-         (ret (list :type type)))
-    (when label
-      (setq args (append args (list "-l" label))))
-    (when host
-      (setq args (append args (list (if keychain-generic "-c" "-s") host))))
-    (when user
-      (setq args (append args (list "-a" user))))
-
-    (when port
-      (if keychain-generic
-          (setq args (append args (list "-s" port)))
-        (setq args (append args (list
-                                 (if (string-match "[0-9]+" port) "-P" "-r")
-                                 port)))))
-
-      (unless (equal coll "default")
-        (setq args (append args (list coll))))
-
-      (with-temp-buffer
-        (apply #'call-process "/usr/bin/security" nil t nil args)
-        (goto-char (point-min))
-        (while (not (eobp))
-          (cond
-           ((looking-at "^password: \"\\(.+\\)\"$")
-            (setq ret (auth-source-macos-keychain-result-append
-                       ret
-                       keychain-generic
-                       "secret"
-                       (lexical-let ((v (match-string 1)))
-                         (lambda () v)))))
-           ;; TODO: check if this is really the label
-           ;; match 0x00000007 <blob>="AppleID"
-           ((looking-at "^[ ]+0x00000007 <blob>=\"\\(.+\\)\"")
-            (setq ret (auth-source-macos-keychain-result-append
-                       ret
-                       keychain-generic
-                       "label"
-                       (match-string 1))))
-           ;; match "crtr"<uint32>="aapl"
-           ;; match "svce"<blob>="AppleID"
-           ((looking-at "^[ ]+\"\\([a-z]+\\)\"[^=]+=\"\\(.+\\)\"")
-            (setq ret (auth-source-macos-keychain-result-append
-                       ret
-                       keychain-generic
-                       (match-string 1)
-                       (match-string 2)))))
-          (forward-line)))
-      ;; return `ret' iff it has the :secret key
-      (and (plist-get ret :secret) (list ret))))
-
-(defun auth-source-macos-keychain-result-append (result generic k v)
-  (push v result)
-  (push (auto-source--symbol-keyword
-         (cond
-          ((equal k "acct") "user")
-          ;; for generic keychains, creator is host, service is port
-          ((and generic (equal k "crtr")) "host")
-          ((and generic (equal k "svce")) "port")
-          ;; for internet keychains, protocol is port, server is host
-          ((and (not generic) (equal k "ptcl")) "port")
-          ((and (not generic) (equal k "srvr")) "host")
-          (t k)))
-        result))
-
-(defun auth-source-macos-keychain-create (&rest spec)
-  ;; TODO
-  (debug spec))
-
-;;; Backend specific parsing: PLSTORE backend
-
-(defun* auth-source-plstore-search (&rest
-                                    spec
-                                    &key backend create delete
-                                    max
-                                    &allow-other-keys)
-  "Search the PLSTORE; spec is like `auth-source'."
-  (let* ((store (oref backend data))
-         (max (or max 5000))     ; sanity check: default to stop at 5K
-         (ignored-keys '(:create :delete :max :backend :label :require :type))
-         (search-keys (loop for i below (length spec) by 2
-                            unless (memq (nth i spec) ignored-keys)
-                            collect (nth i spec)))
-         ;; build a search spec without the ignored keys
-         ;; if a search key is nil or t (match anything), we skip it
-         (search-spec (apply #'append (mapcar
-                                      (lambda (k)
-                                        (let ((v (plist-get spec k)))
-                                          (if (or (null v)
-                                                  (eq t v))
-                                              nil
-                                            (if (stringp v)
-                                                (setq v (list v)))
-                                            (list k v))))
-                                      search-keys)))
-         ;; needed keys (always including host, login, port, and secret)
-         (returned-keys (delete-dups (append
-                                     '(:host :login :port :secret)
-                                     search-keys)))
-         (items (plstore-find store search-spec))
-         (item-names (mapcar #'car items))
-         (items (butlast items (- (length items) max)))
-         ;; convert the item to a full plist
-         (items (mapcar (lambda (item)
-                          (let* ((plist (copy-tree (cdr item)))
-                                 (secret (plist-member plist :secret)))
-                            (if secret
-                                (setcar
-                                 (cdr secret)
-                                 (lexical-let ((v (car (cdr secret))))
-                                   (lambda () v))))
-                            plist))
-                        items))
-         ;; ensure each item has each key in `returned-keys'
-         (items (mapcar (lambda (plist)
-                          (append
-                           (apply #'append
-                                  (mapcar (lambda (req)
-                                            (if (plist-get plist req)
-                                                nil
-                                              (list req nil)))
-                                          returned-keys))
-                           plist))
-                        items)))
-    (cond
-     ;; if we need to create an entry AND none were found to match
-     ((and create
-           (not items))
-
-      ;; create based on the spec and record the value
-      (setq items (or
-                   ;; if the user did not want to create the entry
-                   ;; in the file, it will be returned
-                   (apply (slot-value backend 'create-function) spec)
-                   ;; if not, we do the search again without :create
-                   ;; to get the updated data.
-
-                   ;; the result will be returned, even if the search fails
-                   (apply #'auth-source-plstore-search
-                          (plist-put spec :create nil)))))
-     ((and delete
-           item-names)
-      (dolist (item-name item-names)
-        (plstore-delete store item-name))
-      (plstore-save store)))
-    items))
-
-(defun* auth-source-plstore-create (&rest spec
-                                          &key backend
-                                          host port create
-                                          &allow-other-keys)
-  (let* ((base-required '(host user port secret))
-         (base-secret '(secret))
-         ;; we know (because of an assertion in auth-source-search) that the
-         ;; :create parameter is either t or a list (which includes nil)
-         (create-extra (if (eq t create) nil create))
-         (current-data (car (auth-source-search :max 1
-                                                :host host
-                                                :port port)))
-         (required (append base-required create-extra))
-         ;; `valist' is an alist
-         valist
-         ;; `artificial' will be returned if no creation is needed
-         artificial
-         secret-artificial)
-
-    ;; only for base required elements (defined as function parameters):
-    ;; fill in the valist with whatever data we may have from the search
-    ;; we complete the first value if it's a list and use the value otherwise
-    (dolist (br base-required)
-      (let ((val (plist-get spec (auto-source--symbol-keyword br))))
-        (when val
-          (let ((br-choice (cond
-                            ;; all-accepting choice (predicate is t)
-                            ((eq t val) nil)
-                            ;; just the value otherwise
-                            (t val))))
-            (when br-choice
-              (auth-source--aput valist br br-choice))))))
-
-    ;; for extra required elements, see if the spec includes a value for them
-    (dolist (er create-extra)
-      (let ((k (auto-source--symbol-keyword er))
-            (keys (loop for i below (length spec) by 2
-                        collect (nth i spec))))
-        (when (memq k keys)
-          (auth-source--aput valist er (plist-get spec k)))))
-
-    ;; for each required element
-    (dolist (r required)
-      (let* ((data (auth-source--aget valist r))
-             ;; take the first element if the data is a list
-             (data (or (auth-source-netrc-element-or-first data)
-                       (plist-get current-data
-                                  (auto-source--symbol-keyword r))))
-             ;; this is the default to be offered
-             (given-default (auth-source--aget
-                             auth-source-creation-defaults r))
-             ;; the default supplementals are simple:
-             ;; for the user, try `given-default' and then (user-login-name);
-             ;; otherwise take `given-default'
-             (default (cond
-                       ((and (not given-default) (eq r 'user))
-                        (user-login-name))
-                       (t given-default)))
-             (printable-defaults (list
-                                  (cons 'user
-                                        (or
-                                         (auth-source-netrc-element-or-first
-                                          (auth-source--aget valist 'user))
-                                         (plist-get artificial :user)
-                                         "[any user]"))
-                                  (cons 'host
-                                        (or
-                                         (auth-source-netrc-element-or-first
-                                          (auth-source--aget valist 'host))
-                                         (plist-get artificial :host)
-                                         "[any host]"))
-                                  (cons 'port
-                                        (or
-                                         (auth-source-netrc-element-or-first
-                                          (auth-source--aget valist 'port))
-                                         (plist-get artificial :port)
-                                         "[any port]"))))
-             (prompt (or (auth-source--aget auth-source-creation-prompts r)
-                         (case r
-                           (secret "%p password for %u@%h: ")
-                           (user "%p user name for %h: ")
-                           (host "%p host name for user %u: ")
-                           (port "%p port for %u@%h: "))
-                         (format "Enter %s (%%u@%%h:%%p): " r)))
-             (prompt (auth-source-format-prompt
-                      prompt
-                      `((?u ,(auth-source--aget printable-defaults 'user))
-                        (?h ,(auth-source--aget printable-defaults 'host))
-                        (?p ,(auth-source--aget printable-defaults 'port))))))
-
-        ;; Store the data, prompting for the password if needed.
-        (setq data (or data
-                       (if (eq r 'secret)
-                           (or (eval default) (read-passwd prompt))
-                         (if (stringp default)
-                             (read-string
-                              (if (string-match ": *\\'" prompt)
-                                  (concat (substring prompt 0 (match-beginning 0))
-                                          " (default " default "): ")
-                                (concat prompt "(default " default ") "))
-                              nil nil default)
-                           (eval default)))))
-
-        (when data
-          (if (member r base-secret)
-              (setq secret-artificial
-                    (plist-put secret-artificial
-                               (auto-source--symbol-keyword r)
-                               data))
-            (setq artificial (plist-put artificial
-                                        (auto-source--symbol-keyword r)
-                                        data))))))
-    (plstore-put (oref backend data)
-                 (sha1 (format "%s@%s:%s"
-                               (plist-get artificial :user)
-                               (plist-get artificial :host)
-                               (plist-get artificial :port)))
-                 artificial secret-artificial)
-    (if (y-or-n-p (format "Save auth info to file %s? "
-                          (plstore-get-file (oref backend data))))
-        (plstore-save (oref backend data)))))
-
-;;; older API
-
-;; (auth-source-user-or-password '("login" "password") "imap.myhost.com" t "tzz")
-
-;; deprecate the old interface
-(make-obsolete 'auth-source-user-or-password
-               'auth-source-search "Emacs 24.1")
-(make-obsolete 'auth-source-forget-user-or-password
-               'auth-source-forget "Emacs 24.1")
-
-(defun auth-source-user-or-password
-  (mode host port &optional username create-missing delete-existing)
-  "Find MODE (string or list of strings) matching HOST and PORT.
-
-DEPRECATED in favor of `auth-source-search'!
-
-USERNAME is optional and will be used as \"login\" in a search
-across the Secret Service API (see secrets.el) if the resulting
-items don't have a username.  This means that if you search for
-username \"joe\" and it matches an item but the item doesn't have
-a :user attribute, the username \"joe\" will be returned.
-
-A non nil DELETE-EXISTING means deleting any matching password
-entry in the respective sources.  This is useful only when
-CREATE-MISSING is non nil as well; the intended use case is to
-remove wrong password entries.
-
-If no matching entry is found, and CREATE-MISSING is non nil,
-the password will be retrieved interactively, and it will be
-stored in the password database which matches best (see
-`auth-sources').
-
-MODE can be \"login\" or \"password\"."
-  (auth-source-do-debug
-   "auth-source-user-or-password: DEPRECATED get %s for %s (%s) + user=%s"
-   mode host port username)
-
-  (let* ((listy (listp mode))
-         (mode (if listy mode (list mode)))
-         ;; (cname (if username
-         ;;            (format "%s %s:%s %s" mode host port username)
-         ;;          (format "%s %s:%s" mode host port)))
-         (search (list :host host :port port))
-         (search (if username (append search (list :user username)) search))
-         (search (if create-missing
-                     (append search (list :create t))
-                   search))
-         (search (if delete-existing
-                     (append search (list :delete t))
-                   search))
-         ;; (found (if (not delete-existing)
-         ;;            (gethash cname auth-source-cache)
-         ;;          (remhash cname auth-source-cache)
-         ;;          nil)))
-         (found nil))
-    (if found
-        (progn
-          (auth-source-do-debug
-           "auth-source-user-or-password: DEPRECATED cached %s=%s for %s (%s) + %s"
-           mode
-           ;; don't show the password
-           (if (and (member "password" mode) t)
-               "SECRET"
-             found)
-           host port username)
-          found)                        ; return the found data
-      ;; else, if not found, search with a max of 1
-      (let ((choice (nth 0 (apply #'auth-source-search
-                                  (append '(:max 1) search)))))
-        (when choice
-          (dolist (m mode)
-            (cond
-             ((equal "password" m)
-              (push (if (plist-get choice :secret)
-                        (funcall (plist-get choice :secret))
-                      nil) found))
-             ((equal "login" m)
-              (push (plist-get choice :user) found)))))
-        (setq found (nreverse found))
-        (setq found (if listy found (car-safe found)))))
-
-    found))
-
-(defun auth-source-user-and-password (host &optional user)
-  (let* ((auth-info (car
-                     (if user
-                         (auth-source-search
-                          :host host
-                          :user "yourusername"
-                          :max 1
-                          :require '(:user :secret)
-                          :create nil)
-                       (auth-source-search
-                        :host host
-                        :max 1
-                        :require '(:user :secret)
-                        :create nil))))
-         (user (plist-get auth-info :user))
-         (password (plist-get auth-info :secret)))
-    (when (functionp password)
-      (setq password (funcall password)))
-    (list user password auth-info)))
-
-(provide 'auth-source)
-
-;;; auth-source.el ends here
diff --git a/lisp/gnus/compface.el b/lisp/gnus/compface.el
deleted file mode 100644 (file)
index e2f607b..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-;;; compface.el --- functions for converting X-Face headers
-
-;; Copyright (C) 2002-2016 Free Software Foundation, Inc.
-
-;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
-;; Keywords: news
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;;; Code:
-
-;;;###
-(defun uncompface (face)
-  "Convert FACE to pbm.
-Requires the external programs `uncompface', and `icontopbm'.  On a
-GNU/Linux system these might be in packages with names like `compface'
-or `faces-xface' and `netpbm' or `libgr-progs', for instance."
-  (with-temp-buffer
-    (set-buffer-multibyte nil)
-    (insert face)
-    (let ((coding-system-for-read 'raw-text)
-         ;; At least "icontopbm" doesn't work with Windows because
-         ;; the line-break code is converted into CRLF by default.
-         (coding-system-for-write 'binary))
-      (and (eq 0 (apply 'call-process-region (point-min) (point-max)
-                       "uncompface"
-                       'delete '(t nil) nil))
-          (progn
-            (goto-char (point-min))
-            (insert "/* Format_version=1, Width=48, Height=48, Depth=1,\
- Valid_bits_per_item=16 */\n")
-            ;; Emacs doesn't understand un-raw pbm files.
-            (eq 0 (call-process-region (point-min) (point-max)
-                                       "icontopbm"
-                                       'delete '(t nil))))
-          (buffer-string)))))
-
-(provide 'compface)
-
-;;; compface.el ends here
diff --git a/lisp/gnus/ecomplete.el b/lisp/gnus/ecomplete.el
deleted file mode 100644 (file)
index cb50cce..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-;;; ecomplete.el --- electric completion of addresses and the like
-
-;; Copyright (C) 2006-2016 Free Software Foundation, Inc.
-
-;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
-;; Keywords: mail
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;;; Code:
-
-(eval-when-compile
-  (require 'cl))
-
-(defgroup ecomplete nil
-  "Electric completion of email addresses and the like."
-  :group 'mail)
-
-(defcustom ecomplete-database-file "~/.ecompleterc"
-  "*The name of the file to store the ecomplete data."
-  :group 'ecomplete
-  :type 'file)
-
-(defcustom ecomplete-database-file-coding-system 'iso-2022-7bit
-  "Coding system used for writing the ecomplete database file."
-  :type '(symbol :tag "Coding system")
-  :group 'ecomplete)
-
-;;; Internal variables.
-
-(defvar ecomplete-database nil)
-
-;;;###autoload
-(defun ecomplete-setup ()
-  (when (file-exists-p ecomplete-database-file)
-    (with-temp-buffer
-      (let ((coding-system-for-read ecomplete-database-file-coding-system))
-       (insert-file-contents ecomplete-database-file)
-       (setq ecomplete-database (read (current-buffer)))))))
-
-(defun ecomplete-add-item (type key text)
-  (let ((elems (assq type ecomplete-database))
-       (now (string-to-number (format "%.0f" (float-time))))
-       entry)
-    (unless elems
-      (push (setq elems (list type)) ecomplete-database))
-    (if (setq entry (assoc key (cdr elems)))
-       (setcdr entry (list (1+ (cadr entry)) now text))
-      (nconc elems (list (list key 1 now text))))))
-
-(defun ecomplete-get-item (type key)
-  (assoc key (cdr (assq type ecomplete-database))))
-
-(defun ecomplete-save ()
-  (with-temp-buffer
-    (let ((coding-system-for-write ecomplete-database-file-coding-system))
-      (insert "(")
-      (loop for (type . elems) in ecomplete-database
-           do
-           (insert (format "(%s\n" type))
-           (dolist (entry elems)
-             (prin1 entry (current-buffer))
-             (insert "\n"))
-           (insert ")\n"))
-      (insert ")")
-      (write-region (point-min) (point-max)
-                   ecomplete-database-file nil 'silent))))
-
-(defun ecomplete-get-matches (type match)
-  (let* ((elems (cdr (assq type ecomplete-database)))
-        (match (regexp-quote match))
-        (candidates
-         (sort
-          (loop for (key count time text) in elems
-                when (string-match match text)
-                collect (list count time text))
-          (lambda (l1 l2)
-            (> (car l1) (car l2))))))
-    (when (> (length candidates) 10)
-      (setcdr (nthcdr 10 candidates) nil))
-    (unless (zerop (length candidates))
-      (with-temp-buffer
-       (dolist (candidate candidates)
-         (insert (caddr candidate) "\n"))
-       (goto-char (point-min))
-       (put-text-property (point) (1+ (point)) 'ecomplete t)
-       (while (re-search-forward match nil t)
-         (put-text-property (match-beginning 0) (match-end 0)
-                            'face 'isearch))
-       (buffer-string)))))
-
-(defun ecomplete-display-matches (type word &optional choose)
-  (let* ((matches (ecomplete-get-matches type word))
-        (line 0)
-        (max-lines (when matches (- (length (split-string matches "\n")) 2)))
-        (message-log-max nil)
-        command highlight)
-    (if (not matches)
-       (progn
-         (message "No ecomplete matches")
-         nil)
-      (if (not choose)
-         (progn
-           (message "%s" matches)
-           nil)
-       (setq highlight (ecomplete-highlight-match-line matches line))
-       (let ((local-map (make-sparse-keymap))
-             selected)
-         (define-key local-map (kbd "RET")
-           (lambda () (setq selected (nth line (split-string matches "\n")))))
-         (define-key local-map (kbd "M-n")
-           (lambda () (setq line (min (1+ line) max-lines))))
-         (define-key local-map (kbd "M-p")
-           (lambda () (setq line (max (1- line) 0))))
-         (let ((overriding-local-map local-map))
-           (while (and (null selected)
-                       (setq command (read-key-sequence highlight))
-                       (lookup-key local-map command))
-             (apply (key-binding command) nil)
-             (setq highlight (ecomplete-highlight-match-line matches line))))
-         (if selected
-             (message selected)
-           (message "Abort"))
-         selected)))))
-
-(defun ecomplete-highlight-match-line (matches line)
-  (with-temp-buffer
-    (insert matches)
-    (goto-char (point-min))
-    (forward-line line)
-    (save-restriction
-      (narrow-to-region (point) (point-at-eol))
-      (while (not (eobp))
-       ;; Put the 'region face on any characters on this line that
-       ;; aren't already highlighted.
-       (unless (get-text-property (point) 'face)
-         (put-text-property (point) (1+ (point)) 'face 'highlight))
-       (forward-char 1)))
-    (buffer-string)))
-
-(provide 'ecomplete)
-
-;;; ecomplete.el ends here
diff --git a/lisp/gnus/flow-fill.el b/lisp/gnus/flow-fill.el
deleted file mode 100644 (file)
index d288142..0000000
+++ /dev/null
@@ -1,240 +0,0 @@
-;;; flow-fill.el --- interpret RFC2646 "flowed" text
-
-;; Copyright (C) 2000-2016 Free Software Foundation, Inc.
-
-;; Author: Simon Josefsson <jas@pdc.kth.se>
-;; Keywords: mail
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This implement decoding of RFC2646 formatted text, including the
-;; quoted-depth wins rules.
-
-;; Theory of operation: search for lines ending with SPC, save quote
-;; length of line, remove SPC and concatenate line with the following
-;; line if quote length of following line matches current line.
-
-;; When no further concatenations are possible, we've found a
-;; paragraph and we let `fill-region' fill the long line into several
-;; lines with the quote prefix as `fill-prefix'.
-
-;; Todo: implement basic `fill-region' (Emacs and XEmacs
-;;       implementations differ..)
-
-;;; History:
-
-;; 2000-02-17  posted on ding mailing list
-;; 2000-02-19  use `point-at-{b,e}ol' in XEmacs
-;; 2000-03-11  no compile warnings for point-at-bol stuff
-;; 2000-03-26  committed to gnus cvs
-;; 2000-10-23  don't flow "-- " lines, make "quote-depth wins" rule
-;;             work when first line is at level 0.
-;; 2002-01-12  probably incomplete encoding support
-;; 2003-12-08  started working on test harness.
-
-;;; Code:
-
-(eval-when-compile (require 'cl))
-
-(defcustom fill-flowed-display-column 'fill-column
-  "Column beyond which format=flowed lines are wrapped, when displayed.
-This can be a Lisp expression or an integer."
-  :version "22.1"
-  :group 'mime-display
-  :type '(choice (const :tag "Standard `fill-column'" fill-column)
-                (const :tag "Fit Window" (- (window-width) 5))
-                (sexp)
-                (integer)))
-
-(defcustom fill-flowed-encode-column 66
-  "Column beyond which format=flowed lines are wrapped, in outgoing messages.
-This can be a Lisp expression or an integer.
-RFC 2646 suggests 66 characters for readability."
-  :version "22.1"
-  :group 'mime-display
-  :type '(choice (const :tag "Standard fill-column" fill-column)
-                (const :tag "RFC 2646 default (66)" 66)
-                (sexp)
-                (integer)))
-
-;;;###autoload
-(defun fill-flowed-encode (&optional buffer)
-  (with-current-buffer (or buffer (current-buffer))
-    ;; No point in doing this unless hard newlines is used.
-    (when use-hard-newlines
-      (let ((start (point-min)) end)
-       ;; Go through each paragraph, filling it and adding SPC
-       ;; as the last character on each line.
-       (while (setq end (text-property-any start (point-max) 'hard 't))
-         (save-restriction
-           (narrow-to-region start end)
-           (let ((fill-column (eval fill-flowed-encode-column)))
-             (fill-flowed-fill-buffer))
-           (goto-char (point-min))
-           (while (re-search-forward "\n" nil t)
-             (replace-match " \n" t t))
-           (goto-char (setq start (1+ (point-max)))))))
-      t)))
-
-(defun fill-flowed-fill-buffer ()
-  (let ((prefix nil)
-       (prev-prefix nil)
-       (start (point-min)))
-    (goto-char (point-min))
-    (while (not (eobp))
-      (setq prefix (and (looking-at "[> ]+")
-                       (match-string 0)))
-      (if (equal prefix prev-prefix)
-         (forward-line 1)
-       (save-restriction
-         (narrow-to-region start (point))
-         (let ((fill-prefix prev-prefix))
-           (fill-region (point-min) (point-max) t 'nosqueeze 'to-eop))
-         (goto-char (point-max)))
-       (setq prev-prefix prefix
-             start (point))))
-    (save-restriction
-      (narrow-to-region start (point))
-      (let ((fill-prefix prev-prefix))
-       (fill-region (point-min) (point-max) t 'nosqueeze 'to-eop)))))
-
-;;;###autoload
-(defun fill-flowed (&optional buffer delete-space)
-  (with-current-buffer (or (current-buffer) buffer)
-    (goto-char (point-min))
-    ;; Remove space stuffing.
-    (while (re-search-forward "^\\( \\|>+ $\\)" nil t)
-      (delete-char -1)
-      (forward-line 1))
-    (goto-char (point-min))
-    (while (re-search-forward " $" nil t)
-      (when (save-excursion
-             (beginning-of-line)
-             (looking-at "^\\(>*\\)\\( ?\\)"))
-       (let ((quote (match-string 1))
-             sig)
-         (if (string= quote "")
-             (setq quote nil))
-         (when (and quote (string= (match-string 2) ""))
-           (save-excursion
-             ;; insert SP after quote for pleasant reading of quoted lines
-             (beginning-of-line)
-             (when (> (skip-chars-forward ">") 0)
-               (insert " "))))
-         ;; XXX slightly buggy handling of "-- "
-         (while (and (save-excursion
-                       (ignore-errors (backward-char 3))
-                       (setq sig (looking-at "-- "))
-                       (looking-at "[^-][^-] "))
-                     (save-excursion
-                       (unless (eobp)
-                         (forward-char 1)
-                         (looking-at (format "^\\(%s\\)\\([^>\n\r]\\)"
-                                             (or quote " ?"))))))
-           (save-excursion
-             (replace-match (if (string= (match-string 2) " ")
-                                "" "\\2")))
-           (backward-delete-char -1)
-           (when delete-space
-             (delete-char -1))
-           (end-of-line))
-         (unless sig
-           (condition-case nil
-               (let ((fill-prefix (when quote (concat quote " ")))
-                     (fill-column (eval fill-flowed-display-column))
-                     adaptive-fill-mode)
-                 (fill-region (point-at-bol)
-                              (min (1+ (point-at-eol))
-                                   (point-max))
-                              'left 'nosqueeze))
-             (error
-              (forward-line 1)
-              nil))))))))
-
-;; Test vectors.
-
-(defvar show-trailing-whitespace)
-
-(defvar fill-flowed-encode-tests
-  `(
-    ;; The syntax of each list element is:
-    ;; (INPUT . EXPECTED-OUTPUT)
-    (,(concat
-       "> Thou villainous ill-breeding spongy dizzy-eyed \n"
-       "> reeky elf-skinned pigeon-egg! \n"
-       ">> Thou artless swag-bellied milk-livered \n"
-       ">> dismal-dreaming idle-headed scut!\n"
-       ">>> Thou errant folly-fallen spleeny reeling-ripe \n"
-       ">>> unmuzzled ratsbane!\n"
-       ">>>> Henceforth, the coding style is to be strictly \n"
-       ">>>> enforced, including the use of only upper case.\n"
-       ">>>>> I've noticed a lack of adherence to the coding \n"
-       ">>>>> styles, of late.\n"
-       ">>>>>> Any complaints?")
-     .
-     ,(concat
-       "> Thou villainous ill-breeding spongy dizzy-eyed reeky elf-skinned\n"
-       "> pigeon-egg! \n"
-       ">> Thou artless swag-bellied milk-livered dismal-dreaming idle-headed\n"
-       ">> scut!\n"
-       ">>> Thou errant folly-fallen spleeny reeling-ripe unmuzzled ratsbane!\n"
-       ">>>> Henceforth, the coding style is to be strictly enforced,\n"
-       ">>>> including the use of only upper case.\n"
-       ">>>>> I've noticed a lack of adherence to the coding styles, of late.\n"
-       ">>>>>> Any complaints?\n"
-       ))
-    ;; (,(concat
-    ;;    "\n"
-    ;;    "> foo\n"
-    ;;    "> \n"
-    ;;    "> \n"
-    ;;    "> bar\n")
-    ;;  .
-    ;;  ,(concat
-    ;;    "\n"
-    ;;    "> foo bar\n"))
-    ))
-
-(defun fill-flowed-test ()
-  (interactive "")
-  (switch-to-buffer (get-buffer-create "*Format=Flowed test output*"))
-  (erase-buffer)
-  (setq show-trailing-whitespace t)
-  (dolist (test fill-flowed-encode-tests)
-    (let (start output)
-      (insert "***** BEGIN TEST INPUT *****\n")
-      (insert (car test))
-      (insert "***** END TEST INPUT *****\n\n")
-      (insert "***** BEGIN TEST OUTPUT *****\n")
-      (setq start (point))
-      (insert (car test))
-      (save-restriction
-       (narrow-to-region start (point))
-       (fill-flowed))
-      (setq output (buffer-substring start (point-max)))
-      (insert "***** END TEST OUTPUT *****\n")
-      (unless (string= output (cdr test))
-       (insert "\n***** BEGIN TEST EXPECTED OUTPUT *****\n")
-       (insert (cdr test))
-       (insert "***** END TEST EXPECTED OUTPUT *****\n"))
-      (insert "\n\n")))
-  (goto-char (point-max)))
-
-(provide 'flow-fill)
-
-;;; flow-fill.el ends here
diff --git a/lisp/gnus/gravatar.el b/lisp/gnus/gravatar.el
deleted file mode 100644 (file)
index 81503b7..0000000
+++ /dev/null
@@ -1,157 +0,0 @@
-;;; gravatar.el --- Get Gravatars
-
-;; Copyright (C) 2010-2016 Free Software Foundation, Inc.
-
-;; Author: Julien Danjou <julien@danjou.info>
-;; Keywords: news
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;;; Code:
-
-(require 'url)
-(require 'url-cache)
-(require 'image)
-
-(defgroup gravatar nil
-  "Gravatar."
-  :version "24.1"
-  :group 'comm)
-
-(defcustom gravatar-automatic-caching t
-  "Whether to cache retrieved gravatars."
-  :type 'boolean
-  :group 'gravatar)
-
-;; FIXME a time value is not the nicest format for a custom variable.
-(defcustom gravatar-cache-ttl (days-to-time 30)
-  "Time to live for gravatar cache entries."
-  :type '(repeat integer)
-  :group 'gravatar)
-
-;; FIXME Doc is tautological.  What are the options?
-(defcustom gravatar-rating "g"
-  "Default rating for gravatar."
-  :type 'string
-  :group 'gravatar)
-
-(defcustom gravatar-size 32
-  "Default size in pixels for gravatars."
-  :type 'integer
-  :group 'gravatar)
-
-(defconst gravatar-base-url
-  "http://www.gravatar.com/avatar"
-  "Base URL for getting gravatars.")
-
-(defun gravatar-hash (mail-address)
-  "Create an hash from MAIL-ADDRESS."
-  (md5 (downcase mail-address)))
-
-(defun gravatar-build-url (mail-address)
-  "Return an URL to retrieve MAIL-ADDRESS gravatar."
-  (format "%s/%s?d=404&r=%s&s=%d"
-          gravatar-base-url
-          (gravatar-hash mail-address)
-          gravatar-rating
-          gravatar-size))
-
-(defun gravatar-cache-expired (url)
-  "Check if URL is cached for more than `gravatar-cache-ttl'."
-  (cond (url-standalone-mode
-         (not (file-exists-p (url-cache-create-filename url))))
-        (t (let ((cache-time (url-is-cached url)))
-             (if cache-time
-                 (time-less-p
-                  (time-add
-                   cache-time
-                   gravatar-cache-ttl)
-                  (current-time))
-               t)))))
-
-(defun gravatar-get-data ()
-  "Get data from current buffer."
-  (save-excursion
-    (goto-char (point-min))
-    (when (re-search-forward "^HTTP/.+ 200 OK$" nil (line-end-position))
-      (when (search-forward "\n\n" nil t)
-        (buffer-substring (point) (point-max))))))
-
-(defun gravatar-data->image ()
-  "Get data of current buffer and return an image.
-If no image available, return 'error."
-  (let ((data (gravatar-get-data)))
-    (if data
-       (create-image data nil t)
-      'error)))
-
-(autoload 'help-function-arglist "help-fns")
-
-;;;###autoload
-(defun gravatar-retrieve (mail-address cb &optional cbargs)
-  "Retrieve MAIL-ADDRESS gravatar and call CB on retrieval.
-You can provide a list of argument to pass to CB in CBARGS."
-  (let ((url (gravatar-build-url mail-address)))
-    (if (gravatar-cache-expired url)
-       (let ((args (list url
-                         'gravatar-retrieved
-                         (list cb (when cbargs cbargs)))))
-         (when (> (length (if (featurep 'xemacs)
-                              (cdr (split-string (function-arglist 'url-retrieve)))
-                            (help-function-arglist 'url-retrieve)))
-                  4)
-           (setq args (nconc args (list t))))
-         (apply #'url-retrieve args))
-      (apply cb
-               (with-temp-buffer
-                 (mm-disable-multibyte)
-                 (url-cache-extract (url-cache-create-filename url))
-                 (gravatar-data->image))
-               cbargs))))
-
-;;;###autoload
-(defun gravatar-retrieve-synchronously (mail-address)
-  "Retrieve MAIL-ADDRESS gravatar and returns it."
-  (let ((url (gravatar-build-url mail-address)))
-    (if (gravatar-cache-expired url)
-        (with-current-buffer (url-retrieve-synchronously url)
-         (when gravatar-automatic-caching
-            (url-store-in-cache (current-buffer)))
-          (let ((data (gravatar-data->image)))
-            (kill-buffer (current-buffer))
-            data))
-      (with-temp-buffer
-        (mm-disable-multibyte)
-        (url-cache-extract (url-cache-create-filename url))
-        (gravatar-data->image)))))
-
-
-(defun gravatar-retrieved (status cb &optional cbargs)
-  "Callback function used by `gravatar-retrieve'."
-  ;; Store gravatar?
-  (when gravatar-automatic-caching
-    (url-store-in-cache (current-buffer)))
-  (if (plist-get status :error)
-      ;; Error happened.
-      (apply cb 'error cbargs)
-    (apply cb (gravatar-data->image) cbargs))
-  (kill-buffer (current-buffer)))
-
-(provide 'gravatar)
-
-;;; gravatar.el ends here
diff --git a/lisp/gnus/html2text.el b/lisp/gnus/html2text.el
deleted file mode 100644 (file)
index 2b1c205..0000000
+++ /dev/null
@@ -1,461 +0,0 @@
-;;; html2text.el --- a simple html to plain text converter -*- coding: utf-8 -*-
-
-;; Copyright (C) 2002-2016 Free Software Foundation, Inc.
-
-;; Author: Joakim Hove <hove@phys.ntnu.no>
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; These functions provide a simple way to wash/clean html infected
-;; mails.  Definitely do not work in all cases, but some improvement
-;; in readability is generally obtained.  Formatting is only done in
-;; the buffer, so the next time you enter the article it will be
-;; "re-htmlized".
-;;
-;; The main function is `html2text'.
-
-;;; Code:
-
-;;
-;; <Global variables>
-;;
-
-(eval-when-compile
-  (require 'cl))
-
-(defvar html2text-format-single-element-list '(("hr" . html2text-clean-hr)))
-
-(defvar html2text-replace-list
-  '(("&acute;" . "`")
-    ("&amp;" . "&")
-    ("&apos;" . "'")
-    ("&brvbar;" . "|")
-    ("&cent;" . "c")
-    ("&circ;" . "^")
-    ("&copy;" . "(C)")
-    ("&curren;" . "(#)")
-    ("&deg;" . "degree")
-    ("&divide;" . "/")
-    ("&euro;" . "e")
-    ("&frac12;" . "1/2")
-    ("&gt;" . ">")
-    ("&iquest;" . "?")
-    ("&laquo;" . "<<")
-    ("&ldquo" . "\"")
-    ("&lsaquo;" . "(")
-    ("&lsquo;" . "`")
-    ("&lt;" . "<")
-    ("&mdash;" . "--")
-    ("&nbsp;" . " ")
-    ("&ndash;" . "-")
-    ("&permil;" . "%%")
-    ("&plusmn;" . "+-")
-    ("&pound;" . "£")
-    ("&quot;" . "\"")
-    ("&raquo;" . ">>")
-    ("&rdquo" . "\"")
-    ("&reg;" . "(R)")
-    ("&rsaquo;" . ")")
-    ("&rsquo;" . "'")
-    ("&sect;" . "§")
-    ("&sup1;" . "^1")
-    ("&sup2;" . "^2")
-    ("&sup3;" . "^3")
-    ("&tilde;" . "~"))
-  "The map of entity to text.
-
-This is an alist were each element is a dotted pair consisting of an
-old string, and a replacement string.  This replacement is done by the
-function `html2text-substitute' which basically performs a
-`replace-string' operation for every element in the list.  This is
-completely verbatim - without any use of REGEXP.")
-
-(defvar html2text-remove-tag-list
-  '("html" "body" "p" "img" "dir" "head" "div" "br" "font" "title" "meta")
-  "A list of removable tags.
-
-This is a list of tags which should be removed, without any
-formatting.  Note that tags in the list are presented *without*
-any \"<\" or \">\".  All occurrences of a tag appearing in this
-list are removed, irrespective of whether it is a closing or
-opening tag, or if the tag has additional attributes.  The
-deletion is done by the function `html2text-remove-tags'.
-
-For instance the text:
-
-\"Here comes something <font size\"+3\" face=\"Helvetica\"> big </font>.\"
-
-will be reduced to:
-
-\"Here comes something big.\"
-
-If this list contains the element \"font\".")
-
-(defvar html2text-format-tag-list
-  '(("b"         . html2text-clean-bold)
-    ("strong"     . html2text-clean-bold)
-    ("u"         . html2text-clean-underline)
-    ("i"         . html2text-clean-italic)
-    ("em"         . html2text-clean-italic)
-    ("blockquote" . html2text-clean-blockquote)
-    ("a"          . html2text-clean-anchor)
-    ("ul"         . html2text-clean-ul)
-    ("ol"         . html2text-clean-ol)
-    ("dl"         . html2text-clean-dl)
-    ("center"     . html2text-clean-center))
-  "An alist of tags and processing functions.
-
-This is an alist where each dotted pair consists of a tag, and then
-the name of a function to be called when this tag is found.  The
-function is called with the arguments p1, p2, p3 and p4. These are
-demonstrated below:
-
-\"<b> This is bold text </b>\"
- ^   ^                 ^    ^
- |   |                 |    |
-p1  p2                p3   p4
-
-Then the called function will typically format the text somewhat and
-remove the tags.")
-
-(defvar html2text-remove-tag-list2  '("li" "dt" "dd" "meta")
-  "Another list of removable tags.
-
-This is a list of tags which are removed similarly to the list
-`html2text-remove-tag-list' - but these tags are retained for the
-formatting, and then moved afterward.")
-
-;;
-;; </Global variables>
-;;
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;;
-;; <Utility functions>
-;;
-
-
-(defun html2text-replace-string (from-string to-string min max)
-  "Replace FROM-STRING with TO-STRING in region from MIN to MAX."
-  (goto-char min)
-  (let ((delta (- (string-width to-string) (string-width from-string)))
-       (change 0))
-    (while (search-forward from-string max t)
-      (replace-match to-string)
-      (setq change (+ change delta)))
-    change))
-
-;;
-;; </Utility functions>
-;;
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;;
-;; <Functions related to attributes> i.e. <font size=+3>
-;;
-
-(defun html2text-attr-value (list attribute)
-  "Get value of ATTRIBUTE from LIST."
-  (nth 1 (assoc attribute list)))
-
-(defun html2text-get-attr (p1 p2)
-  (goto-char p1)
-  (re-search-forward "\\s-+" p2 t)
-  (let (attr-list)
-    (while (re-search-forward "[-a-z0-9._]+" p2 t)
-      (setq attr-list
-           (cons
-            (list (match-string 0)
-                  (when (looking-at "\\s-*=")
-                    (goto-char (match-end 0))
-                    (skip-chars-forward "[:space:]")
-                    (when (or (looking-at "\"[^\"]*\"\\|'[^']*'")
-                              (looking-at "[-a-z0-9._:]+"))
-                      (goto-char (match-end 0))
-                      (match-string 0))))
-            attr-list)))
-    attr-list))
-
-;;
-;; </Functions related to attributes>
-;;
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;;
-;; <Functions to be called to format a tag-pair>
-;;
-(defun html2text-clean-list-items (p1 p2 list-type)
-  (goto-char p1)
-  (let ((item-nr 0)
-       (items   0))
-    (while (search-forward "<li>" p2 t)
-      (setq items (1+ items)))
-    (goto-char p1)
-    (while (< item-nr items)
-      (setq item-nr (1+ item-nr))
-      (search-forward "<li>" (point-max) t)
-      (cond
-       ((string= list-type "ul") (insert " o "))
-       ((string= list-type "ol") (insert (format " %s: " item-nr)))
-       (t (insert " x "))))))
-
-(defun html2text-clean-dtdd (p1 p2)
-  (goto-char p1)
-  (let ((items   0)
-       (item-nr 0))
-    (while (search-forward "<dt>" p2 t)
-      (setq items (1+ items)))
-    (goto-char p1)
-    (while (< item-nr items)
-      (setq item-nr (1+ item-nr))
-      (re-search-forward "<dt>\\([ ]*\\)" (point-max) t)
-      (when (match-string 1)
-       (delete-region (point) (- (point) (string-width (match-string 1)))))
-      (let ((def-p1 (point))
-           (def-p2 0))
-       (re-search-forward "\\([ ]*\\)\\(</dt>\\|<dd>\\)" (point-max) t)
-       (if (match-string 1)
-           (progn
-             (let* ((mw1 (string-width (match-string 1)))
-                    (mw2 (string-width (match-string 2)))
-                    (mw  (+ mw1 mw2)))
-               (goto-char (- (point) mw))
-               (delete-region (point) (+ (point) mw1))
-               (setq def-p2 (point))))
-         (setq def-p2 (- (point) (string-width (match-string 2)))))
-       (put-text-property def-p1 def-p2 'face 'bold)))))
-
-(defun html2text-delete-tags (p1 p2 p3 p4)
-  (delete-region p1 p2)
-  (delete-region (- p3 (- p2 p1)) (- p4 (- p2 p1))))
-
-(defun html2text-delete-single-tag (p1 p2)
-  (delete-region p1 p2))
-
-(defun html2text-clean-hr (p1 p2)
-  (html2text-delete-single-tag p1 p2)
-  (goto-char p1)
-  (newline 1)
-  (insert (make-string fill-column ?-)))
-
-(defun html2text-clean-ul (p1 p2 p3 p4)
-  (html2text-delete-tags p1 p2 p3 p4)
-  (html2text-clean-list-items p1 (- p3 (- p1 p2)) "ul"))
-
-(defun html2text-clean-ol (p1 p2 p3 p4)
-  (html2text-delete-tags p1 p2 p3 p4)
-  (html2text-clean-list-items p1 (- p3 (- p1 p2)) "ol"))
-
-(defun html2text-clean-dl (p1 p2 p3 p4)
-  (html2text-delete-tags p1 p2 p3 p4)
-  (html2text-clean-dtdd p1 (- p3 (- p1 p2))))
-
-(defun html2text-clean-center (p1 p2 p3 p4)
-  (html2text-delete-tags p1 p2 p3 p4)
-  (center-region p1 (- p3 (- p2 p1))))
-
-(defun html2text-clean-bold (p1 p2 p3 p4)
-  (put-text-property p2 p3 'face 'bold)
-  (html2text-delete-tags p1 p2 p3 p4))
-
-(defun html2text-clean-title (p1 p2 p3 p4)
-  (put-text-property p2 p3 'face 'bold)
-  (html2text-delete-tags p1 p2 p3 p4))
-
-(defun html2text-clean-underline (p1 p2 p3 p4)
-  (put-text-property p2 p3 'face 'underline)
-  (html2text-delete-tags p1 p2 p3 p4))
-
-(defun html2text-clean-italic (p1 p2 p3 p4)
-  (put-text-property p2 p3 'face 'italic)
-  (html2text-delete-tags p1 p2 p3 p4))
-
-(defun html2text-clean-font (p1 p2 p3 p4)
-  (html2text-delete-tags p1 p2 p3 p4))
-
-(defun html2text-clean-blockquote (p1 p2 p3 p4)
-  (html2text-delete-tags p1 p2 p3 p4))
-
-(defun html2text-clean-anchor (p1 p2 p3 p4)
-  ;; If someone can explain how to make the URL clickable I will surely
-  ;; improve upon this.
-  ;; Maybe `goto-addr.el' can be used here.
-  (let* ((attr-list (html2text-get-attr p1 p2))
-        (href (html2text-attr-value attr-list "href")))
-    (delete-region p1 p4)
-    (when href
-      (goto-char p1)
-      (insert (if (string-match "\\`['\"].*['\"]\\'" href)
-                 (substring href 1 -1) href))
-      (put-text-property p1 (point) 'face 'bold))))
-
-;;
-;; </Functions to be called to format a tag-pair>
-;;
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;;
-;; <Functions to be called to fix up paragraphs>
-;;
-
-(defun html2text-fix-paragraph (p1 p2)
-  (goto-char p1)
-  (let ((refill-start)
-       (refill-stop))
-    (when (re-search-forward "<br>$" p2 t)
-      (goto-char p1)
-      (when (re-search-forward ".+[^<][^b][^r][^>]$" p2 t)
-       (beginning-of-line)
-       (setq refill-start (point))
-       (goto-char p2)
-       (re-search-backward ".+[^<][^b][^r][^>]$" refill-start t)
-       (forward-line 1)
-       (end-of-line)
-       ;; refill-stop should ideally be adjusted to
-       ;; accommodate the "<br>" strings which are removed
-       ;; between refill-start and refill-stop.  Can simply
-       ;; be returned from my-replace-string
-       (setq refill-stop (+ (point)
-                            (html2text-replace-string
-                             "<br>" ""
-                             refill-start (point))))
-       ;; (message "Point = %s  refill-stop = %s" (point) refill-stop)
-       ;; (sleep-for 4)
-       (fill-region refill-start refill-stop))))
-  (html2text-replace-string "<br>" "" p1 p2))
-
-;;
-;; This one is interactive ...
-;;
-(defun html2text-fix-paragraphs ()
-  "This _tries_ to fix up the paragraphs - this is done in quite a ad-hook
-fashion, quite close to pure guess-work. It does work in some cases though."
-  (interactive)
-  (goto-char (point-min))
-  (while (re-search-forward "^<br>$" nil t)
-    (delete-region (match-beginning 0) (match-end 0)))
-  ;; Removing lonely <br> on a single line, if they are left intact we
-  ;; don't have any paragraphs at all.
-  (goto-char (point-min))
-  (while (not (eobp))
-    (let ((p1 (point)))
-      (forward-paragraph 1)
-      ;;(message "Kaller fix med p1=%s  p2=%s " p1 (1- (point))) (sleep-for 5)
-      (html2text-fix-paragraph p1 (1- (point)))
-      (goto-char p1)
-      (when (not (eobp))
-       (forward-paragraph 1)))))
-
-;;
-;; </Functions to be called to fix up paragraphs>
-;;
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-;;
-;; <Interactive functions>
-;;
-
-(defun html2text-remove-tags (tag-list)
-  "Removes the tags listed in the list `html2text-remove-tag-list'.
-See the documentation for that variable."
-  (interactive)
-  (dolist (tag tag-list)
-    (goto-char (point-min))
-    (while (re-search-forward (format "\\(</?%s[^>]*>\\)" tag) (point-max) t)
-      (delete-region (match-beginning 0) (match-end 0)))))
-
-(defun html2text-format-tags ()
-  "See the variable `html2text-format-tag-list' for documentation."
-  (interactive)
-  (dolist (tag-and-function html2text-format-tag-list)
-    (let ((tag      (car tag-and-function))
-         (function (cdr tag-and-function)))
-      (goto-char (point-min))
-      (while (re-search-forward (format "\\(<%s\\( [^>]*\\)?>\\)" tag)
-                               (point-max) t)
-       (let ((p1)
-             (p2 (point))
-             (p3) (p4))
-         (search-backward "<" (point-min) t)
-         (setq p1 (point))
-         (unless (search-forward (format "</%s>" tag) (point-max) t)
-           (goto-char p2)
-           (insert (format "</%s>" tag)))
-         (setq p4 (point))
-         (search-backward "</" (point-min) t)
-         (setq p3 (point))
-         (funcall function p1 p2 p3 p4)
-         (goto-char p1))))))
-
-(defun html2text-substitute ()
-  "See the variable `html2text-replace-list' for documentation."
-  (interactive)
-  (dolist (e html2text-replace-list)
-    (goto-char (point-min))
-    (let ((old-string (car e))
-         (new-string (cdr e)))
-      (html2text-replace-string old-string new-string (point-min) (point-max)))))
-
-(defun html2text-format-single-elements ()
-  (interactive)
-  (dolist (tag-and-function html2text-format-single-element-list)
-    (let ((tag      (car tag-and-function))
-         (function (cdr tag-and-function)))
-      (goto-char (point-min))
-      (while (re-search-forward (format "\\(<%s\\( [^>]*\\)?>\\)" tag)
-                               (point-max) t)
-       (let ((p1)
-             (p2 (point)))
-         (search-backward "<" (point-min) t)
-         (setq p1 (point))
-         (funcall function p1 p2))))))
-
-;;
-;; Main function
-;;
-
-;;;###autoload
-(defun html2text ()
-  "Convert HTML to plain text in the current buffer."
-  (interactive)
-  (save-excursion
-    (let ((case-fold-search t)
-         (buffer-read-only))
-      (html2text-remove-tags html2text-remove-tag-list)
-      (html2text-format-tags)
-      (html2text-remove-tags html2text-remove-tag-list2)
-      (html2text-substitute)
-      (html2text-format-single-elements)
-      (html2text-fix-paragraphs))))
-
-;;
-;; </Interactive functions>
-;;
-(provide 'html2text)
-
-;;; html2text.el ends here
diff --git a/lisp/gnus/ietf-drums.el b/lisp/gnus/ietf-drums.el
deleted file mode 100644 (file)
index 03349d1..0000000
+++ /dev/null
@@ -1,291 +0,0 @@
-;;; ietf-drums.el --- Functions for parsing RFC822bis headers
-
-;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
-
-;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; DRUMS is an IETF Working Group that works (or worked) on the
-;; successor to RFC822, "Standard For The Format Of Arpa Internet Text
-;; Messages".  This library is based on
-;; draft-ietf-drums-msg-fmt-05.txt, released on 1998-08-05.
-
-;; Pending a real regression self test suite, Simon Josefsson added
-;; various self test expressions snipped from bug reports, and their
-;; expected value, below.  I you believe it could be useful, please
-;; add your own test cases, or write a real self test suite, or just
-;; remove this.
-
-;; <m3oekvfd50.fsf@whitebox.m5r.de>
-;; (ietf-drums-parse-address "'foo' <foo@example.com>")
-;; => ("foo@example.com" . "'foo'")
-
-;;; Code:
-
-(eval-when-compile (require 'cl))
-
-(defvar ietf-drums-no-ws-ctl-token "\001-\010\013\014\016-\037\177"
-  "US-ASCII control characters excluding CR, LF and white space.")
-(defvar ietf-drums-text-token "\001-\011\013\014\016-\177"
-  "US-ASCII characters excluding CR and LF.")
-(defvar ietf-drums-specials-token "()<>[]:;@\\,.\""
-  "Special characters.")
-(defvar ietf-drums-quote-token "\\"
-  "Quote character.")
-(defvar ietf-drums-wsp-token " \t"
-  "White space.")
-(defvar ietf-drums-fws-regexp
-  (concat "[" ietf-drums-wsp-token "]*\n[" ietf-drums-wsp-token "]+")
-  "Folding white space.")
-(defvar ietf-drums-atext-token "-^a-zA-Z0-9!#$%&'*+/=?_`{|}~"
-  "Textual token.")
-(defvar ietf-drums-dot-atext-token "-^a-zA-Z0-9!#$%&'*+/=?_`{|}~."
-  "Textual token including full stop.")
-(defvar ietf-drums-qtext-token
-  (concat ietf-drums-no-ws-ctl-token "\041\043-\133\135-\177")
-  "Non-white-space control characters, plus the rest of ASCII excluding
-backslash and doublequote.")
-(defvar ietf-drums-tspecials "][()<>@,;:\\\"/?="
-  "Tspecials.")
-
-(defvar ietf-drums-syntax-table
-  (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table)))
-    (modify-syntax-entry ?\\ "/" table)
-    (modify-syntax-entry ?< "(" table)
-    (modify-syntax-entry ?> ")" table)
-    (modify-syntax-entry ?@ "w" table)
-    (modify-syntax-entry ?/ "w" table)
-    (modify-syntax-entry ?* "_" table)
-    (modify-syntax-entry ?\; "_" table)
-    (modify-syntax-entry ?\' "_" table)
-    table))
-
-(defun ietf-drums-token-to-list (token)
-  "Translate TOKEN into a list of characters."
-  (let ((i 0)
-       b e c out range)
-    (while (< i (length token))
-      (setq c (aref token i))
-      (incf i)
-      (cond
-       ((eq c ?-)
-       (if b
-           (setq range t)
-         (push c out)))
-       (range
-       (while (<= b c)
-         (push (make-char 'ascii b) out)
-         (incf b))
-       (setq range nil))
-       ((= i (length token))
-       (push (make-char 'ascii c) out))
-       (t
-       (when b
-         (push (make-char 'ascii b) out))
-       (setq b c))))
-    (nreverse out)))
-
-(defsubst ietf-drums-init (string)
-  (set-syntax-table ietf-drums-syntax-table)
-  (insert string)
-  (ietf-drums-unfold-fws)
-  (goto-char (point-min)))
-
-(defun ietf-drums-remove-comments (string)
-  "Remove comments from STRING."
-  (with-temp-buffer
-    (let (c)
-      (ietf-drums-init string)
-      (while (not (eobp))
-       (setq c (char-after))
-       (cond
-        ((eq c ?\")
-         (condition-case err
-             (forward-sexp 1)
-           (error (goto-char (point-max)))))
-        ((eq c ?\()
-         (delete-region
-              (point)
-              (condition-case nil
-                  (with-syntax-table (copy-syntax-table ietf-drums-syntax-table)
-                    (modify-syntax-entry ?\" "w")
-                    (forward-sexp 1)
-                    (point))
-                (error (point-max)))))
-        (t
-         (forward-char 1))))
-      (buffer-string))))
-
-(defun ietf-drums-remove-whitespace (string)
-  "Remove whitespace from STRING."
-  (with-temp-buffer
-    (ietf-drums-init string)
-    (let (c)
-      (while (not (eobp))
-       (setq c (char-after))
-       (cond
-        ((eq c ?\")
-         (forward-sexp 1))
-        ((eq c ?\()
-         (forward-sexp 1))
-        ((memq c '(?\  ?\t ?\n))
-         (delete-char 1))
-        (t
-         (forward-char 1))))
-      (buffer-string))))
-
-(defun ietf-drums-get-comment (string)
-  "Return the first comment in STRING."
-  (with-temp-buffer
-    (ietf-drums-init string)
-    (let (result c)
-      (while (not (eobp))
-       (setq c (char-after))
-       (cond
-        ((eq c ?\")
-         (forward-sexp 1))
-        ((eq c ?\()
-         (setq result
-               (buffer-substring
-                (1+ (point))
-                (progn (forward-sexp 1) (1- (point))))))
-        (t
-         (forward-char 1))))
-      result)))
-
-(defun ietf-drums-strip (string)
-  "Remove comments and whitespace from STRING."
-  (ietf-drums-remove-whitespace (ietf-drums-remove-comments string)))
-
-(defun ietf-drums-parse-address (string)
-  "Parse STRING and return a MAILBOX / DISPLAY-NAME pair."
-  (with-temp-buffer
-    (let (display-name mailbox c display-string)
-      (ietf-drums-init string)
-      (while (not (eobp))
-       (setq c (char-after))
-       (cond
-        ((or (eq c ? )
-             (eq c ?\t))
-         (forward-char 1))
-        ((eq c ?\()
-         (forward-sexp 1))
-        ((eq c ?\")
-         (push (buffer-substring
-                (1+ (point)) (progn (forward-sexp 1) (1- (point))))
-               display-name))
-        ((looking-at (concat "[" ietf-drums-atext-token "@" "]"))
-         (push (buffer-substring (point) (progn (forward-sexp 1) (point)))
-               display-name))
-        ((eq c ?<)
-         (setq mailbox
-               (ietf-drums-remove-whitespace
-                (ietf-drums-remove-comments
-                 (buffer-substring
-                  (1+ (point))
-                  (progn (forward-sexp 1) (1- (point))))))))
-        (t
-         (forward-char 1))))
-      ;; If we found no display-name, then we look for comments.
-      (if display-name
-         (setq display-string
-               (mapconcat 'identity (reverse display-name) " "))
-       (setq display-string (ietf-drums-get-comment string)))
-      (if (not mailbox)
-         (when (and display-string
-                    (string-match "@" display-string))
-           (cons
-            (mapconcat 'identity (nreverse display-name) "")
-            (ietf-drums-get-comment string)))
-       (cons mailbox display-string)))))
-
-(defun ietf-drums-parse-addresses (string &optional rawp)
-  "Parse STRING and return a list of MAILBOX / DISPLAY-NAME pairs.
-If RAWP, don't actually parse the addresses, but instead return
-a list of address strings."
-  (if (null string)
-      nil
-    (with-temp-buffer
-      (ietf-drums-init string)
-      (let ((beg (point))
-           pairs c address)
-       (while (not (eobp))
-         (setq c (char-after))
-         (cond
-          ((memq c '(?\" ?< ?\())
-           (condition-case nil
-               (forward-sexp 1)
-             (error
-              (skip-chars-forward "^,"))))
-          ((eq c ?,)
-           (setq address
-                 (if rawp
-                     (buffer-substring beg (point))
-                   (condition-case nil
-                       (ietf-drums-parse-address
-                        (buffer-substring beg (point)))
-                     (error nil))))
-           (if address (push address pairs))
-           (forward-char 1)
-           (setq beg (point)))
-          (t
-           (forward-char 1))))
-       (setq address
-             (if rawp
-                 (buffer-substring beg (point))
-               (condition-case nil
-                   (ietf-drums-parse-address
-                    (buffer-substring beg (point)))
-                 (error nil))))
-       (if address (push address pairs))
-       (nreverse pairs)))))
-
-(defun ietf-drums-unfold-fws ()
-  "Unfold folding white space in the current buffer."
-  (goto-char (point-min))
-  (while (re-search-forward ietf-drums-fws-regexp nil t)
-    (replace-match " " t t))
-  (goto-char (point-min)))
-
-(defun ietf-drums-parse-date (string)
-  "Return an Emacs time spec from STRING."
-  (apply 'encode-time (parse-time-string string)))
-
-(defun ietf-drums-narrow-to-header ()
-  "Narrow to the header section in the current buffer."
-  (narrow-to-region
-   (goto-char (point-min))
-   (if (re-search-forward "^\r?$" nil 1)
-       (match-beginning 0)
-     (point-max)))
-  (goto-char (point-min)))
-
-(defun ietf-drums-quote-string (string)
-  "Quote string if it needs quoting to be displayed in a header."
-  (if (string-match (concat "[^" ietf-drums-atext-token "]") string)
-      (concat "\"" string "\"")
-    string))
-
-(defun ietf-drums-make-address (name address)
-  (if name
-      (concat (ietf-drums-quote-string name) " <" address ">")
-    address))
-
-(provide 'ietf-drums)
-
-;;; ietf-drums.el ends here
diff --git a/lisp/gnus/mail-parse.el b/lisp/gnus/mail-parse.el
deleted file mode 100644 (file)
index 4fc7e46..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-;;; mail-parse.el --- Interface functions for parsing mail
-
-;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
-
-;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This file contains wrapper functions for a wide range of mail
-;; parsing functions.  The idea is that there are low-level libraries
-;; that implement according to various specs (RFC2231, DRUMS, USEFOR),
-;; but that programmers that want to parse some header (say,
-;; Content-Type) will want to use the latest spec.
-;;
-;; So while each low-level library (rfc2231.el, for instance) decodes
-;; faithfully according to that (proposed) standard, this library is
-;; the interface library.  If some later RFC supersedes RFC2231, one
-;; would just have to write a new low-level library, adjust the
-;; aliases in this library, and the users and programmers won't notice
-;; any changes.
-
-;;; Code:
-
-(require 'mail-prsvr)
-(require 'ietf-drums)
-(require 'rfc2231)
-(require 'rfc2047)
-(require 'rfc2045)
-
-(defalias 'mail-header-parse-content-type 'rfc2231-parse-qp-string)
-(defalias 'mail-header-parse-content-disposition 'rfc2231-parse-qp-string)
-(defalias 'mail-content-type-get 'rfc2231-get-value)
-(defalias 'mail-header-encode-parameter 'rfc2047-encode-parameter)
-
-(defalias 'mail-header-remove-comments 'ietf-drums-remove-comments)
-(defalias 'mail-header-remove-whitespace 'ietf-drums-remove-whitespace)
-(defalias 'mail-header-strip 'ietf-drums-strip)
-(defalias 'mail-header-get-comment 'ietf-drums-get-comment)
-(defalias 'mail-header-parse-address 'ietf-drums-parse-address)
-(defalias 'mail-header-parse-addresses 'ietf-drums-parse-addresses)
-(defalias 'mail-header-parse-date 'ietf-drums-parse-date)
-(defalias 'mail-narrow-to-head 'ietf-drums-narrow-to-header)
-(defalias 'mail-quote-string 'ietf-drums-quote-string)
-(defalias 'mail-header-make-address 'ietf-drums-make-address)
-
-(defalias 'mail-header-fold-field 'rfc2047-fold-field)
-(defalias 'mail-header-unfold-field 'rfc2047-unfold-field)
-(defalias 'mail-header-narrow-to-field 'rfc2047-narrow-to-field)
-(defalias 'mail-header-field-value 'rfc2047-field-value)
-
-(defalias 'mail-encode-encoded-word-region 'rfc2047-encode-region)
-(defalias 'mail-encode-encoded-word-buffer 'rfc2047-encode-message-header)
-(defalias 'mail-encode-encoded-word-string 'rfc2047-encode-string)
-(defalias 'mail-decode-encoded-word-region 'rfc2047-decode-region)
-(defalias 'mail-decode-encoded-word-string 'rfc2047-decode-string)
-(defalias 'mail-decode-encoded-address-region 'rfc2047-decode-address-region)
-(defalias 'mail-decode-encoded-address-string 'rfc2047-decode-address-string)
-
-(provide 'mail-parse)
-
-;;; mail-parse.el ends here
diff --git a/lisp/gnus/mail-prsvr.el b/lisp/gnus/mail-prsvr.el
deleted file mode 100644 (file)
index 789c002..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-;;; mail-prsvr.el --- Interface variables for parsing mail
-
-;; Copyright (C) 1999-2016 Free Software Foundation, Inc.
-
-;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;;; Code:
-
-(defvar mail-parse-charset nil
-  "Default charset used by low-level libraries.
-This variable should never be set.  Instead, it should be bound by
-functions that wish to call mail-parse functions and let them know
-what the desired charset is to be.")
-
-(defvar mail-parse-mule-charset nil
-  "Default MULE charset used by low-level libraries.
-This variable should never be set.")
-
-(defvar mail-parse-ignored-charsets nil
-  "Ignored charsets used by low-level libraries.
-This variable should never be set.  Instead, it should be bound by
-functions that wish to call mail-parse functions and let them know
-what the desired charsets is to be ignored.")
-
-(provide 'mail-prsvr)
-
-;;; mail-prsvr.el ends here
diff --git a/lisp/gnus/mailcap.el b/lisp/gnus/mailcap.el
deleted file mode 100644 (file)
index 609a8f4..0000000
+++ /dev/null
@@ -1,1054 +0,0 @@
-;;; mailcap.el --- MIME media types configuration
-
-;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
-
-;; Author: William M. Perry <wmperry@aventail.com>
-;;     Lars Magne Ingebrigtsen <larsi@gnus.org>
-;; Keywords: news, mail, multimedia
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; Provides configuration of MIME media types from directly from Lisp
-;; and via the usual mailcap mechanism (RFC 1524).  Deals with
-;; mime.types similarly.
-
-;;; Code:
-
-(eval-when-compile (require 'cl))
-(autoload 'mail-header-parse-content-type "mail-parse")
-
-(defgroup mailcap nil
-  "Definition of viewers for MIME types."
-  :version "21.1"
-  :group 'mime)
-
-(defvar mailcap-parse-args-syntax-table
-  (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table)))
-    (modify-syntax-entry ?' "\"" table)
-    (modify-syntax-entry ?` "\"" table)
-    (modify-syntax-entry ?{ "(" table)
-    (modify-syntax-entry ?} ")" table)
-    table)
-  "A syntax table for parsing SGML attributes.")
-
-(defvar mailcap-print-command
-  (mapconcat 'identity
-            (cons (if (boundp 'lpr-command)
-                      lpr-command
-                    "lpr")
-                  (when (boundp 'lpr-switches)
-                    (if (stringp lpr-switches)
-                        (list lpr-switches)
-                      lpr-switches)))
-            " ")
-  "Shell command (including switches) used to print PostScript files.")
-
-;; Postpone using defcustom for this as it's so big and we essentially
-;; have to have two copies of the data around then.  Perhaps just
-;; customize the Lisp viewers and rely on the normal configuration
-;; files for the rest?  -- fx
-(defvar mailcap-mime-data
-  `(("application"
-     ("vnd\\.ms-excel"
-      (viewer . "gnumeric %s")
-      (test   . (getenv "DISPLAY"))
-      (type . "application/vnd.ms-excel"))
-     ("x-x509-ca-cert"
-      (viewer . ssl-view-site-cert)
-      (type . "application/x-x509-ca-cert"))
-     ("x-x509-user-cert"
-      (viewer . ssl-view-user-cert)
-      (type . "application/x-x509-user-cert"))
-     ("octet-stream"
-      (viewer . mailcap-save-binary-file)
-      (non-viewer . t)
-      (type . "application/octet-stream"))
-     ("dvi"
-      (viewer . "xdvi -safer %s")
-      (test   . (eq window-system 'x))
-      ("needsx11")
-      (type   . "application/dvi")
-      ("print" . "dvips -qRP %s"))
-     ("dvi"
-      (viewer . "dvitty %s")
-      (test   . (not (getenv "DISPLAY")))
-      (type   . "application/dvi")
-      ("print" . "dvips -qRP %s"))
-     ("emacs-lisp"
-      (viewer . mailcap-maybe-eval)
-      (type   . "application/emacs-lisp"))
-     ("x-emacs-lisp"
-      (viewer . mailcap-maybe-eval)
-      (type   . "application/x-emacs-lisp"))
-     ("x-tar"
-      (viewer . mailcap-save-binary-file)
-      (non-viewer . t)
-      (type   . "application/x-tar"))
-     ("x-latex"
-      (viewer . tex-mode)
-      (type   . "application/x-latex"))
-     ("x-tex"
-      (viewer . tex-mode)
-      (type   . "application/x-tex"))
-     ("latex"
-      (viewer . tex-mode)
-      (type   . "application/latex"))
-     ("tex"
-      (viewer . tex-mode)
-      (type   . "application/tex"))
-     ("texinfo"
-      (viewer . texinfo-mode)
-      (type   . "application/tex"))
-     ("zip"
-      (viewer . mailcap-save-binary-file)
-      (non-viewer . t)
-      (type   . "application/zip")
-      ("copiousoutput"))
-     ("pdf"
-      (viewer . pdf-view-mode)
-      (type . "application/pdf")
-      (test . (eq window-system 'x)))
-     ("pdf"
-      (viewer . doc-view-mode)
-      (type . "application/pdf")
-      (test . (eq window-system 'x)))
-     ("pdf"
-      (viewer . "gv -safer %s")
-      (type . "application/pdf")
-      (test . window-system)
-      ("print" . ,(concat "pdf2ps %s - | " mailcap-print-command)))
-     ("pdf"
-      (viewer . "gpdf %s")
-      (type . "application/pdf")
-      ("print" . ,(concat "pdftops %s - | " mailcap-print-command))
-      (test . (eq window-system 'x)))
-     ("pdf"
-      (viewer . "xpdf %s")
-      (type . "application/pdf")
-      ("print" . ,(concat "pdftops %s - | " mailcap-print-command))
-      (test . (eq window-system 'x)))
-     ("pdf"
-      (viewer . ,(concat "pdftotext %s -"))
-      (type   . "application/pdf")
-      ("print" . ,(concat "pdftops %s - | " mailcap-print-command))
-      ("copiousoutput"))
-     ("postscript"
-      (viewer . "gv -safer %s")
-      (type . "application/postscript")
-      (test   . window-system)
-      ("print" . ,(concat mailcap-print-command " %s"))
-      ("needsx11"))
-     ("postscript"
-      (viewer . "ghostview -dSAFER %s")
-      (type . "application/postscript")
-      (test   . (eq window-system 'x))
-      ("print" . ,(concat mailcap-print-command " %s"))
-      ("needsx11"))
-     ("postscript"
-      (viewer . "ps2ascii %s")
-      (type . "application/postscript")
-      (test . (not (getenv "DISPLAY")))
-      ("print" . ,(concat mailcap-print-command " %s"))
-      ("copiousoutput"))
-     ("sieve"
-      (viewer . sieve-mode)
-      (type   . "application/sieve"))
-     ("pgp-keys"
-      (viewer . "gpg --import --interactive --verbose")
-      (type   . "application/pgp-keys")
-      ("needsterminal")))
-    ("audio"
-     ("x-mpeg"
-      (viewer . "maplay %s")
-      (type   . "audio/x-mpeg"))
-     (".*"
-      (viewer . "showaudio")
-      (type   . "audio/*")))
-    ("message"
-     ("rfc-*822"
-      (viewer . mm-view-message)
-      (test   . (and (featurep 'gnus)
-                    (gnus-alive-p)))
-      (type   . "message/rfc822"))
-     ("rfc-*822"
-      (viewer . vm-mode)
-      (type   . "message/rfc822"))
-     ("rfc-*822"
-      (viewer . view-mode)
-      (type   . "message/rfc822")))
-    ("image"
-     ("x-xwd"
-      (viewer  . "xwud -in %s")
-      (type    . "image/x-xwd")
-      ("compose" . "xwd -frame > %s")
-      (test    . (eq window-system 'x))
-      ("needsx11"))
-     ("x11-dump"
-      (viewer . "xwud -in %s")
-      (type . "image/x-xwd")
-      ("compose" . "xwd -frame > %s")
-      (test   . (eq window-system 'x))
-      ("needsx11"))
-     ("windowdump"
-      (viewer . "xwud -in %s")
-      (type . "image/x-xwd")
-      ("compose" . "xwd -frame > %s")
-      (test   . (eq window-system 'x))
-      ("needsx11"))
-     (".*"
-      (viewer . "display %s")
-      (type . "image/*")
-      (test   . (eq window-system 'x))
-      ("needsx11"))
-     (".*"
-      (viewer . "ee %s")
-      (type . "image/*")
-      (test   . (eq window-system 'x))
-      ("needsx11")))
-    ("text"
-     ("plain"
-      (viewer  . view-mode)
-      (type    . "text/plain"))
-     ("plain"
-      (viewer  . fundamental-mode)
-      (type    . "text/plain"))
-     ("enriched"
-      (viewer . enriched-decode)
-      (type   . "text/enriched"))
-     ("dns"
-      (viewer . dns-mode)
-      (type   . "text/dns")))
-    ("video"
-     ("mpeg"
-      (viewer . "mpeg_play %s")
-      (type   . "video/mpeg")
-      (test   . (eq window-system 'x))
-      ("needsx11")))
-    ("x-world"
-     ("x-vrml"
-      (viewer  . "webspace -remote %s -URL %u")
-      (type    . "x-world/x-vrml")
-      ("description"
-       "VRML document")))
-    ("archive"
-     ("tar"
-      (viewer . tar-mode)
-      (type . "archive/tar"))))
-  "The mailcap structure is an assoc list of assoc lists.
-1st assoc list is keyed on the major content-type
-2nd assoc list is keyed on the minor content-type (which can be a regexp)
-
-Which looks like:
------------------
- ((\"application\"
-   (\"postscript\" . <info>))
-  (\"text\"
-   (\"plain\" . <info>)))
-
-Where <info> is another assoc list of the various information
-related to the mailcap RFC 1524.  This is keyed on the lowercase
-attribute name (viewer, test, etc).  This looks like:
- ((viewer . VIEWERINFO)
-  (test   . TESTINFO)
-  (xxxx   . \"STRING\")
-  FLAG)
-
-Where VIEWERINFO specifies how the content-type is viewed.  Can be
-a string, in which case it is run through a shell, with appropriate
-parameters, or a symbol, in which case the symbol is `funcall'ed if
-and only if it exists as a function, with the buffer as an argument.
-
-TESTINFO is a test for the viewer's applicability, or nil.  If nil, it
-means the viewer is always valid.  If it is a Lisp function, it is
-called with a list of items from any extra fields from the
-Content-Type header as argument to return a boolean value for the
-validity.  Otherwise, if it is a non-function Lisp symbol or list
-whose car is a symbol, it is `eval'led to yield the validity.  If it
-is a string or list of strings, it represents a shell command to run
-to return a true or false shell value for the validity.")
-(put 'mailcap-mime-data 'risky-local-variable t)
-
-(defcustom mailcap-download-directory nil
-  "*Directory to which `mailcap-save-binary-file' downloads files by default.
-nil means your home directory."
-  :type '(choice (const :tag "Home directory" nil)
-                directory)
-  :group 'mailcap)
-
-(defvar mailcap-poor-system-types
-  '(ms-dos windows-nt)
-  "Systems that don't have a Unix-like directory hierarchy.")
-
-;;;
-;;; Utility functions
-;;;
-
-(defun mailcap-save-binary-file ()
-  (goto-char (point-min))
-  (unwind-protect
-      (let ((file (read-file-name
-                  "Filename to save as: "
-                  (or mailcap-download-directory "~/")))
-           (require-final-newline nil))
-       (write-region (point-min) (point-max) file))
-    (kill-buffer (current-buffer))))
-
-(defvar mailcap-maybe-eval-warning
-  "*** WARNING ***
-
-This MIME part contains untrusted and possibly harmful content.
-If you evaluate the Emacs Lisp code contained in it, a lot of nasty
-things can happen.  Please examine the code very carefully before you
-instruct Emacs to evaluate it.  You can browse the buffer containing
-the code using \\[scroll-other-window].
-
-If you are unsure what to do, please answer \"no\"."
-  "Text of warning message displayed by `mailcap-maybe-eval'.
-Make sure that this text consists only of few text lines.  Otherwise,
-Gnus might fail to display all of it.")
-
-(defun mailcap-maybe-eval ()
-  "Maybe evaluate a buffer of Emacs Lisp code."
-  (let ((lisp-buffer (current-buffer)))
-    (goto-char (point-min))
-    (when
-       (save-window-excursion
-         (delete-other-windows)
-         (let ((buffer (get-buffer-create (generate-new-buffer-name
-                                           "*Warning*"))))
-           (unwind-protect
-               (with-current-buffer buffer
-                 (insert (substitute-command-keys
-                          mailcap-maybe-eval-warning))
-                 (goto-char (point-min))
-                 (display-buffer buffer)
-                 (yes-or-no-p "This is potentially dangerous emacs-lisp code, evaluate it? "))
-             (kill-buffer buffer))))
-      (eval-buffer (current-buffer)))
-    (when (buffer-live-p lisp-buffer)
-      (with-current-buffer lisp-buffer
-       (emacs-lisp-mode)))))
-
-
-;;;
-;;; The mailcap parser
-;;;
-
-(defun mailcap-replace-regexp (regexp to-string)
-  ;; Quiet replace-regexp.
-  (goto-char (point-min))
-  (while (re-search-forward regexp nil t)
-    (replace-match to-string t nil)))
-
-(defvar mailcap-parsed-p nil)
-
-(defun mailcap-parse-mailcaps (&optional path force)
-  "Parse out all the mailcaps specified in a path string PATH.
-Components of PATH are separated by the `path-separator' character
-appropriate for this system.  If FORCE, re-parse even if already
-parsed.  If PATH is omitted, use the value of environment variable
-MAILCAPS if set; otherwise (on Unix) use the path from RFC 1524, plus
-/usr/local/etc/mailcap."
-  (interactive (list nil t))
-  (when (or (not mailcap-parsed-p)
-           force)
-    (cond
-     (path nil)
-     ((getenv "MAILCAPS") (setq path (getenv "MAILCAPS")))
-     ((memq system-type mailcap-poor-system-types)
-      (setq path '("~/.mailcap" "~/mail.cap" "~/etc/mail.cap")))
-     (t (setq path
-             ;; This is per RFC 1524, specifically
-             ;; with /usr before /usr/local.
-             '("~/.mailcap" "/etc/mailcap" "/usr/etc/mailcap"
-               "/usr/local/etc/mailcap"))))
-    (let ((fnames (reverse
-                  (if (stringp path)
-                      (split-string path path-separator t)
-                    path)))
-         fname)
-      (while fnames
-       (setq fname (car fnames))
-       (if (and (file-readable-p fname)
-                (file-regular-p fname))
-           (mailcap-parse-mailcap fname))
-       (setq fnames (cdr fnames))))
-      (setq mailcap-parsed-p t)))
-
-(defun mailcap-parse-mailcap (fname)
-  "Parse out the mailcap file specified by FNAME."
-  (let (major                          ; The major mime type (image/audio/etc)
-       minor                           ; The minor mime type (gif, basic, etc)
-       save-pos                        ; Misc saved positions used in parsing
-       viewer                          ; How to view this mime type
-       info                            ; Misc info about this mime type
-       )
-    (with-temp-buffer
-      (insert-file-contents fname)
-      (set-syntax-table mailcap-parse-args-syntax-table)
-      (mailcap-replace-regexp "#.*" "")        ; Remove all comments
-      (mailcap-replace-regexp "\\\\[ \t]*\n" " ") ; And collapse spaces
-      (mailcap-replace-regexp "\n+" "\n") ; And blank lines
-      (goto-char (point-max))
-      (skip-chars-backward " \t\n")
-      (delete-region (point) (point-max))
-      (while (not (bobp))
-       (skip-chars-backward " \t\n")
-       (beginning-of-line)
-       (setq save-pos (point)
-             info nil)
-       (skip-chars-forward "^/; \t\n")
-       (downcase-region save-pos (point))
-       (setq major (buffer-substring save-pos (point)))
-       (skip-chars-forward " \t")
-       (setq minor "")
-       (when (eq (char-after) ?/)
-         (forward-char)
-         (skip-chars-forward " \t")
-         (setq save-pos (point))
-         (skip-chars-forward "^; \t\n")
-         (downcase-region save-pos (point))
-         (setq minor
-               (cond
-                ((eq ?* (or (char-after save-pos) 0)) ".*")
-                ((= (point) save-pos) ".*")
-                (t (regexp-quote (buffer-substring save-pos (point)))))))
-       (skip-chars-forward " \t")
-       ;;; Got the major/minor chunks, now for the viewers/etc
-       ;;; The first item _must_ be a viewer, according to the
-       ;;; RFC for mailcap files (#1524)
-       (setq viewer "")
-       (when (eq (char-after) ?\;)
-         (forward-char)
-         (skip-chars-forward " \t")
-         (setq save-pos (point))
-         (skip-chars-forward "^;\n")
-         ;; skip \;
-         (while (eq (char-before) ?\\)
-           (backward-delete-char 1)
-           (forward-char)
-           (skip-chars-forward "^;\n"))
-         (if (eq (or (char-after save-pos) 0) ?')
-             (setq viewer (progn
-                            (narrow-to-region (1+ save-pos) (point))
-                            (goto-char (point-min))
-                            (prog1
-                                (read (current-buffer))
-                              (goto-char (point-max))
-                              (widen))))
-           (setq viewer (buffer-substring save-pos (point)))))
-       (setq save-pos (point))
-       (end-of-line)
-       (unless (equal viewer "")
-         (setq info (nconc (list (cons 'viewer viewer)
-                                 (cons 'type (concat major "/"
-                                                     (if (string= minor ".*")
-                                                         "*" minor))))
-                           (mailcap-parse-mailcap-extras save-pos (point))))
-         (mailcap-mailcap-entry-passes-test info)
-         (mailcap-add-mailcap-entry major minor info))
-       (beginning-of-line)))))
-
-(defun mailcap-parse-mailcap-extras (st nd)
-  "Grab all the extra stuff from a mailcap entry."
-  (let (
-       name                            ; From name=
-       value                           ; its value
-       results                         ; Assoc list of results
-       name-pos                        ; Start of XXXX= position
-       val-pos                         ; Start of value position
-       done                            ; Found end of \'d ;s?
-       )
-    (save-restriction
-      (narrow-to-region st nd)
-      (goto-char (point-min))
-      (skip-chars-forward " \n\t;")
-      (while (not (eobp))
-       (setq done nil)
-       (setq name-pos (point))
-       (skip-chars-forward "^ \n\t=;")
-       (downcase-region name-pos (point))
-       (setq name (buffer-substring name-pos (point)))
-       (skip-chars-forward " \t\n")
-       (if (not (eq (char-after (point)) ?=)) ; There is no value
-           (setq value t)
-         (skip-chars-forward " \t\n=")
-         (setq val-pos (point))
-         (if (memq (char-after val-pos) '(?\" ?'))
-             (progn
-               (setq val-pos (1+ val-pos))
-               (condition-case nil
-                   (progn
-                     (forward-sexp 1)
-                     (backward-char 1))
-                 (error (goto-char (point-max)))))
-           (while (not done)
-             (skip-chars-forward "^;")
-             (if (eq (char-after (1- (point))) ?\\ )
-                 (progn
-                   (subst-char-in-region (1- (point)) (point) ?\\ ? )
-                   (skip-chars-forward ";"))
-               (setq done t))))
-         (setq value (buffer-substring val-pos (point))))
-       ;; `test' as symbol, others like "copiousoutput" and "needsx11" as
-       ;; strings
-       (setq results (cons (cons (if (string-equal name "test")
-                                      'test
-                                    name)
-                                  value) results))
-       (skip-chars-forward " \";\n\t"))
-      results)))
-
-(defun mailcap-mailcap-entry-passes-test (info)
-  "Replace the test clause of INFO itself with a boolean for some cases.
-This function supports only `test -n $DISPLAY' and `test -z $DISPLAY',
-replaces them with t or nil.  As for others or if INFO has a interactive
-spec (needsterm, needsterminal, or needsx11) but DISPLAY is not set,
-the test clause will be unchanged."
-  (let ((test (assq 'test info))       ; The test clause
-       status)
-    (setq status (and test (split-string (cdr test) " ")))
-    (if (and (or (assoc "needsterm" info)
-                (assoc "needsterminal" info)
-                (assoc "needsx11" info))
-            (not (getenv "DISPLAY")))
-       (setq status nil)
-      (cond
-       ((and (equal (nth 0 status) "test")
-            (equal (nth 1 status) "-n")
-            (or (equal (nth 2 status) "$DISPLAY")
-                (equal (nth 2 status) "\"$DISPLAY\"")))
-       (setq status (if (getenv "DISPLAY") t nil)))
-       ((and (equal (nth 0 status) "test")
-            (equal (nth 1 status) "-z")
-            (or (equal (nth 2 status) "$DISPLAY")
-                (equal (nth 2 status) "\"$DISPLAY\"")))
-       (setq status (if (getenv "DISPLAY") nil t)))
-       (test nil)
-       (t nil)))
-    (and test (listp test) (setcdr test status))))
-
-;;;
-;;; The action routines.
-;;;
-
-(defun mailcap-possible-viewers (major minor)
-  "Return a list of possible viewers from MAJOR for minor type MINOR."
-  (let ((exact '())
-       (wildcard '()))
-    (while major
-      (cond
-       ((equal (car (car major)) minor)
-       (setq exact (cons (cdr (car major)) exact)))
-       ((and minor (string-match (concat "^" (car (car major)) "$") minor))
-       (setq wildcard (cons (cdr (car major)) wildcard))))
-      (setq major (cdr major)))
-    (nconc exact wildcard)))
-
-(defun mailcap-unescape-mime-test (test type-info)
-  (let (save-pos save-chr subst)
-    (cond
-     ((symbolp test) test)
-     ((and (listp test) (symbolp (car test))) test)
-     ((or (stringp test)
-         (and (listp test) (stringp (car test))
-              (setq test (mapconcat 'identity test " "))))
-      (with-temp-buffer
-       (insert test)
-       (goto-char (point-min))
-       (while (not (eobp))
-         (skip-chars-forward "^%")
-         (if (/= (- (point)
-                    (progn (skip-chars-backward "\\\\")
-                           (point)))
-                 0)                    ; It is an escaped %
-             (progn
-               (delete-char 1)
-               (skip-chars-forward "%."))
-           (setq save-pos (point))
-           (skip-chars-forward "%")
-           (setq save-chr (char-after (point)))
-           ;; Escapes:
-           ;; %s: name of a file for the body data
-           ;; %t: content-type
-           ;; %{<parameter name}: value of parameter in mailcap entry
-           ;; %n: number of sub-parts for multipart content-type
-           ;; %F: a set of content-type/filename pairs for multiparts
-           (cond
-            ((null save-chr) nil)
-            ((= save-chr ?t)
-             (delete-region save-pos (progn (forward-char 1) (point)))
-             (insert (or (cdr (assq 'type type-info)) "\"\"")))
-            ((memq save-chr '(?M ?n ?F))
-             (delete-region save-pos (progn (forward-char 1) (point)))
-             (insert "\"\""))
-            ((= save-chr ?{)
-             (forward-char 1)
-             (skip-chars-forward "^}")
-             (downcase-region (+ 2 save-pos) (point))
-             (setq subst (buffer-substring (+ 2 save-pos) (point)))
-             (delete-region save-pos (1+ (point)))
-             (insert (or (cdr (assoc subst type-info)) "\"\"")))
-            (t nil))))
-       (buffer-string)))
-     (t (error "Bad value to mailcap-unescape-mime-test: %s" test)))))
-
-(defvar mailcap-viewer-test-cache nil)
-
-(defun mailcap-viewer-passes-test (viewer-info type-info)
-  "Return non-nil if viewer specified by VIEWER-INFO passes its test clause.
-Also return non-nil if it has no test clause.  TYPE-INFO is an argument
-to supply to the test."
-  (let* ((test-info (assq 'test viewer-info))
-        (test (cdr test-info))
-        (otest test)
-        (viewer (cdr (assq 'viewer viewer-info)))
-        (default-directory (expand-file-name "~/"))
-        status parsed-test cache result)
-    (cond ((not (or (stringp viewer) (fboundp viewer)))
-          nil)                         ; Non-existent Lisp function
-         ((setq cache (assoc test mailcap-viewer-test-cache))
-          (cadr cache))
-         ((not test-info) t)           ; No test clause
-         (t
-          (setq
-           result
-           (cond
-            ((not test) nil)           ; Already failed test
-            ((eq test t) t)            ; Already passed test
-            ((functionp test)          ; Lisp function as test
-             (funcall test type-info))
-            ((and (symbolp test)       ; Lisp variable as test
-                  (boundp test))
-             (symbol-value test))
-            ((and (listp test)         ; List to be eval'd
-                  (symbolp (car test)))
-             (eval test))
-            (t
-             (setq test (mailcap-unescape-mime-test test type-info)
-                   test (list shell-file-name nil nil nil
-                              shell-command-switch test)
-                   status (apply 'call-process test))
-             (eq 0 status))))
-          (push (list otest result) mailcap-viewer-test-cache)
-          result))))
-
-(defun mailcap-add-mailcap-entry (major minor info)
-  (let ((old-major (assoc major mailcap-mime-data)))
-    (if (null old-major)               ; New major area
-       (setq mailcap-mime-data
-             (cons (cons major (list (cons minor info)))
-                   mailcap-mime-data))
-      (let ((cur-minor (assoc minor old-major)))
-       (cond
-        ((or (null cur-minor)          ; New minor area, or
-             (assq 'test info))        ; Has a test, insert at beginning
-         (setcdr old-major (cons (cons minor info) (cdr old-major))))
-        ((and (not (assq 'test info))  ; No test info, replace completely
-              (not (assq 'test cur-minor))
-              (equal (assq 'viewer info)  ; Keep alternative viewer
-                     (assq 'viewer cur-minor)))
-         (setcdr cur-minor info))
-        (t
-         (setcdr old-major (cons (cons minor info) (cdr old-major))))))
-      )))
-
-(defun mailcap-add (type viewer &optional test)
-  "Add VIEWER as a handler for TYPE.
-If TEST is not given, it defaults to t."
-  (let ((tl (split-string type "/")))
-    (when (or (not (car tl))
-             (not (cadr tl)))
-      (error "%s is not a valid MIME type" type))
-    (mailcap-add-mailcap-entry
-     (car tl) (cadr tl)
-     `((viewer . ,viewer)
-       (test . ,(if test test t))
-       (type . ,type)))))
-
-;;;
-;;; The main whabbo
-;;;
-
-(defun mailcap-viewer-lessp (x y)
-  "Return t if viewer X is more desirable than viewer Y."
-  (let ((x-wild (string-match "[*?]" (or (cdr-safe (assq 'type x)) "")))
-       (y-wild (string-match "[*?]" (or (cdr-safe (assq 'type y)) "")))
-       (x-lisp (not (stringp (or (cdr-safe (assq 'viewer x)) ""))))
-       (y-lisp (not (stringp (or (cdr-safe (assq 'viewer y)) "")))))
-    (cond
-     ((and x-wild (not y-wild))
-      nil)
-     ((and (not x-wild) y-wild)
-      t)
-     ((and (not y-lisp) x-lisp)
-      t)
-     (t nil))))
-
-(defun mailcap-mime-info (string &optional request no-decode)
-  "Get the MIME viewer command for STRING, return nil if none found.
-Expects a complete content-type header line as its argument.
-
-Second argument REQUEST specifies what information to return.  If it is
-nil or the empty string, the viewer (second field of the mailcap
-entry) will be returned.  If it is a string, then the mailcap field
-corresponding to that string will be returned (print, description,
-whatever).  If a number, then all the information for this specific
-viewer is returned.  If `all', then all possible viewers for
-this type is returned.
-
-If NO-DECODE is non-nil, don't decode STRING."
-  ;; NO-DECODE avoids calling `mail-header-parse-content-type' from
-  ;; `mail-parse.el'
-  (let (
-       major                           ; Major encoding (text, etc)
-       minor                           ; Minor encoding (html, etc)
-       info                            ; Other info
-       save-pos                        ; Misc. position during parse
-       major-info                      ; (assoc major mailcap-mime-data)
-       minor-info                      ; (assoc minor major-info)
-       test                            ; current test proc.
-       viewers                         ; Possible viewers
-       passed                          ; Viewers that passed the test
-       viewer                          ; The one and only viewer
-       ctl)
-    (save-excursion
-      (setq ctl
-           (if no-decode
-               (list (or string "text/plain"))
-             (mail-header-parse-content-type (or string "text/plain"))))
-      (setq major (split-string (car ctl) "/"))
-      (setq minor (cadr major)
-           major (car major))
-      (when (setq major-info (cdr (assoc major mailcap-mime-data)))
-       (when (setq viewers (mailcap-possible-viewers major-info minor))
-         (setq info (mapcar (lambda (a) (cons (symbol-name (car a))
-                                              (cdr a)))
-                            (cdr ctl)))
-         (while viewers
-           (if (mailcap-viewer-passes-test (car viewers) info)
-               (setq passed (cons (car viewers) passed)))
-           (setq viewers (cdr viewers)))
-         (setq passed (sort passed 'mailcap-viewer-lessp))
-         (setq viewer (car passed))))
-      (when (and (stringp (cdr (assq 'viewer viewer)))
-                passed)
-       (setq viewer (car passed)))
-      (cond
-       ((and (null viewer) (not (equal major "default")) request)
-       (mailcap-mime-info "default" request no-decode))
-       ((or (null request) (equal request ""))
-       (mailcap-unescape-mime-test (cdr (assq 'viewer viewer)) info))
-       ((stringp request)
-       (mailcap-unescape-mime-test
-        (cdr-safe (assoc request viewer)) info))
-       ((eq request 'all)
-       passed)
-       (t
-       ;; MUST make a copy *sigh*, else we modify mailcap-mime-data
-       (setq viewer (copy-sequence viewer))
-       (let ((view (assq 'viewer viewer))
-             (test (assq 'test viewer)))
-         (if view (setcdr view (mailcap-unescape-mime-test (cdr view) info)))
-         (if test (setcdr test (mailcap-unescape-mime-test (cdr test) info))))
-       viewer)))))
-
-;;;
-;;; Experimental MIME-types parsing
-;;;
-
-(defvar mailcap-mime-extensions
-  '((""       . "text/plain")
-    (".1"     . "text/plain")  ;; Manual pages
-    (".3"     . "text/plain")
-    (".8"     . "text/plain")
-    (".abs"   . "audio/x-mpeg")
-    (".aif"   . "audio/aiff")
-    (".aifc"  . "audio/aiff")
-    (".aiff"  . "audio/aiff")
-    (".ano"   . "application/x-annotator")
-    (".au"    . "audio/ulaw")
-    (".avi"   . "video/x-msvideo")
-    (".bcpio" . "application/x-bcpio")
-    (".bin"   . "application/octet-stream")
-    (".cdf"   . "application/x-netcdr")
-    (".cpio"  . "application/x-cpio")
-    (".csh"   . "application/x-csh")
-    (".css"   . "text/css")
-    (".dvi"   . "application/x-dvi")
-    (".diff"  . "text/x-patch")
-    (".dpatch". "test/x-patch")
-    (".el"    . "application/emacs-lisp")
-    (".eps"   . "application/postscript")
-    (".etx"   . "text/x-setext")
-    (".exe"   . "application/octet-stream")
-    (".fax"   . "image/x-fax")
-    (".gif"   . "image/gif")
-    (".hdf"   . "application/x-hdf")
-    (".hqx"   . "application/mac-binhex40")
-    (".htm"   . "text/html")
-    (".html"  . "text/html")
-    (".icon"  . "image/x-icon")
-    (".ief"   . "image/ief")
-    (".jpg"   . "image/jpeg")
-    (".macp"  . "image/x-macpaint")
-    (".man"   . "application/x-troff-man")
-    (".me"    . "application/x-troff-me")
-    (".mif"   . "application/mif")
-    (".mov"   . "video/quicktime")
-    (".movie" . "video/x-sgi-movie")
-    (".mp2"   . "audio/x-mpeg")
-    (".mp3"   . "audio/x-mpeg")
-    (".mp2a"  . "audio/x-mpeg2")
-    (".mpa"   . "audio/x-mpeg")
-    (".mpa2"  . "audio/x-mpeg2")
-    (".mpe"   . "video/mpeg")
-    (".mpeg"  . "video/mpeg")
-    (".mpega" . "audio/x-mpeg")
-    (".mpegv" . "video/mpeg")
-    (".mpg"   . "video/mpeg")
-    (".mpv"   . "video/mpeg")
-    (".ms"    . "application/x-troff-ms")
-    (".nc"    . "application/x-netcdf")
-    (".nc"    . "application/x-netcdf")
-    (".oda"   . "application/oda")
-    (".patch" . "text/x-patch")
-    (".pbm"   . "image/x-portable-bitmap")
-    (".pdf"   . "application/pdf")
-    (".pgm"   . "image/portable-graymap")
-    (".pict"  . "image/pict")
-    (".png"   . "image/png")
-    (".pnm"   . "image/x-portable-anymap")
-    (".pod"   . "text/plain")
-    (".ppm"   . "image/portable-pixmap")
-    (".ps"    . "application/postscript")
-    (".qt"    . "video/quicktime")
-    (".ras"   . "image/x-raster")
-    (".rgb"   . "image/x-rgb")
-    (".rtf"   . "application/rtf")
-    (".rtx"   . "text/richtext")
-    (".sh"    . "application/x-sh")
-    (".sit"   . "application/x-stuffit")
-    (".siv"   . "application/sieve")
-    (".snd"   . "audio/basic")
-    (".soa"   . "text/dns")
-    (".src"   . "application/x-wais-source")
-    (".tar"   . "archive/tar")
-    (".tcl"   . "application/x-tcl")
-    (".tex"   . "application/x-tex")
-    (".texi"  . "application/texinfo")
-    (".tga"   . "image/x-targa")
-    (".tif"   . "image/tiff")
-    (".tiff"  . "image/tiff")
-    (".tr"    . "application/x-troff")
-    (".troff" . "application/x-troff")
-    (".tsv"   . "text/tab-separated-values")
-    (".txt"   . "text/plain")
-    (".vbs"   . "video/mpeg")
-    (".vox"   . "audio/basic")
-    (".vrml"  . "x-world/x-vrml")
-    (".wav"   . "audio/x-wav")
-    (".xls"   . "application/vnd.ms-excel")
-    (".wrl"   . "x-world/x-vrml")
-    (".xbm"   . "image/xbm")
-    (".xpm"   . "image/xpm")
-    (".xwd"   . "image/windowdump")
-    (".zip"   . "application/zip")
-    (".ai"    . "application/postscript")
-    (".jpe"   . "image/jpeg")
-    (".jpeg"  . "image/jpeg")
-    (".org"   . "text/x-org"))
-  "An alist of file extensions and corresponding MIME content-types.
-This exists for you to customize the information in Lisp.  It is
-merged with values from mailcap files by `mailcap-parse-mimetypes'.")
-
-(defvar mailcap-mimetypes-parsed-p nil)
-
-(defun mailcap-parse-mimetypes (&optional path force)
-  "Parse out all the mimetypes specified in a Unix-style path string PATH.
-Components of PATH are separated by the `path-separator' character
-appropriate for this system.  If PATH is omitted, use the value of
-environment variable MIMETYPES if set; otherwise use a default path.
-If FORCE, re-parse even if already parsed."
-  (interactive (list nil t))
-  (when (or (not mailcap-mimetypes-parsed-p)
-           force)
-    (cond
-     (path nil)
-     ((getenv "MIMETYPES") (setq path (getenv "MIMETYPES")))
-     ((memq system-type mailcap-poor-system-types)
-      (setq path '("~/mime.typ" "~/etc/mime.typ")))
-     (t (setq path
-             ;; mime.types seems to be the normal name, definitely so
-             ;; on current GNUish systems.  The search order follows
-             ;; that for mailcap.
-             '("~/.mime.types"
-               "/etc/mime.types"
-               "/usr/etc/mime.types"
-               "/usr/local/etc/mime.types"
-               "/usr/local/www/conf/mime.types"
-               "~/.mime-types"
-               "/etc/mime-types"
-               "/usr/etc/mime-types"
-               "/usr/local/etc/mime-types"
-               "/usr/local/www/conf/mime-types"))))
-    (let ((fnames (reverse (if (stringp path)
-                              (split-string path path-separator t)
-                            path)))
-         fname)
-      (while fnames
-       (setq fname (car fnames))
-       (if (and (file-readable-p fname))
-           (mailcap-parse-mimetype-file fname))
-       (setq fnames (cdr fnames))))
-    (setq mailcap-mimetypes-parsed-p t)))
-
-(defun mailcap-parse-mimetype-file (fname)
-  "Parse out a mime-types file FNAME."
-  (let (type                           ; The MIME type for this line
-       extns                           ; The extensions for this line
-       save-pos                        ; Misc. saved buffer positions
-       )
-    (with-temp-buffer
-      (insert-file-contents fname)
-      (mailcap-replace-regexp "#.*" "")
-      (mailcap-replace-regexp "\n+" "\n")
-      (mailcap-replace-regexp "[ \t]+$" "")
-      (goto-char (point-max))
-      (skip-chars-backward " \t\n")
-      (delete-region (point) (point-max))
-      (goto-char (point-min))
-      (while (not (eobp))
-       (skip-chars-forward " \t\n")
-       (setq save-pos (point))
-       (skip-chars-forward "^ \t\n")
-       (downcase-region save-pos (point))
-       (setq type (buffer-substring save-pos (point)))
-       (while (not (eolp))
-         (skip-chars-forward " \t")
-         (setq save-pos (point))
-         (skip-chars-forward "^ \t\n")
-         (setq extns (cons (buffer-substring save-pos (point)) extns)))
-       (while extns
-         (setq mailcap-mime-extensions
-               (cons
-                (cons (if (= (string-to-char (car extns)) ?.)
-                          (car extns)
-                        (concat "." (car extns))) type)
-                mailcap-mime-extensions)
-               extns (cdr extns)))))))
-
-(defun mailcap-extension-to-mime (extn)
-  "Return the MIME content type of the file extensions EXTN."
-  (mailcap-parse-mimetypes)
-  (if (and (stringp extn)
-          (not (eq (string-to-char extn) ?.)))
-      (setq extn (concat "." extn)))
-  (cdr (assoc (downcase extn) mailcap-mime-extensions)))
-
-;; Unused?
-(defalias 'mailcap-command-p 'executable-find)
-
-(defun mailcap-mime-types ()
-  "Return a list of MIME media types."
-  (mailcap-parse-mimetypes)
-  (delete-dups
-   (nconc
-    (mapcar 'cdr mailcap-mime-extensions)
-    (apply
-     'nconc
-     (mapcar
-      (lambda (l)
-       (delq nil
-             (mapcar
-              (lambda (m)
-                (let ((type (cdr (assq 'type (cdr m)))))
-                  (if (equal (cadr (split-string type "/"))
-                             "*")
-                      nil
-                    type)))
-              (cdr l))))
-      mailcap-mime-data)))))
-
-;;;
-;;; Useful supplementary functions
-;;;
-
-(defun mailcap-file-default-commands (files)
-  "Return a list of default commands for FILES."
-  (mailcap-parse-mailcaps)
-  (mailcap-parse-mimetypes)
-  (let* ((all-mime-type
-         ;; All unique MIME types from file extensions
-         (delete-dups
-          (mapcar (lambda (file)
-                    (mailcap-extension-to-mime
-                     (file-name-extension file t)))
-                  files)))
-        (all-mime-info
-         ;; All MIME info lists
-         (delete-dups
-          (mapcar (lambda (mime-type)
-                    (mailcap-mime-info mime-type 'all))
-                  all-mime-type)))
-        (common-mime-info
-         ;; Intersection of mime-infos from different mime-types;
-         ;; or just the first MIME info for a single MIME type
-         (if (cdr all-mime-info)
-             (delq nil (mapcar (lambda (mi1)
-                                 (unless (memq nil (mapcar
-                                                    (lambda (mi2)
-                                                      (member mi1 mi2))
-                                                    (cdr all-mime-info)))
-                                   mi1))
-                               (car all-mime-info)))
-           (car all-mime-info)))
-        (commands
-         ;; Command strings from `viewer' field of the MIME info
-         (delete-dups
-          (delq nil (mapcar
-                     (lambda (mime-info)
-                       (let ((command (cdr (assoc 'viewer mime-info))))
-                         (if (stringp command)
-                             (replace-regexp-in-string
-                              ;; Replace mailcap's `%s' placeholder
-                              ;; with dired's `?' placeholder
-                              "%s" "?"
-                              (replace-regexp-in-string
-                               ;; Remove the final filename placeholder
-                               "[ \t\n]*\\('\\)?%s\\1?[ \t\n]*\\'" ""
-                               command nil t)
-                              nil t))))
-                            common-mime-info)))))
-    commands))
-
-(defun mailcap-view-mime (type)
-  "View the data in the current buffer that has MIME type TYPE.
-`mailcap-mime-data' determines the method to use."
-  (let ((method (mailcap-mime-info type)))
-    (if (stringp method)
-       (shell-command-on-region (point-min) (point-max)
-                                ;; Use stdin as the "%s".
-                                (format method "-")
-                                (current-buffer)
-                                t)
-      (funcall method))))
-
-(provide 'mailcap)
-
-;;; mailcap.el ends here
diff --git a/lisp/gnus/plstore.el b/lisp/gnus/plstore.el
deleted file mode 100644 (file)
index 62c50c0..0000000
+++ /dev/null
@@ -1,570 +0,0 @@
-;;; plstore.el --- secure plist store -*- lexical-binding: t -*-
-;; Copyright (C) 2011-2016 Free Software Foundation, Inc.
-
-;; Author: Daiki Ueno <ueno@unixuser.org>
-;; Keywords: PGP, GnuPG
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary
-
-;; Plist based data store providing search and partial encryption.
-;;
-;; Creating:
-;;
-;; ;; Open a new store associated with ~/.emacs.d/auth.plist.
-;; (setq store (plstore-open (expand-file-name "~/.emacs.d/auth.plist")))
-;; ;; Both `:host' and `:port' are public property.
-;; (plstore-put store "foo" '(:host "foo.example.org" :port 80) nil)
-;; ;; No encryption will be needed.
-;; (plstore-save store)
-;;
-;; ;; `:user' is marked as secret.
-;; (plstore-put store "bar" '(:host "bar.example.org") '(:user "test"))
-;; ;; `:password' is marked as secret.
-;; (plstore-put store "baz" '(:host "baz.example.org") '(:password "test"))
-;; ;; Those secret properties are encrypted together.
-;; (plstore-save store)
-;;
-;; ;; Kill the buffer visiting ~/.emacs.d/auth.plist.
-;; (plstore-close store)
-;;
-;; Searching:
-;;
-;; (setq store (plstore-open (expand-file-name "~/.emacs.d/auth.plist")))
-;;
-;; ;; As the entry "foo" associated with "foo.example.org" has no
-;; ;; secret properties, no need to decryption.
-;; (plstore-find store '(:host ("foo.example.org")))
-;;
-;; ;; As the entry "bar" associated with "bar.example.org" has a
-;; ;; secret property `:user', Emacs tries to decrypt the secret (and
-;; ;; thus you will need to input passphrase).
-;; (plstore-find store '(:host ("bar.example.org")))
-;;
-;; ;; While the entry "baz" associated with "baz.example.org" has also
-;; ;; a secret property `:password', it is encrypted together with
-;; ;; `:user' of "bar", so no need to decrypt the secret.
-;; (plstore-find store '(:host ("bar.example.org")))
-;;
-;; (plstore-close store)
-;;
-;; Editing:
-;;
-;; This file also provides `plstore-mode', a major mode for editing
-;; the PLSTORE format file.  Visit a non-existing file and put the
-;; following line:
-;;
-;; (("foo" :host "foo.example.org" :secret-user "user"))
-;;
-;; where the prefixing `:secret-' means the property (without
-;; `:secret-' prefix) is marked as secret.  Thus, when you save the
-;; buffer, the `:secret-user' property is encrypted as `:user'.
-;;
-;; You can toggle the view between encrypted form and the decrypted
-;; form with C-c C-c.
-
-;;; Code:
-
-(require 'epg)
-
-(defgroup plstore nil
-  "Searchable, partially encrypted, persistent plist store"
-  :version "24.1"
-  :group 'files)
-
-(defcustom plstore-select-keys 'silent
-  "Control whether or not to pop up the key selection dialog.
-
-If t, always asks user to select recipients.
-If nil, query user only when a file's default recipients are not
-known (i.e. `plstore-encrypt-to' is not locally set in the buffer
-visiting a plstore file).
-If neither t nor nil, doesn't ask user."
-  :type '(choice (const :tag "Ask always" t)
-                (const :tag "Ask when recipients are not set" nil)
-                (const :tag "Don't ask" silent))
-  :group 'plstore)
-
-(defvar plstore-encrypt-to nil
-  "*Recipient(s) used for encrypting secret entries.
-May either be a string or a list of strings.  If it is nil,
-symmetric encryption will be used.")
-
-(put 'plstore-encrypt-to 'safe-local-variable
-     (lambda (val)
-       (or (stringp val)
-          (and (listp val)
-               (catch 'safe
-                 (mapc (lambda (elt)
-                         (unless (stringp elt)
-                           (throw 'safe nil)))
-                       val)
-                 t)))))
-
-(put 'plstore-encrypt-to 'permanent-local t)
-
-(defvar plstore-encoded nil)
-
-(put 'plstore-encoded 'permanent-local t)
-
-(defvar plstore-cache-passphrase-for-symmetric-encryption nil)
-(defvar plstore-passphrase-alist nil)
-
-(defun plstore-passphrase-callback-function (_context _key-id plstore)
-  (if plstore-cache-passphrase-for-symmetric-encryption
-      (let* ((file (file-truename (plstore-get-file plstore)))
-            (entry (assoc file plstore-passphrase-alist))
-            passphrase)
-       (or (copy-sequence (cdr entry))
-           (progn
-             (unless entry
-               (setq entry (list file)
-                     plstore-passphrase-alist
-                     (cons entry
-                           plstore-passphrase-alist)))
-             (setq passphrase
-                   (read-passwd (format "Passphrase for PLSTORE %s: "
-                                        (plstore--get-buffer plstore))))
-             (setcdr entry (copy-sequence passphrase))
-             passphrase)))
-    (read-passwd (format "Passphrase for PLSTORE %s: "
-                        (plstore--get-buffer plstore)))))
-
-(defun plstore-progress-callback-function (_context _what _char current total
-                                                   handback)
-  (if (= current total)
-      (message "%s...done" handback)
-    (message "%s...%d%%" handback
-            (if (> total 0) (floor (* (/ current (float total)) 100)) 0))))
-
-(defun plstore--get-buffer (arg)
-  (aref arg 0))
-
-(defun plstore--get-alist (arg)
-  (aref arg 1))
-
-(defun plstore--get-encrypted-data (arg)
-  (aref arg 2))
-
-(defun plstore--get-secret-alist (arg)
-  (aref arg 3))
-
-(defun plstore--get-merged-alist (arg)
-  (aref arg 4))
-
-(defun plstore--set-buffer (arg buffer)
-  (aset arg 0 buffer))
-
-(defun plstore--set-alist (arg plist)
-  (aset arg 1 plist))
-
-(defun plstore--set-encrypted-data (arg encrypted-data)
-  (aset arg 2 encrypted-data))
-
-(defun plstore--set-secret-alist (arg secret-alist)
-  (aset arg 3 secret-alist))
-
-(defun plstore--set-merged-alist (arg merged-alist)
-  (aset arg 4 merged-alist))
-
-(defun plstore-get-file (arg)
-  (buffer-file-name (plstore--get-buffer arg)))
-
-(defun plstore--make (&optional buffer alist encrypted-data secret-alist
-                               merged-alist)
-  (vector buffer alist encrypted-data secret-alist merged-alist))
-
-(defun plstore--init-from-buffer (plstore)
-  (goto-char (point-min))
-  (when (looking-at ";;; public entries")
-    (forward-line)
-    (plstore--set-alist plstore (read (point-marker)))
-    (forward-sexp)
-    (forward-char)
-    (when (looking-at ";;; secret entries")
-      (forward-line)
-      (plstore--set-encrypted-data plstore (read (point-marker))))
-    (plstore--merge-secret plstore)))
-
-;;;###autoload
-(defun plstore-open (file)
-  "Create a plstore instance associated with FILE."
-  (let* ((filename (file-truename file))
-        (buffer (or (find-buffer-visiting filename)
-                    (generate-new-buffer (format " plstore %s" filename))))
-        (store (plstore--make buffer)))
-    (with-current-buffer buffer
-      (erase-buffer)
-      (condition-case nil
-         (insert-file-contents-literally file)
-       (error))
-      (setq buffer-file-name (file-truename file))
-      (set-buffer-modified-p nil)
-      (plstore--init-from-buffer store)
-      store)))
-
-(defun plstore-revert (plstore)
-  "Replace current data in PLSTORE with the file on disk."
-  (with-current-buffer (plstore--get-buffer plstore)
-    (revert-buffer t t)
-    (plstore--init-from-buffer plstore)))
-
-(defun plstore-close (plstore)
-  "Destroy a plstore instance PLSTORE."
-  (kill-buffer (plstore--get-buffer plstore)))
-
-(defun plstore--merge-secret (plstore)
-  (let ((alist (plstore--get-secret-alist plstore))
-       modified-alist
-       modified-plist
-       modified-entry
-       entry
-       plist
-       placeholder)
-    (plstore--set-merged-alist
-     plstore
-     (copy-tree (plstore--get-alist plstore)))
-    (setq modified-alist (plstore--get-merged-alist plstore))
-    (while alist
-      (setq entry (car alist)
-           alist (cdr alist)
-           plist (cdr entry)
-           modified-entry (assoc (car entry) modified-alist)
-           modified-plist (cdr modified-entry))
-      (while plist
-       (setq placeholder
-             (plist-member
-              modified-plist
-              (intern (concat ":secret-"
-                              (substring (symbol-name (car plist)) 1)))))
-       (if placeholder
-           (setcar placeholder (car plist)))
-       (setq modified-plist
-             (plist-put modified-plist (car plist) (car (cdr plist))))
-       (setq plist (nthcdr 2 plist)))
-      (setcdr modified-entry modified-plist))))
-
-(defun plstore--decrypt (plstore)
-  (if (plstore--get-encrypted-data plstore)
-      (let ((context (epg-make-context 'OpenPGP))
-           plain)
-       (epg-context-set-passphrase-callback
-        context
-        (cons #'plstore-passphrase-callback-function
-              plstore))
-       (epg-context-set-progress-callback
-        context
-        (cons #'plstore-progress-callback-function
-              (format "Decrypting %s" (plstore-get-file plstore))))
-       (condition-case error
-           (setq plain
-                 (epg-decrypt-string context
-                                     (plstore--get-encrypted-data plstore)))
-         (error
-          (let ((entry (assoc (plstore-get-file plstore)
-                              plstore-passphrase-alist)))
-            (if entry
-                (setcdr entry nil)))
-          (signal (car error) (cdr error))))
-       (plstore--set-secret-alist plstore (car (read-from-string plain)))
-       (plstore--merge-secret plstore)
-       (plstore--set-encrypted-data plstore nil))))
-
-(defun plstore--match (entry keys skip-if-secret-found)
-  (let ((result t) key-name key-value prop-value secret-name)
-    (while keys
-      (setq key-name (car keys)
-           key-value (car (cdr keys))
-           prop-value (plist-get (cdr entry) key-name))
-       (unless (member prop-value key-value)
-         (if skip-if-secret-found
-             (progn
-               (setq secret-name
-                     (intern (concat ":secret-"
-                                     (substring (symbol-name key-name) 1))))
-               (if (plist-member (cdr entry) secret-name)
-                   (setq result 'secret)
-                 (setq result nil
-                       keys nil)))
-           (setq result nil
-                 keys nil)))
-       (setq keys (nthcdr 2 keys)))
-    result))
-
-(defun plstore-find (plstore keys)
-  "Perform search on PLSTORE with KEYS.
-KEYS is a plist."
-  (let (entries alist entry match decrypt plist)
-    ;; First, go through the merged plist alist and collect entries
-    ;; matched with keys.
-    (setq alist (plstore--get-merged-alist plstore))
-    (while alist
-      (setq entry (car alist)
-           alist (cdr alist)
-           match (plstore--match entry keys t))
-      (if (eq match 'secret)
-         (setq decrypt t)
-       (when match
-         (setq plist (cdr entry))
-         (while plist
-           (if (string-match "\\`:secret-" (symbol-name (car plist)))
-               (setq decrypt t
-                     plist nil))
-           (setq plist (nthcdr 2 plist)))
-         (setq entries (cons entry entries)))))
-    ;; Second, decrypt the encrypted plist and try again.
-    (when decrypt
-      (setq entries nil)
-      (plstore--decrypt plstore)
-      (setq alist (plstore--get-merged-alist plstore))
-      (while alist
-       (setq entry (car alist)
-             alist (cdr alist)
-             match (plstore--match entry keys nil))
-       (if match
-           (setq entries (cons entry entries)))))
-    (nreverse entries)))
-
-(defun plstore-get (plstore name)
-  "Get an entry with NAME in PLSTORE."
-  (let ((entry (assoc name (plstore--get-merged-alist plstore)))
-       plist)
-    (setq plist (cdr entry))
-    (while plist
-      (if (string-match "\\`:secret-" (symbol-name (car plist)))
-         (progn
-           (plstore--decrypt plstore)
-           (setq entry (assoc name (plstore--get-merged-alist plstore))
-                 plist nil))
-       (setq plist (nthcdr 2 plist))))
-    entry))
-
-(defun plstore-put (plstore name keys secret-keys)
-  "Put an entry with NAME in PLSTORE.
-KEYS is a plist containing non-secret data.
-SECRET-KEYS is a plist containing secret data."
-  (let (entry
-       plist
-       secret-plist
-       symbol)
-    (if secret-keys
-       (plstore--decrypt plstore))
-    (while secret-keys
-      (setq symbol
-           (intern (concat ":secret-"
-                           (substring (symbol-name (car secret-keys)) 1))))
-      (setq plist (plist-put plist symbol t)
-           secret-plist (plist-put secret-plist
-                                   (car secret-keys) (car (cdr secret-keys)))
-           secret-keys (nthcdr 2 secret-keys)))
-    (while keys
-      (setq symbol
-           (intern (concat ":secret-"
-                           (substring (symbol-name (car keys)) 1))))
-      (setq plist (plist-put plist (car keys) (car (cdr keys)))
-           keys (nthcdr 2 keys)))
-    (setq entry (assoc name (plstore--get-alist plstore)))
-    (if entry
-       (setcdr entry plist)
-      (plstore--set-alist
-       plstore
-       (cons (cons name plist) (plstore--get-alist plstore))))
-    (when secret-plist
-      (setq entry (assoc name (plstore--get-secret-alist plstore)))
-      (if entry
-         (setcdr entry secret-plist)
-       (plstore--set-secret-alist
-        plstore
-        (cons (cons name secret-plist) (plstore--get-secret-alist plstore)))))
-    (plstore--merge-secret plstore)))
-
-(defun plstore-delete (plstore name)
-  "Delete an entry with NAME from PLSTORE."
-  (let ((entry (assoc name (plstore--get-alist plstore))))
-    (if entry
-       (plstore--set-alist
-        plstore
-        (delq entry (plstore--get-alist plstore))))
-    (setq entry (assoc name (plstore--get-secret-alist plstore)))
-    (if entry
-       (plstore--set-secret-alist
-        plstore
-        (delq entry (plstore--get-secret-alist plstore))))
-    (setq entry (assoc name (plstore--get-merged-alist plstore)))
-    (if entry
-       (plstore--set-merged-alist
-        plstore
-        (delq entry (plstore--get-merged-alist plstore))))))
-
-(defvar pp-escape-newlines)
-(defun plstore--insert-buffer (plstore)
-  (insert ";;; public entries -*- mode: plstore -*- \n"
-         (pp-to-string (plstore--get-alist plstore)))
-  (if (plstore--get-secret-alist plstore)
-      (let ((context (epg-make-context 'OpenPGP))
-           (pp-escape-newlines nil)
-           (recipients
-            (cond
-             ((listp plstore-encrypt-to) plstore-encrypt-to)
-             ((stringp plstore-encrypt-to) (list plstore-encrypt-to))))
-           cipher)
-       (setf (epg-context-armor context) t)
-       (epg-context-set-passphrase-callback
-        context
-        (cons #'plstore-passphrase-callback-function
-              plstore))
-       (setq cipher (epg-encrypt-string
-                     context
-                     (pp-to-string
-                      (plstore--get-secret-alist plstore))
-                     (if (or (eq plstore-select-keys t)
-                             (and (null plstore-select-keys)
-                                  (not (local-variable-p 'plstore-encrypt-to
-                                                         (current-buffer)))))
-                         (epa-select-keys
-                          context
-                          "Select recipients for encryption.
-If no one is selected, symmetric encryption will be performed.  "
-                          recipients)
-                       (if plstore-encrypt-to
-                           (epg-list-keys context recipients)))))
-       (goto-char (point-max))
-       (insert ";;; secret entries\n" (pp-to-string cipher)))))
-
-(defun plstore-save (plstore)
-  "Save the contents of PLSTORE associated with a FILE."
-  (with-current-buffer (plstore--get-buffer plstore)
-    (erase-buffer)
-    (plstore--insert-buffer plstore)
-    (save-buffer)))
-
-(defun plstore--encode (plstore)
-  (plstore--decrypt plstore)
-  (let ((merged-alist (plstore--get-merged-alist plstore)))
-    (concat "("
-           (mapconcat
-            (lambda (entry)
-              (setq entry (copy-sequence entry))
-              (let ((merged-plist (cdr (assoc (car entry) merged-alist)))
-                    (plist (cdr entry)))
-                (while plist
-                  (if (string-match "\\`:secret-" (symbol-name (car plist)))
-                      (setcar (cdr plist)
-                              (plist-get
-                               merged-plist
-                               (intern (concat ":"
-                                               (substring (symbol-name
-                                                           (car plist))
-                                                          (match-end 0)))))))
-                  (setq plist (nthcdr 2 plist)))
-                (prin1-to-string entry)))
-            (plstore--get-alist plstore)
-            "\n")
-           ")")))
-
-(defun plstore--decode (string)
-  (let* ((alist (car (read-from-string string)))
-        (pointer alist)
-        secret-alist
-        plist
-        entry)
-    (while pointer
-      (unless (stringp (car (car pointer)))
-       (error "Invalid PLSTORE format %s" string))
-      (setq plist (cdr (car pointer)))
-      (while plist
-       (when (string-match "\\`:secret-" (symbol-name (car plist)))
-         (setq entry (assoc (car (car pointer)) secret-alist))
-         (unless entry
-           (setq entry (list (car (car pointer)))
-                 secret-alist (cons entry secret-alist)))
-         (setcdr entry (plist-put (cdr entry)
-                                  (intern (concat ":"
-                                               (substring (symbol-name
-                                                           (car plist))
-                                                          (match-end 0))))
-                                  (car (cdr plist))))
-         (setcar (cdr plist) t))
-       (setq plist (nthcdr 2 plist)))
-      (setq pointer (cdr pointer)))
-    (plstore--make nil alist nil secret-alist)))
-
-(defun plstore--write-contents-functions ()
-  (when plstore-encoded
-    (let ((store (plstore--decode (buffer-string)))
-         (file (buffer-file-name)))
-      (unwind-protect
-         (progn
-           (set-visited-file-name nil)
-           (with-temp-buffer
-             (plstore--insert-buffer store)
-             (write-region (buffer-string) nil file)))
-       (set-visited-file-name file)
-       (set-buffer-modified-p nil))
-      t)))
-
-(defun plstore-mode-original ()
-  "Show the original form of the this buffer."
-  (interactive)
-  (when plstore-encoded
-    (if (and (buffer-modified-p)
-            (y-or-n-p "Save buffer before reading the original form? "))
-       (save-buffer))
-    (erase-buffer)
-    (insert-file-contents-literally (buffer-file-name))
-    (set-buffer-modified-p nil)
-    (setq plstore-encoded nil)))
-
-(defun plstore-mode-decoded ()
-  "Show the decoded form of the this buffer."
-  (interactive)
-  (unless plstore-encoded
-    (if (and (buffer-modified-p)
-            (y-or-n-p "Save buffer before decoding? "))
-       (save-buffer))
-    (let ((store (plstore--make (current-buffer))))
-      (plstore--init-from-buffer store)
-      (erase-buffer)
-      (insert
-       (substitute-command-keys "\
-;;; You are looking at the decoded form of the PLSTORE file.\n\
-;;; To see the original form content, do \\[plstore-mode-toggle-display]\n\n"))
-      (insert (plstore--encode store))
-      (set-buffer-modified-p nil)
-      (setq plstore-encoded t))))
-
-(defun plstore-mode-toggle-display ()
-  "Toggle the display mode of PLSTORE between the original and decoded forms."
-  (interactive)
-  (if plstore-encoded
-      (plstore-mode-original)
-    (plstore-mode-decoded)))
-
-;;;###autoload
-(define-derived-mode plstore-mode emacs-lisp-mode "PLSTORE"
-  "Major mode for editing PLSTORE files."
-  (make-local-variable 'plstore-encoded)
-  (add-hook 'write-contents-functions #'plstore--write-contents-functions)
-  (define-key plstore-mode-map "\C-c\C-c" #'plstore-mode-toggle-display)
-  ;; to create a new file with plstore-mode, mark it as already decoded
-  (if (called-interactively-p 'any)
-      (setq plstore-encoded t)
-    (plstore-mode-decoded)))
-
-(provide 'plstore)
-
-;;; plstore.el ends here
diff --git a/lisp/gnus/pop3.el b/lisp/gnus/pop3.el
deleted file mode 100644 (file)
index 1695bbd..0000000
+++ /dev/null
@@ -1,914 +0,0 @@
-;;; pop3.el --- Post Office Protocol (RFC 1460) interface
-
-;; Copyright (C) 1996-2016 Free Software Foundation, Inc.
-
-;; Author: Richard L. Pieri <ratinox@peorth.gweep.net>
-;; Maintainer: emacs-devel@gnu.org
-;; Keywords: mail
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; Most of the standard Post Office Protocol version 3 (RFC 1460) commands
-;; are implemented.  The LIST command has not been implemented due to lack
-;; of actual usefulness.
-;; The optional POP3 command TOP has not been implemented.
-
-;; This program was inspired by Kyle E. Jones's vm-pop program.
-
-;;; Code:
-
-(eval-when-compile (require 'cl))
-
-(require 'mail-utils)
-(defvar parse-time-months)
-
-(defgroup pop3 nil
-  "Post Office Protocol."
-  :group 'mail
-  :group 'mail-source)
-
-(defcustom pop3-maildrop (or (user-login-name)
-                            (getenv "LOGNAME")
-                            (getenv "USER"))
-  "*POP3 maildrop."
-  :version "22.1" ;; Oort Gnus
-  :type 'string
-  :group 'pop3)
-
-(defcustom pop3-mailhost (or (getenv "MAILHOST") ;; nil -> mismatch
-                            "pop3")
-  "*POP3 mailhost."
-  :version "22.1" ;; Oort Gnus
-  :type 'string
-  :group 'pop3)
-
-(defcustom pop3-port 110
-  "*POP3 port."
-  :version "22.1" ;; Oort Gnus
-  :type 'number
-  :group 'pop3)
-
-(defcustom pop3-password-required t
-  "*Non-nil if a password is required when connecting to POP server."
-  :version "22.1" ;; Oort Gnus
-  :type 'boolean
-  :group 'pop3)
-
-;; Should this be customizable?
-(defvar pop3-password nil
-  "*Password to use when connecting to POP server.")
-
-(defcustom pop3-authentication-scheme 'pass
-  "*POP3 authentication scheme.
-Defaults to `pass', for the standard USER/PASS authentication.  The other
-valid value is `apop'."
-  :type '(choice (const :tag "Normal user/password" pass)
-                (const :tag "APOP" apop))
-  :version "22.1" ;; Oort Gnus
-  :group 'pop3)
-
-(defcustom pop3-stream-length 100
-  "How many messages should be requested at one time.
-The lower the number, the more latency-sensitive the fetching
-will be.  If your pop3 server doesn't support streaming at all,
-set this to 1."
-  :type 'number
-  :version "24.1"
-  :group 'pop3)
-
-(defcustom pop3-leave-mail-on-server nil
-  "Non-nil if the mail is to be left on the POP server after fetching.
-Mails once fetched will never be fetched again by the UIDL control.
-
-If this is neither nil nor a number, all mails will be left on the
-server.  If this is a number, leave mails on the server for this many
-days since you first checked new mails.  If this is nil, mails will be
-deleted on the server right after fetching.
-
-Gnus users should use the `:leave' keyword in a mail source to direct
-the behavior per server, rather than directly modifying this value.
-
-Note that POP servers maintain no state information between sessions,
-so what the client believes is there and what is actually there may
-not match up.  If they do not, then you may get duplicate mails or
-the whole thing can fall apart and leave you with a corrupt mailbox."
-  :version "24.4"
-  :type '(choice (const :tag "Don't leave mails" nil)
-                (const :tag "Leave all mails" t)
-                (number :tag "Leave mails for this many days" :value 14))
-  :group 'pop3)
-
-(defcustom pop3-uidl-file "~/.pop3-uidl"
-  "File used to save UIDL."
-  :version "24.4"
-  :type 'file
-  :group 'pop3)
-
-(defcustom pop3-uidl-file-backup '(0 9)
-  "How to backup the UIDL file `pop3-uidl-file' when updating.
-If it is a list of numbers, the first one binds `kept-old-versions' and
-the other binds `kept-new-versions' to keep number of oldest and newest
-versions.  Otherwise, the value binds `version-control' (which see).
-
-Note: Backup will take place whenever you check new mails on a server.
-So, you may lose the backup files having been saved before a trouble
-if you set it so as to make too few backups whereas you have access to
-many servers."
-  :version "24.4"
-  :type '(choice (group :tag "Keep versions" :format "\n%v" :indent 3
-                       (number :tag "oldest")
-                       (number :tag "newest"))
-                (sexp :format "%v"
-                      :match (lambda (widget value)
-                               (condition-case nil
-                                   (not (and (numberp (car value))
-                                             (numberp (car (cdr value)))))
-                                 (error t)))))
-  :group 'pop3)
-
-(defvar pop3-timestamp nil
-  "Timestamp returned when initially connected to the POP server.
-Used for APOP authentication.")
-
-(defvar pop3-read-point nil)
-(defvar pop3-debug nil)
-
-;; Borrowed from nnheader-accept-process-output in nnheader.el.  See the
-;; comments there for explanations about the values.
-
-(eval-and-compile
-  (if (and (fboundp 'nnheader-accept-process-output)
-          (boundp 'nnheader-read-timeout))
-      (defalias 'pop3-accept-process-output 'nnheader-accept-process-output)
-    ;; Borrowed from `nnheader.el':
-    (defvar pop3-read-timeout
-      (if (string-match "windows-nt\\|os/2\\|cygwin"
-                       (symbol-name system-type))
-         1.0
-       0.01)
-      "How long pop3 should wait between checking for the end of output.
-Shorter values mean quicker response, but are more CPU intensive.")
-    (defun pop3-accept-process-output (process)
-      (accept-process-output
-       process
-       (truncate pop3-read-timeout)
-       (truncate (* (- pop3-read-timeout
-                      (truncate pop3-read-timeout))
-                   1000))))))
-
-(defvar pop3-uidl)
-;; List of UIDLs of existing messages at present in the server:
-;; ("UIDL1" "UIDL2" "UIDL3"...)
-
-(defvar pop3-uidl-saved)
-;; Locally saved UIDL data; an alist of the server, the user, and the UIDL
-;; and timestamp pairs:
-;; (("SERVER_A" ("USER_A1" "UIDL1" TIMESTAMP1 "UIDL2" TIMESTAMP2...)
-;;              ("USER_A2" "UIDL1" TIMESTAMP1 "UIDL2" TIMESTAMP2...)
-;;              ...)
-;;  ("SERVER_B" ("USER_B1" "UIDL1" TIMESTAMP1 "UIDL2" TIMESTAMP2...)
-;;              ("USER_B2" "UIDL1" TIMESTAMP1 "UIDL2" TIMESTAMP2...)
-;;              ...))
-;; Where TIMESTAMP is the most significant two digits of an Emacs time,
-;; i.e. the return value of `current-time'.
-
-;;;###autoload
-(defun pop3-movemail (file)
-  "Transfer contents of a maildrop to the specified FILE.
-Use streaming commands."
-  (let ((process (pop3-open-server pop3-mailhost pop3-port))
-       messages total-size
-       pop3-uidl
-       pop3-uidl-saved)
-    (pop3-logon process)
-    (if pop3-leave-mail-on-server
-       (setq messages (pop3-uidl-stat process)
-             total-size (cadr messages)
-             messages (car messages))
-      (let ((size (pop3-stat process)))
-       (dotimes (i (car size)) (push (1+ i) messages))
-       (setq messages (nreverse messages)
-             total-size (cadr size))))
-    (when messages
-      (with-current-buffer (process-buffer process)
-       (pop3-send-streaming-command process "RETR" messages total-size)
-       (pop3-write-to-file file messages)
-       (unless pop3-leave-mail-on-server
-         (pop3-send-streaming-command process "DELE" messages nil))))
-    (if pop3-leave-mail-on-server
-       (when (prog1 (pop3-uidl-dele process) (pop3-quit process))
-         (pop3-uidl-save))
-      (pop3-quit process)
-      ;; Remove UIDL data for the account that got not to leave mails.
-      (setq pop3-uidl-saved (pop3-uidl-load))
-      (let ((elt (assoc pop3-maildrop
-                       (cdr (assoc pop3-mailhost pop3-uidl-saved)))))
-       (when elt
-         (setcdr elt nil)
-         (pop3-uidl-save))))
-    t))
-
-(defun pop3-send-streaming-command (process command messages total-size)
-  (erase-buffer)
-  (let ((count (length messages))
-       (i 1)
-       (start-point (point-min))
-       (waited-for 0))
-    (while messages
-      (process-send-string process (format "%s %d\r\n" command (pop messages)))
-      ;; Only do 100 messages at a time to avoid pipe stalls.
-      (when (zerop (% i pop3-stream-length))
-       (setq start-point
-             (pop3-wait-for-messages process pop3-stream-length
-                                     total-size start-point))
-       (incf waited-for pop3-stream-length))
-      (incf i))
-    (pop3-wait-for-messages process (- count waited-for)
-                           total-size start-point)))
-
-(defun pop3-wait-for-messages (process count total-size start-point)
-  (while (> count 0)
-    (goto-char start-point)
-    (while (or (and (re-search-forward "^\\+OK" nil t)
-                   (or (not total-size)
-                       (re-search-forward "^\\.\r?\n" nil t)))
-              (re-search-forward "^-ERR " nil t))
-      (decf count)
-      (setq start-point (point)))
-    (unless (memq (process-status process) '(open run))
-      (error "pop3 process died"))
-    (when total-size
-      (let ((size 0))
-       (goto-char (point-min))
-       (while (re-search-forward "^\\+OK.*\n" nil t)
-         (setq size (+ size (- (point))
-                       (if (re-search-forward "^\\.\r?\n" nil 'move)
-                           (match-beginning 0)
-                         (point)))))
-       (message "pop3 retrieved %dKB (%d%%)"
-                (truncate (/ size 1000))
-                (truncate (* (/ (* size 1.0) total-size) 100)))))
-    (pop3-accept-process-output process))
-  start-point)
-
-(defun pop3-write-to-file (file messages)
-  (let ((pop-buffer (current-buffer))
-       (start (point-min))
-       beg end
-       temp-buffer)
-    (with-temp-buffer
-      (setq temp-buffer (current-buffer))
-      (with-current-buffer pop-buffer
-       (goto-char (point-min))
-       (while (re-search-forward "^\\+OK" nil t)
-         (forward-line 1)
-         (setq beg (point))
-         (when (re-search-forward "^\\.\r?\n" nil t)
-           (setq start (point))
-           (forward-line -1)
-           (setq end (point)))
-         (with-current-buffer temp-buffer
-           (goto-char (point-max))
-           (let ((hstart (point)))
-             (insert-buffer-substring pop-buffer beg end)
-             (pop3-clean-region hstart (point))
-             (goto-char (point-max))
-             (pop3-munge-message-separator hstart (point))
-             (when pop3-leave-mail-on-server
-               (pop3-uidl-add-xheader hstart (pop messages)))
-             (goto-char (point-max))))))
-      (let ((coding-system-for-write 'binary))
-       (goto-char (point-min))
-       ;; Check whether something inserted a newline at the start and
-       ;; delete it.
-       (when (eolp)
-         (delete-char 1))
-       (write-region (point-min) (point-max) file nil 'nomesg)))))
-
-(defun pop3-logon (process)
-  (let ((pop3-password pop3-password))
-    ;; for debugging only
-    (if pop3-debug (switch-to-buffer (process-buffer process)))
-    ;; query for password
-    (if (and pop3-password-required (not pop3-password))
-       (setq pop3-password
-             (read-passwd (format "Password for %s: " pop3-maildrop))))
-    (cond ((equal 'apop pop3-authentication-scheme)
-          (pop3-apop process pop3-maildrop))
-         ((equal 'pass pop3-authentication-scheme)
-          (pop3-user process pop3-maildrop)
-          (pop3-pass process))
-         (t (error "Invalid POP3 authentication scheme")))))
-
-(defun pop3-get-message-count ()
-  "Return the number of messages in the maildrop."
-  (let* ((process (pop3-open-server pop3-mailhost pop3-port))
-        message-count
-        (pop3-password pop3-password))
-    ;; for debugging only
-    (if pop3-debug (switch-to-buffer (process-buffer process)))
-    ;; query for password
-    (if (and pop3-password-required (not pop3-password))
-       (setq pop3-password
-             (read-passwd (format "Password for %s: " pop3-maildrop))))
-    (cond ((equal 'apop pop3-authentication-scheme)
-          (pop3-apop process pop3-maildrop))
-         ((equal 'pass pop3-authentication-scheme)
-          (pop3-user process pop3-maildrop)
-          (pop3-pass process))
-         (t (error "Invalid POP3 authentication scheme")))
-    (setq message-count (car (pop3-stat process)))
-    (pop3-quit process)
-    message-count))
-
-(defun pop3-uidl-stat (process)
-  "Return a list of unread message numbers and total size."
-  (pop3-send-command process "UIDL")
-  (let (err messages size)
-    (if (condition-case code
-           (progn
-             (pop3-read-response process)
-             t)
-         (error (setq err (error-message-string code))
-                nil))
-       (let ((start pop3-read-point)
-             saved list)
-         (with-current-buffer (process-buffer process)
-           (while (not (re-search-forward "^\\.\r\n" nil t))
-             (unless (memq (process-status process) '(open run))
-               (error "pop3 server closed the connection"))
-             (pop3-accept-process-output process)
-             (goto-char start))
-           (setq pop3-read-point (point-marker)
-                 pop3-uidl nil)
-           (while (progn (forward-line -1) (>= (point) start))
-             (when (looking-at "[0-9]+ \\([^\n\r ]+\\)")
-               (push (match-string 1) pop3-uidl)))
-           (when pop3-uidl
-             (setq pop3-uidl-saved (pop3-uidl-load)
-                   saved (cdr (assoc pop3-maildrop
-                                     (cdr (assoc pop3-mailhost
-                                                 pop3-uidl-saved)))))
-             (let ((i (length pop3-uidl)))
-               (while (> i 0)
-                 (unless (member (nth (1- i) pop3-uidl) saved)
-                   (push i messages))
-                 (decf i)))
-             (when messages
-               (setq list (pop3-list process)
-                     size 0)
-               (dolist (msg messages)
-                 (setq size (+ size (cdr (assq msg list)))))
-               (list messages size)))))
-      (message "%s doesn't support UIDL (%s), so we try a regressive way..."
-              pop3-mailhost err)
-      (sit-for 1)
-      (setq size (pop3-stat process))
-      (dotimes (i (car size)) (push (1+ i) messages))
-      (setcar size (nreverse messages))
-      size)))
-
-(defun pop3-uidl-dele (process)
-  "Delete messages according to `pop3-leave-mail-on-server'.
-Return non-nil if it is necessary to update the local UIDL file."
-  (let* ((ctime (current-time))
-        (srvr (assoc pop3-mailhost pop3-uidl-saved))
-        (saved (assoc pop3-maildrop (cdr srvr)))
-        i uidl mod new tstamp dele)
-    (setcdr (cdr ctime) nil)
-    ;; Add new messages to the data to be saved.
-    (cond ((and pop3-uidl saved)
-          (setq i (1- (length pop3-uidl)))
-          (while (>= i 0)
-            (unless (member (setq uidl (nth i pop3-uidl)) (cdr saved))
-              (push ctime new)
-              (push uidl new))
-            (decf i)))
-         (pop3-uidl
-          (setq new (apply 'nconc (mapcar (lambda (elt) (list elt ctime))
-                                          pop3-uidl)))))
-    (when new (setq mod t))
-    ;; List expirable messages and delete them from the data to be saved.
-    (setq ctime (when (numberp pop3-leave-mail-on-server)
-                 (/ (+ (* (car ctime) 65536.0) (cadr ctime)) 86400))
-         i (1- (length saved)))
-    (while (> i 0)
-      (if (member (setq uidl (nth (1- i) saved)) pop3-uidl)
-         (progn
-           (setq tstamp (nth i saved))
-           (if (and ctime
-                    (> (- ctime (/ (+ (* (car tstamp) 65536.0) (cadr tstamp))
-                                   86400))
-                       pop3-leave-mail-on-server))
-               ;; Mails to delete.
-               (progn
-                 (setq mod t)
-                 (push uidl dele))
-             ;; Mails to keep.
-             (push tstamp new)
-             (push uidl new)))
-       ;; Mails having been deleted in the server.
-       (setq mod t))
-      (decf i 2))
-    (cond (saved
-          (setcdr saved new))
-         (srvr
-          (setcdr (last srvr) (list (cons pop3-maildrop new))))
-         (t
-          (add-to-list 'pop3-uidl-saved
-                       (list pop3-mailhost (cons pop3-maildrop new))
-                       t)))
-    ;; Actually delete the messages in the server.
-    (when dele
-      (setq uidl nil
-           i (length pop3-uidl))
-      (while (> i 0)
-       (when (member (nth (1- i) pop3-uidl) dele)
-         (push i uidl))
-       (decf i))
-      (when uidl
-       (pop3-send-streaming-command process "DELE" uidl nil)))
-    mod))
-
-(defun pop3-uidl-load ()
-  "Load saved UIDL."
-  (when (file-exists-p pop3-uidl-file)
-    (with-temp-buffer
-      (condition-case code
-         (progn
-           (insert-file-contents pop3-uidl-file)
-           (goto-char (point-min))
-           (read (current-buffer)))
-       (error
-        (message "Error while loading %s (%s)"
-                 pop3-uidl-file (error-message-string code))
-        (sit-for 1)
-        nil)))))
-
-(defun pop3-uidl-save ()
-  "Save UIDL."
-  (with-temp-buffer
-    (if pop3-uidl-saved
-       (progn
-         (insert "(")
-         (dolist (srvr pop3-uidl-saved)
-           (when (cdr srvr)
-             (insert "(\"" (pop srvr) "\"\n  ")
-             (dolist (elt srvr)
-               (when (cdr elt)
-                 (insert "(\"" (pop elt) "\"\n   ")
-                 (while elt
-                   (insert (format "\"%s\" %s\n   " (pop elt) (pop elt))))
-                 (delete-char -4)
-                 (insert ")\n  ")))
-             (delete-char -3)
-             (if (eq (char-before) ?\))
-                 (insert ")\n ")
-               (goto-char (1+ (point-at-bol)))
-               (delete-region (point) (point-max)))))
-         (when (eq (char-before) ? )
-           (delete-char -2))
-         (insert ")\n"))
-      (insert "()\n"))
-    (let ((buffer-file-name pop3-uidl-file)
-         (delete-old-versions t)
-         (kept-new-versions kept-new-versions)
-         (kept-old-versions kept-old-versions)
-         (version-control version-control))
-      (if (consp pop3-uidl-file-backup)
-         (setq kept-new-versions (cadr pop3-uidl-file-backup)
-               kept-old-versions (car pop3-uidl-file-backup)
-               version-control t)
-       (setq version-control pop3-uidl-file-backup))
-      (save-buffer))))
-
-(defun pop3-uidl-add-xheader (start msgno)
-  "Add X-UIDL header."
-  (let ((case-fold-search t))
-    (save-restriction
-      (narrow-to-region start (progn
-                               (goto-char start)
-                               (search-forward "\n\n" nil 'move)
-                               (1- (point))))
-      (goto-char start)
-      (while (re-search-forward "^x-uidl:" nil t)
-       (while (progn
-                (forward-line 1)
-                (memq (char-after) '(?\t ? ))))
-       (delete-region (match-beginning 0) (point)))
-      (goto-char (point-max))
-      (insert "X-UIDL: " (nth (1- msgno) pop3-uidl) "\n"))))
-
-(defcustom pop3-stream-type nil
-  "*Transport security type for POP3 connections.
-This may be either nil (plain connection), `ssl' (use an
-SSL/TSL-secured stream) or `starttls' (use the starttls mechanism
-to turn on TLS security after opening the stream).  However, if
-this is nil, `ssl' is assumed for connections to port
-995 (pop3s)."
-  :version "23.1" ;; No Gnus
-  :group 'pop3
-  :type '(choice (const :tag "Plain" nil)
-                (const :tag "SSL/TLS" ssl)
-                (const starttls)))
-
-(defun pop3-open-server (mailhost port)
-  "Open TCP connection to MAILHOST on PORT.
-Returns the process associated with the connection."
-  (let ((coding-system-for-read 'binary)
-       (coding-system-for-write 'binary)
-       result)
-    (with-current-buffer
-        (get-buffer-create (concat " trace of POP session to "
-                                   mailhost))
-      (erase-buffer)
-      (setq pop3-read-point (point-min))
-      (setq result
-           (open-network-stream
-            "POP" (current-buffer) mailhost port
-            :type (cond
-                   ((or (eq pop3-stream-type 'ssl)
-                        (and (not pop3-stream-type)
-                             (member port '(995 "pop3s"))))
-                    'tls)
-                   (t
-                    (or pop3-stream-type 'network)))
-            :warn-unless-encrypted t
-            :capability-command "CAPA\r\n"
-            :end-of-command "^\\(-ERR\\|+OK\\).*\n"
-            :end-of-capability "^\\.\r?\n\\|^-ERR"
-            :success "^\\+OK.*\n"
-            :return-list t
-            :starttls-function
-            (lambda (capabilities)
-              (and (string-match "\\bSTLS\\b" capabilities)
-                   "STLS\r\n"))))
-      (when result
-       (let ((response (plist-get (cdr result) :greeting)))
-         (setq pop3-timestamp
-               (substring response (or (string-match "<" response) 0)
-                          (+ 1 (or (string-match ">" response) -1)))))
-       (set-process-query-on-exit-flag (car result) nil)
-       (erase-buffer)
-       (car result)))))
-
-;; Support functions
-
-(defun pop3-send-command (process command)
-  (set-buffer (process-buffer process))
-  (goto-char (point-max))
-  ;; (if (= (aref command 0) ?P)
-  ;;     (insert "PASS <omitted>\r\n")
-  ;;   (insert command "\r\n"))
-  (setq pop3-read-point (point))
-  (goto-char (point-max))
-  (process-send-string process (concat command "\r\n")))
-
-(defun pop3-read-response (process &optional return)
-  "Read the response from the server.
-Return the response string if optional second argument is non-nil."
-  (let ((case-fold-search nil)
-       match-end)
-    (with-current-buffer (process-buffer process)
-      (goto-char pop3-read-point)
-      (while (and (memq (process-status process) '(open run))
-                 (not (search-forward "\r\n" nil t)))
-       (pop3-accept-process-output process)
-       (goto-char pop3-read-point))
-      (setq match-end (point))
-      (goto-char pop3-read-point)
-      (if (looking-at "-ERR")
-         (error "%s" (buffer-substring (point) (- match-end 2)))
-       (if (not (looking-at "+OK"))
-           (progn (setq pop3-read-point match-end) nil)
-         (setq pop3-read-point match-end)
-         (if return
-             (buffer-substring (point) match-end)
-           t)
-         )))))
-
-(defun pop3-clean-region (start end)
-  (setq end (set-marker (make-marker) end))
-  (save-excursion
-    (goto-char start)
-    (while (and (< (point) end) (search-forward "\r\n" end t))
-      (replace-match "\n" t t))
-    (goto-char start)
-    (while (and (< (point) end) (re-search-forward "^\\." end t))
-      (replace-match "" t t)
-      (forward-char)))
-  (set-marker end nil))
-
-;; Copied from message-make-date.
-(defun pop3-make-date (&optional now)
-  "Make a valid date header.
-If NOW, use that time instead."
-  (require 'parse-time)
-  (let* ((now (or now (current-time)))
-        (zone (nth 8 (decode-time now)))
-        (sign "+"))
-    (when (< zone 0)
-      (setq sign "-")
-      (setq zone (- zone)))
-    (concat
-     (format-time-string "%d" now)
-     ;; The month name of the %b spec is locale-specific.  Pfff.
-     (format " %s "
-            (capitalize (car (rassoc (nth 4 (decode-time now))
-                                     parse-time-months))))
-     (format-time-string "%Y %H:%M:%S %z" now))))
-
-(defun pop3-munge-message-separator (start end)
-  "Check to see if a message separator exists.  If not, generate one."
-  (save-excursion
-    (save-restriction
-      (narrow-to-region start end)
-      (goto-char (point-min))
-      (if (not (or (looking-at "From .?") ; Unix mail
-                  (looking-at "\001\001\001\001\n") ; MMDF
-                  (looking-at "BABYL OPTIONS:") ; Babyl
-                  ))
-         (let* ((from (mail-strip-quoted-names (mail-fetch-field "From")))
-                (tdate (mail-fetch-field "Date"))
-                (date (split-string (or (and tdate
-                                             (not (string= "" tdate))
-                                             tdate)
-                                        (pop3-make-date))
-                                    " "))
-                (From_))
-           ;; sample date formats I have seen
-           ;; Date: Tue, 9 Jul 1996 09:04:21 -0400 (EDT)
-           ;; Date: 08 Jul 1996 23:22:24 -0400
-           ;; should be
-           ;; Tue Jul 9 09:04:21 1996
-
-           ;; Fixme: This should use timezone on the date field contents.
-           (setq date
-                 (cond ((not date)
-                        "Tue Jan 1 00:00:0 1900")
-                       ((string-match "[A-Z]" (nth 0 date))
-                        (format "%s %s %s %s %s"
-                                (nth 0 date) (nth 2 date) (nth 1 date)
-                                (nth 4 date) (nth 3 date)))
-                       (t
-                        ;; this really needs to be better but I don't feel
-                        ;; like writing a date to day converter.
-                        (format "Sun %s %s %s %s"
-                                (nth 1 date) (nth 0 date)
-                                (nth 3 date) (nth 2 date)))
-                       ))
-           (setq From_ (format "\nFrom %s  %s\n" from date))
-           (while (string-match "," From_)
-             (setq From_ (concat (substring From_ 0 (match-beginning 0))
-                                 (substring From_ (match-end 0)))))
-           (goto-char (point-min))
-           (insert From_)
-           (if (search-forward "\n\n" nil t)
-               nil
-             (goto-char (point-max))
-             (insert "\n"))
-           (let ((size (- (point-max) (point))))
-             (forward-line -1)
-             (insert (format "Content-Length: %s\n" size)))
-           )))))
-
-;; The Command Set
-
-;; AUTHORIZATION STATE
-
-(defun pop3-user (process user)
-  "Send USER information to POP3 server."
-  (pop3-send-command process (format "USER %s" user))
-  (let ((response (pop3-read-response process t)))
-    (if (not (and response (string-match "+OK" response)))
-       (error "USER %s not valid" user))))
-
-(defun pop3-pass (process)
-  "Send authentication information to the server."
-  (pop3-send-command process (format "PASS %s" pop3-password))
-  (let ((response (pop3-read-response process t)))
-    (if (not (and response (string-match "+OK" response)))
-       (pop3-quit process))))
-
-(defun pop3-apop (process user)
-  "Send alternate authentication information to the server."
-  (let ((pass pop3-password))
-    (if (and pop3-password-required (not pass))
-       (setq pass
-             (read-passwd (format "Password for %s: " pop3-maildrop))))
-    (if pass
-       (let ((hash (md5 (concat pop3-timestamp pass) nil nil 'binary)))
-         (pop3-send-command process (format "APOP %s %s" user hash))
-         (let ((response (pop3-read-response process t)))
-           (if (not (and response (string-match "+OK" response)))
-               (pop3-quit process)))))
-    ))
-
-;; TRANSACTION STATE
-
-(defun pop3-stat (process)
-  "Return the number of messages in the maildrop and the maildrop's size."
-  (pop3-send-command process "STAT")
-  (let ((response (pop3-read-response process t)))
-    (list (string-to-number (nth 1 (split-string response " ")))
-         (string-to-number (nth 2 (split-string response " "))))
-    ))
-
-(defun pop3-list (process &optional msg)
-  "If MSG is nil, return an alist of (MESSAGE-ID . SIZE) pairs.
-Otherwise, return the size of the message-id MSG"
-  (pop3-send-command process (if msg
-                                (format "LIST %d" msg)
-                              "LIST"))
-  (let ((response (pop3-read-response process t)))
-    (if msg
-       (string-to-number (nth 2 (split-string response " ")))
-      (let ((start pop3-read-point) end)
-       (with-current-buffer (process-buffer process)
-         (while (not (re-search-forward "^\\.\r\n" nil t))
-           (pop3-accept-process-output process)
-           (goto-char start))
-         (setq pop3-read-point (point-marker))
-         (goto-char (match-beginning 0))
-         (setq end (point-marker))
-         (mapcar #'(lambda (s) (let ((split (split-string s " ")))
-                                 (cons (string-to-number (nth 0 split))
-                                       (string-to-number (nth 1 split)))))
-                 (split-string (buffer-substring start end) "\r\n" t)))))))
-
-(defun pop3-retr (process msg crashbuf)
-  "Retrieve message-id MSG to buffer CRASHBUF."
-  (pop3-send-command process (format "RETR %s" msg))
-  (pop3-read-response process)
-  (let ((start pop3-read-point) end)
-    (with-current-buffer (process-buffer process)
-      (while (not (re-search-forward "^\\.\r\n" nil t))
-       (unless (memq (process-status process) '(open run))
-         (error "pop3 server closed the connection"))
-       (pop3-accept-process-output process)
-       (goto-char start))
-      (setq pop3-read-point (point-marker))
-      ;; this code does not seem to work for some POP servers...
-      ;; and I cannot figure out why not.
-      ;;      (goto-char (match-beginning 0))
-      ;;      (backward-char 2)
-      ;;      (if (not (looking-at "\r\n"))
-      ;;         (insert "\r\n"))
-      ;;      (re-search-forward "\\.\r\n")
-      (goto-char (match-beginning 0))
-      (setq end (point-marker))
-      (pop3-clean-region start end)
-      (pop3-munge-message-separator start end)
-      (with-current-buffer crashbuf
-       (erase-buffer))
-      (copy-to-buffer crashbuf start end)
-      (delete-region start end)
-      )))
-
-(defun pop3-dele (process msg)
-  "Mark message-id MSG as deleted."
-  (pop3-send-command process (format "DELE %s" msg))
-  (pop3-read-response process))
-
-(defun pop3-noop (process msg)
-  "No-operation."
-  (pop3-send-command process "NOOP")
-  (pop3-read-response process))
-
-(defun pop3-last (process)
-  "Return highest accessed message-id number for the session."
-  (pop3-send-command process "LAST")
-  (let ((response (pop3-read-response process t)))
-    (string-to-number (nth 1 (split-string response " ")))
-    ))
-
-(defun pop3-rset (process)
-  "Remove all delete marks from current maildrop."
-  (pop3-send-command process "RSET")
-  (pop3-read-response process))
-
-;; UPDATE
-
-(defun pop3-quit (process)
-  "Close connection to POP3 server.
-Tell server to remove all messages marked as deleted, unlock the maildrop,
-and close the connection."
-  (pop3-send-command process "QUIT")
-  (pop3-read-response process t)
-  (if process
-      (with-current-buffer (process-buffer process)
-       (goto-char (point-max))
-       (delete-process process))))
-\f
-;; Summary of POP3 (Post Office Protocol version 3) commands and responses
-
-;;; AUTHORIZATION STATE
-
-;; Initial TCP connection
-;; Arguments: none
-;; Restrictions: none
-;; Possible responses:
-;;  +OK [POP3 server ready]
-
-;; USER name
-;; Arguments: a server specific user-id (required)
-;; Restrictions: authorization state [after unsuccessful USER or PASS
-;; Possible responses:
-;;  +OK [valid user-id]
-;;  -ERR [invalid user-id]
-
-;; PASS string
-;; Arguments: a server/user-id specific password (required)
-;; Restrictions: authorization state, after successful USER
-;; Possible responses:
-;;  +OK [maildrop locked and ready]
-;;  -ERR [invalid password]
-;;  -ERR [unable to lock maildrop]
-
-;; STLS      (RFC 2595)
-;; Arguments: none
-;; Restrictions: Only permitted in AUTHORIZATION state.
-;; Possible responses:
-;;  +OK
-;;  -ERR
-
-;;; TRANSACTION STATE
-
-;; STAT
-;; Arguments: none
-;; Restrictions: transaction state
-;; Possible responses:
-;;  +OK nn mm [# of messages, size of maildrop]
-
-;; LIST [msg]
-;; Arguments: a message-id (optional)
-;; Restrictions: transaction state; msg must not be deleted
-;; Possible responses:
-;;  +OK [scan listing follows]
-;;  -ERR [no such message]
-
-;; RETR msg
-;; Arguments: a message-id (required)
-;; Restrictions: transaction state; msg must not be deleted
-;; Possible responses:
-;;  +OK [message contents follow]
-;;  -ERR [no such message]
-
-;; DELE msg
-;; Arguments: a message-id (required)
-;; Restrictions: transaction state; msg must not be deleted
-;; Possible responses:
-;;  +OK [message deleted]
-;;  -ERR [no such message]
-
-;; NOOP
-;; Arguments: none
-;; Restrictions: transaction state
-;; Possible responses:
-;;  +OK
-
-;; LAST
-;; Arguments: none
-;; Restrictions: transaction state
-;; Possible responses:
-;;  +OK nn [highest numbered message accessed]
-
-;; RSET
-;; Arguments: none
-;; Restrictions: transaction state
-;; Possible responses:
-;;  +OK [all delete marks removed]
-
-;; UIDL [msg]
-;; Arguments: a message-id (optional)
-;; Restrictions: transaction state; msg must not be deleted
-;; Possible responses:
-;;  +OK [uidl listing follows]
-;;  -ERR [no such message]
-
-;;; UPDATE STATE
-
-;; QUIT
-;; Arguments: none
-;; Restrictions: none
-;; Possible responses:
-;;  +OK [TCP connection closed]
-
-(provide 'pop3)
-
-;;; pop3.el ends here
diff --git a/lisp/gnus/qp.el b/lisp/gnus/qp.el
deleted file mode 100644 (file)
index a295e0c..0000000
+++ /dev/null
@@ -1,177 +0,0 @@
-;;; qp.el --- Quoted-Printable functions
-
-;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
-
-;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
-;; Keywords: mail, extensions
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; Functions for encoding and decoding quoted-printable text as
-;; defined in RFC 2045.
-
-;;; Code:
-
-;;;###autoload
-(defun quoted-printable-decode-region (from to &optional coding-system)
-  "Decode quoted-printable in the region between FROM and TO, per RFC 2045.
-If CODING-SYSTEM is non-nil, decode bytes into characters with that
-coding-system.
-
-Interactively, you can supply the CODING-SYSTEM argument
-with \\[universal-coding-system-argument].
-
-The CODING-SYSTEM argument is a historical hangover and is deprecated.
-QP encodes raw bytes and should be decoded into raw bytes.  Decoding
-them into characters should be done separately."
-  (interactive
-   ;; Let the user determine the coding system with "C-x RET c".
-   (list (region-beginning) (region-end) coding-system-for-read))
-  (when (and coding-system
-            (not (coding-system-p coding-system))) ; e.g. `ascii' from Gnus
-    (setq coding-system nil))
-  (save-excursion
-    (save-restriction
-      ;; RFC 2045:  ``An "=" followed by two hexadecimal digits, one
-      ;; or both of which are lowercase letters in "abcdef", is
-      ;; formally illegal. A robust implementation might choose to
-      ;; recognize them as the corresponding uppercase letters.''
-      (let ((case-fold-search t))
-       (narrow-to-region from to)
-       ;; Do this in case we're called from Gnus, say, in a buffer
-       ;; which already contains non-ASCII characters which would
-       ;; then get doubly-decoded below.
-       (if coding-system
-           (encode-coding-region (point-min) (point-max) coding-system))
-       (goto-char (point-min))
-       (while (and (skip-chars-forward "^=")
-                   (not (eobp)))
-         (cond ((eq (char-after (1+ (point))) ?\n)
-                (delete-char 2))
-               ((looking-at "\\(=[0-9A-F][0-9A-F]\\)+")
-                ;; Decode this sequence at once; i.e. by a single
-                ;; deletion and insertion.
-                (let* ((n (/ (- (match-end 0) (point)) 3))
-                       (str (make-string n 0)))
-                  (dotimes (i n)
-                     (let ((n1 (char-after (1+ (point))))
-                           (n2 (char-after (+ 2 (point)))))
-                       (aset str i
-                             (+ (* 16 (- n1 (if (<= n1 ?9) ?0
-                                              (if (<= n1 ?F) (- ?A 10)
-                                                (- ?a 10)))))
-                                (- n2 (if (<= n2 ?9) ?0
-                                        (if (<= n2 ?F) (- ?A 10)
-                                          (- ?a 10)))))))
-                    (forward-char 3))
-                  (delete-region (match-beginning 0) (match-end 0))
-                  (insert str)))
-               (t
-                (message "Malformed quoted-printable text")
-                (forward-char)))))
-      (if coding-system
-         (decode-coding-region (point-min) (point-max) coding-system)))))
-
-(defun quoted-printable-decode-string (string &optional coding-system)
-  "Decode the quoted-printable encoded STRING and return the result.
-If CODING-SYSTEM is non-nil, decode the string with coding-system.
-Use of CODING-SYSTEM is deprecated; this function should deal with
-raw bytes, and coding conversion should be done separately."
-  (with-temp-buffer
-    (set-buffer-multibyte nil)
-    (insert string)
-    (quoted-printable-decode-region (point-min) (point-max) coding-system)
-    (buffer-string)))
-
-(defun quoted-printable-encode-region (from to &optional fold class)
-  "Quoted-printable encode the region between FROM and TO per RFC 2045.
-
-If FOLD, fold long lines at 76 characters (as required by the RFC).
-If CLASS is non-nil, translate the characters not matched by that
-regexp class, which is in the form expected by `skip-chars-forward'.
-You should probably avoid non-ASCII characters in this arg.
-
-If `mm-use-ultra-safe-encoding' is set, fold lines unconditionally and
-encode lines starting with \"From\"."
-  (interactive "r")
-  (unless class
-    ;; Avoid using 8bit characters. = is \075.
-    ;; Equivalent to "^\000-\007\013\015-\037\200-\377="
-    (setq class "\010-\012\014\040-\074\076-\177"))
-  (save-excursion
-    (goto-char from)
-    (if (re-search-forward (string-to-multibyte "[^\x0-\x7f\x80-\xff]")
-                          to t)
-       (error "Multibyte character in QP encoding region"))
-    (save-restriction
-      (narrow-to-region from to)
-      ;; Encode all the non-ascii and control characters.
-      (goto-char (point-min))
-      (while (and (skip-chars-forward class)
-                 (not (eobp)))
-       (insert
-        (prog1
-            (format "=%02X" (char-after))
-          (delete-char 1))))
-      ;; Encode white space at the end of lines.
-      (goto-char (point-min))
-      (while (re-search-forward "[ \t]+$" nil t)
-       (goto-char (match-beginning 0))
-       (while (not (eolp))
-         (insert
-          (prog1
-              (format "=%02X" (char-after))
-            (delete-char 1)))))
-      (let ((ultra
-            (and (boundp 'mm-use-ultra-safe-encoding)
-                 mm-use-ultra-safe-encoding)))
-       (when (or fold ultra)
-         (let ((tab-width 1)           ; HTAB is one character.
-               (case-fold-search nil))
-           (goto-char (point-min))
-           (while (not (eobp))
-             ;; In ultra-safe mode, encode "From " at the beginning
-             ;; of a line.
-             (when ultra
-               (if (looking-at "From ")
-                   (replace-match "From=20" nil t)
-                 (if (looking-at "-")
-                     (replace-match "=2D" nil t))))
-             (end-of-line)
-             ;; Fold long lines.
-             (while (> (current-column) 76) ; tab-width must be 1.
-               (beginning-of-line)
-               (forward-char 75)       ; 75 chars plus an "="
-               (search-backward "=" (- (point) 2) t)
-               (insert "=\n")
-               (end-of-line))
-             (forward-line))))))))
-
-(defun quoted-printable-encode-string (string)
-  "Encode the STRING as quoted-printable and return the result."
-  (with-temp-buffer
-    (if (multibyte-string-p string)
-       (set-buffer-multibyte 'to)
-      (set-buffer-multibyte nil))
-    (insert string)
-    (quoted-printable-encode-region (point-min) (point-max))
-    (buffer-string)))
-
-(provide 'qp)
-
-;;; qp.el ends here
diff --git a/lisp/gnus/registry.el b/lisp/gnus/registry.el
deleted file mode 100644 (file)
index e8bc6f5..0000000
+++ /dev/null
@@ -1,379 +0,0 @@
-;;; registry.el --- Track and remember data items by various fields
-
-;; Copyright (C) 2011-2016 Free Software Foundation, Inc.
-
-;; Author: Teodor Zlatanov <tzz@lifelogs.com>
-;; Keywords: data
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This library provides a general-purpose EIEIO-based registry
-;; database with persistence, initialized with these fields:
-
-;; version: a float
-
-;; max-size: an integer, default most-positive-fixnum
-
-;; prune-factor: a float between 0 and 1, default 0.1
-
-;; precious: a list of symbols
-
-;; tracked: a list of symbols
-
-;; tracker: a hashtable tuned for 100 symbols to track (you should
-;; only access this with the :lookup2-function and the
-;; :lookup2+-function)
-
-;; data: a hashtable with default size 10K and resize threshold 2.0
-;; (this reflects the expected usage so override it if you know better)
-
-;; ...plus methods to do all the work: `registry-search',
-;; `registry-lookup', `registry-lookup-secondary',
-;; `registry-lookup-secondary-value', `registry-insert',
-;; `registry-delete', `registry-prune', `registry-size' which see
-
-;; and with the following properties:
-
-;; Every piece of data has a unique ID and some general-purpose fields
-;; (F1=D1, F2=D2, F3=(a b c)...) expressed as an alist, e.g.
-
-;; ((F1 D1) (F2 D2) (F3 a b c))
-
-;; Note that whether a field has one or many pieces of data, the data
-;; is always a list of values.
-
-;; The user decides which fields are "precious", F2 for example.  When
-;; the registry is pruned, any entries without the F2 field will be
-;; removed until the size is :max-size * :prune-factor _less_ than the
-;; maximum database size. No entries with the F2 field will be removed
-;; at PRUNE TIME, which means it may not be possible to prune back all
-;; the way to the target size.
-
-;; When an entry is inserted, the registry will reject new entries if
-;; they bring it over the :max-size limit, even if they have the F2
-;; field.
-
-;; The user decides which fields are "tracked", F1 for example.  Any
-;; new entry is then indexed by all the tracked fields so it can be
-;; quickly looked up that way.  The data is always a list (see example
-;; above) and each list element is indexed.
-
-;; Precious and tracked field names must be symbols.  All other
-;; fields can be any other Emacs Lisp types.
-
-;;; Code:
-
-(eval-when-compile (require 'cl))
-
-(require 'eieio)
-(require 'eieio-base)
-
-;; The version number needs to be kept outside of the class definition
-;; itself.  The persistent-save process does *not* write to file any
-;; slot values that are equal to the default :initform value.  If a
-;; database object is at the most recent version, therefore, its
-;; version number will not be written to file.  That makes it
-;; difficult to know when a database needs to be upgraded.
-(defvar registry-db-version 0.2
-  "The current version of the registry format.")
-
-(defclass registry-db (eieio-persistent)
-  ((version :initarg :version
-            :initform nil
-            :type (or null float)
-            :documentation "The registry version.")
-   (max-size :initarg :max-size
-            ;; EIEIO's :initform is not 100% compatible with CLOS in
-            ;; that if the form is an atom, it assumes it's constant
-            ;; value rather than an expression, so in order to get the value
-            ;; of `most-positive-fixnum', we need to use an
-            ;; expression that's not just a symbol.
-             :initform (symbol-value 'most-positive-fixnum)
-             :type integer
-             :custom integer
-             :documentation "The maximum number of registry entries.")
-   (prune-factor
-    :initarg :prune-factor
-    :initform 0.1
-    :type float
-    :custom float
-    :documentation "Prune to (:max-size * :prune-factor) less
-    than the :max-size limit.  Should be a float between 0 and 1.")
-   (tracked :initarg :tracked
-            :initform nil
-            :type t
-            :documentation "The tracked (indexed) fields, a list of symbols.")
-   (precious :initarg :precious
-             :initform nil
-             :type t
-             :documentation "The precious fields, a list of symbols.")
-   (tracker :initarg :tracker
-            :type hash-table
-            :documentation "The field tracking hashtable.")
-   (data :initarg :data
-         :type hash-table
-         :documentation "The data hashtable.")))
-
-(cl-defmethod initialize-instance :before ((this registry-db) slots)
-  "Check whether a registry object needs to be upgraded."
-  ;; Hardcoded upgrade routines.  Version 0.1 to 0.2 requires the
-  ;; :max-soft slot to disappear, and the :max-hard slot to be renamed
-  ;; :max-size.
-  (let ((current-version
-        (and (plist-member slots :version)
-             (plist-get slots :version))))
-    (when (or (null current-version)
-             (eql current-version 0.1))
-      (setq slots
-           (plist-put slots :max-size (plist-get slots :max-hard)))
-      (setq slots
-           (plist-put slots :version registry-db-version))
-      (cl-remf slots :max-hard)
-      (cl-remf slots :max-soft))))
-
-(cl-defmethod initialize-instance :after ((this registry-db) slots)
-  "Set value of data slot of THIS after initialization."
-  (with-slots (data tracker) this
-    (unless (member :data slots)
-      (setq data
-           (make-hash-table :size 10000 :rehash-size 2.0 :test 'equal)))
-    (unless (member :tracker slots)
-      (setq tracker (make-hash-table :size 100 :rehash-size 2.0)))))
-
-(cl-defmethod registry-lookup ((db registry-db) keys)
-  "Search for KEYS in the registry-db THIS.
-Returns an alist of the key followed by the entry in a list, not a cons cell."
-  (let ((data (oref db data)))
-    (delq nil
-         (mapcar
-          (lambda (k)
-            (when (gethash k data)
-              (list k (gethash k data))))
-          keys))))
-
-(cl-defmethod registry-lookup-breaks-before-lexbind ((db registry-db) keys)
-  "Search for KEYS in the registry-db THIS.
-Returns an alist of the key followed by the entry in a list, not a cons cell."
-  (let ((data (oref db data)))
-    (delq nil
-         (loop for key in keys
-               when (gethash key data)
-               collect (list key (gethash key data))))))
-
-(cl-defmethod registry-lookup-secondary ((db registry-db) tracksym
-                                        &optional create)
-  "Search for TRACKSYM in the registry-db THIS.
-When CREATE is not nil, create the secondary index hashtable if needed."
-  (let ((h (gethash tracksym (oref db tracker))))
-    (if h
-       h
-      (when create
-       (puthash tracksym
-                (make-hash-table :size 800 :rehash-size 2.0 :test 'equal)
-                (oref db tracker))
-       (gethash tracksym (oref db tracker))))))
-
-(cl-defmethod registry-lookup-secondary-value ((db registry-db) tracksym val
-                                              &optional set)
-  "Search for TRACKSYM with value VAL in the registry-db THIS.
-When SET is not nil, set it for VAL (use t for an empty list)."
-  ;; either we're asked for creation or there should be an existing index
-  (when (or set (registry-lookup-secondary db tracksym))
-    ;; set the entry if requested,
-    (when set
-      (puthash val (if (eq t set) '() set)
-              (registry-lookup-secondary db tracksym t)))
-    (gethash val (registry-lookup-secondary db tracksym))))
-
-(defun registry--match (mode entry check-list)
-  ;; for all members
-  (when check-list
-    (let ((key (nth 0 (nth 0 check-list)))
-          (vals (cdr-safe (nth 0 check-list)))
-          found)
-      (while (and key vals (not found))
-        (setq found (case mode
-                      (:member
-                       (member (car-safe vals) (cdr-safe (assoc key entry))))
-                      (:regex
-                       (string-match (car vals)
-                                     (mapconcat
-                                      'prin1-to-string
-                                      (cdr-safe (assoc key entry))
-                                      "\0"))))
-              vals (cdr-safe vals)))
-      (or found
-          (registry--match mode entry (cdr-safe check-list))))))
-
-(cl-defmethod registry-search ((db registry-db) &rest spec)
-  "Search for SPEC across the registry-db THIS.
-For example calling with `:member \\='(a 1 2)' will match entry \((a 3 1)).
-Calling with `:all t' (any non-nil value) will match all.
-Calling with `:regex \\='(a \"h.llo\")' will match entry \(a \"hullo\" \"bye\").
-The test order is to check :all first, then :member, then :regex."
-  (when db
-    (let ((all (plist-get spec :all))
-         (member (plist-get spec :member))
-         (regex (plist-get spec :regex)))
-      (loop for k being the hash-keys of (oref db data)
-           using (hash-values v)
-           when (or
-                 ;; :all non-nil returns all
-                 all
-                 ;; member matching
-                 (and member (registry--match :member v member))
-                 ;; regex matching
-                 (and regex (registry--match :regex v regex)))
-           collect k))))
-
-(cl-defmethod registry-delete ((db registry-db) keys assert &rest spec)
-  "Delete KEYS from the registry-db THIS.
-If KEYS is nil, use SPEC to do a search.
-Updates the secondary ('tracked') indices as well.
-With assert non-nil, errors out if the key does not exist already."
-  (let* ((data (oref db data))
-        (keys (or keys
-                  (apply 'registry-search db spec)))
-        (tracked (oref db tracked)))
-
-    (dolist (key keys)
-      (let ((entry (gethash key data)))
-       (when assert
-         (assert entry nil
-                 "Key %s does not exist in database" key))
-       ;; clean entry from the secondary indices
-       (dolist (tr tracked)
-         ;; is this tracked symbol indexed?
-         (when (registry-lookup-secondary db tr)
-           ;; for every value in the entry under that key...
-           (dolist (val (cdr-safe (assq tr entry)))
-             (let* ((value-keys (registry-lookup-secondary-value
-                                 db tr val)))
-               (when (member key value-keys)
-                 ;; override the previous value
-                 (registry-lookup-secondary-value
-                  db tr val
-                  ;; with the indexed keys MINUS the current key
-                  ;; (we pass t when the list is empty)
-                  (or (delete key value-keys) t)))))))
-       (remhash key data)))
-    keys))
-
-(cl-defmethod registry-size ((db registry-db))
-  "Returns the size of the registry-db object THIS.
-This is the key count of the `data' slot."
-  (hash-table-count (oref db data)))
-
-(cl-defmethod registry-full ((db registry-db))
-  "Checks if registry-db THIS is full."
-  (>= (registry-size db)
-      (oref db max-size)))
-
-(cl-defmethod registry-insert ((db registry-db) key entry)
-  "Insert ENTRY under KEY into the registry-db THIS.
-Updates the secondary ('tracked') indices as well.
-Errors out if the key exists already."
-
-  (assert (not (gethash key (oref db data))) nil
-         "Key already exists in database")
-
-  (assert (not (registry-full db))
-         nil
-         "registry max-size limit reached")
-
-  ;; store the entry
-  (puthash key entry (oref db data))
-
-  ;; store the secondary indices
-  (dolist (tr (oref db tracked))
-    ;; for every value in the entry under that key...
-    (dolist (val (cdr-safe (assq tr entry)))
-      (let* ((value-keys (registry-lookup-secondary-value db tr val)))
-       (pushnew key value-keys :test 'equal)
-       (registry-lookup-secondary-value db tr val value-keys))))
-  entry)
-
-(cl-defmethod registry-reindex ((db registry-db))
-  "Rebuild the secondary indices of registry-db THIS."
-  (let ((count 0)
-       (expected (* (length (oref db tracked)) (registry-size db))))
-    (dolist (tr (oref db tracked))
-      (let (values)
-       (maphash
-        (lambda (key v)
-          (incf count)
-          (when (and (< 0 expected)
-                     (= 0 (mod count 1000)))
-            (message "reindexing: %d of %d (%.2f%%)"
-                     count expected (/ (* 100.0 count) expected)))
-          (dolist (val (cdr-safe (assq tr v)))
-            (let* ((value-keys (registry-lookup-secondary-value db tr val)))
-              (push key value-keys)
-              (registry-lookup-secondary-value db tr val value-keys))))
-        (oref db data))))))
-
-(cl-defmethod registry-prune ((db registry-db) &optional sortfunc)
-  "Prunes the registry-db object DB.
-
-Attempts to prune the number of entries down to \(*
-:max-size :prune-factor) less than the max-size limit, so
-pruning doesn't need to happen on every save. Removes only
-entries without the :precious keys, so it may not be possible to
-reach the target limit.
-
-Entries to be pruned are first sorted using SORTFUNC.  Entries
-from the front of the list are deleted first.
-
-Returns the number of deleted entries."
-  (let ((size (registry-size db))
-       (target-size
-        (floor (- (oref db max-size)
-                  (* (oref db max-size)
-                     (oref db prune-factor)))))
-       candidates)
-    (if (registry-full db)
-       (progn
-         (setq candidates
-               (registry-collect-prune-candidates
-                db (- size target-size) sortfunc))
-         (length (registry-delete db candidates nil)))
-      0)))
-
-(cl-defmethod registry-collect-prune-candidates ((db registry-db)
-                                                limit sortfunc)
-  "Collects pruning candidates from the registry-db object DB.
-
-Proposes only entries without the :precious keys, and attempts to
-return LIMIT such candidates.  If SORTFUNC is provided, sort
-entries first and return candidates from beginning of list."
-  (let* ((precious (oref db precious))
-        (precious-p (lambda (entry-key)
-                      (cdr (memq (car entry-key) precious))))
-        (data (oref db data))
-        (candidates (cl-loop for k being the hash-keys of data
-                             using (hash-values v)
-                             when (notany precious-p v)
-                             collect (cons k v))))
-    ;; We want the full entries for sorting, but should only return a
-    ;; list of entry keys.
-    (when sortfunc
-      (setq candidates (sort candidates sortfunc)))
-    (cl-subseq (mapcar #'car candidates) 0 (min limit (length candidates)))))
-
-(provide 'registry)
-;;; registry.el ends here
diff --git a/lisp/gnus/rfc1843.el b/lisp/gnus/rfc1843.el
deleted file mode 100644 (file)
index 508629f..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-;;; rfc1843.el --- HZ (rfc1843) decoding
-
-;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
-
-;; Author: Shenghuo Zhu <zsh@cs.rochester.edu>
-;; Keywords: news HZ HZ+ mail i18n
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; Test:
-;; (rfc1843-decode-string  "~{<:Ky2;S{#,NpJ)l6HK!#~}")
-
-;;; Code:
-
-(eval-when-compile (require 'cl))
-
-(defvar rfc1843-word-regexp
-  "~\\({\\([\041-\167][\041-\176]\\| \\)+\\)\\(~}\\|$\\)")
-
-(defvar rfc1843-word-regexp-strictly
-  "~\\({\\([\041-\167][\041-\176]\\)+\\)\\(~}\\|$\\)")
-
-(defvar rfc1843-hzp-word-regexp
-  "~\\({\\([\041-\167][\041-\176]\\| \\)+\\|\
-[<>]\\([\041-\175][\041-\176]\\| \\)+\\)\\(~}\\|$\\)")
-
-(defvar rfc1843-hzp-word-regexp-strictly
-  "~\\({\\([\041-\167][\041-\176]\\)+\\|\
-[<>]\\([\041-\175][\041-\176]\\)+\\)\\(~}\\|$\\)")
-
-(defcustom rfc1843-decode-loosely nil
-  "Loosely check HZ encoding if non-nil.
-When it is set non-nil, only buffers or strings with strictly
-HZ-encoded are decoded."
-  :type 'boolean
-  :group 'mime)
-
-(defcustom rfc1843-decode-hzp t
-  "HZ+ decoding support if non-nil.
-HZ+ specification (also known as HZP) is to provide a standardized
-7-bit representation of mixed Big5, GB, and ASCII text for convenient
-e-mail transmission, news posting, etc.
-The document of HZ+ 0.78 specification can be found at
-ftp://ftp.math.psu.edu/pub/simpson/chinese/hzp/hzp.doc"
-  :type 'boolean
-  :group 'mime)
-
-(defcustom rfc1843-newsgroups-regexp "chinese\\|hz"
-  "Regexp of newsgroups in which might be HZ encoded."
-  :type 'string
-  :group 'mime)
-
-(defun rfc1843-decode-region (from to)
-  "Decode HZ in the region between FROM and TO."
-  (interactive "r")
-  (let (str firstc)
-    (save-excursion
-      (goto-char from)
-      (if (or rfc1843-decode-loosely
-             (re-search-forward (if rfc1843-decode-hzp
-                                    rfc1843-hzp-word-regexp-strictly
-                                  rfc1843-word-regexp-strictly) to t))
-         (save-restriction
-           (narrow-to-region from to)
-           (goto-char (point-min))
-           (while (re-search-forward (if rfc1843-decode-hzp
-                                         rfc1843-hzp-word-regexp
-                                       rfc1843-word-regexp) (point-max) t)
-             (setq str (buffer-substring-no-properties
-                        (match-beginning 1)
-                        (match-end 1)))
-             (setq firstc (aref str 0))
-             (insert (decode-coding-string
-                      (rfc1843-decode
-                       (prog1
-                           (substring str 1)
-                         (delete-region (match-beginning 0) (match-end 0)))
-                       firstc)
-                      (if (eq firstc ?{) 'cn-gb-2312 'cn-big5))))
-           (goto-char (point-min))
-           (while (search-forward "~" (point-max) t)
-             (cond ((eq (char-after) ?\n)
-                    (delete-char -1)
-                    (delete-char 1))
-                   ((eq (char-after) ?~)
-                    (delete-char 1)))))))))
-
-(defun rfc1843-decode-string (string)
-  "Decode HZ STRING and return the results."
-  (let ((m enable-multibyte-characters))
-    (with-temp-buffer
-      (when m
-       (set-buffer-multibyte 'to))
-      (insert string)
-      (inline
-       (rfc1843-decode-region (point-min) (point-max)))
-      (buffer-string))))
-
-(defun rfc1843-decode (word &optional firstc)
-  "Decode HZ WORD and return it."
-  (let ((i -1) (s (substring word 0)) v)
-    (if (or (not firstc) (eq firstc ?{))
-       (while (< (incf i) (length s))
-         (if (eq (setq v (aref s i)) ? ) nil
-           (aset s i (+ 128 v))))
-      (while (< (incf i) (length s))
-       (if (eq (setq v (aref s i)) ? ) nil
-         (setq v (+ (* 94 v) (aref s (1+ i)) -3135))
-         (aset s i (+ (/ v 157) (if (eq firstc ?<) 201 161)))
-         (setq v (% v 157))
-         (aset s (incf i) (+ v (if (< v 63) 64 98))))))
-    s))
-
-(provide 'rfc1843)
-
-;;; rfc1843.el ends here
diff --git a/lisp/gnus/rfc2045.el b/lisp/gnus/rfc2045.el
deleted file mode 100644 (file)
index c2ddf90..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-;;; rfc2045.el --- Functions for decoding rfc2045 headers
-
-;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
-
-;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;; RFC 2045 is: "Multipurpose Internet Mail Extensions (MIME) Part
-;; One:  Format of Internet Message Bodies".
-
-;;; Commentary:
-
-;;; Code:
-
-(require 'ietf-drums)
-
-(defun rfc2045-encode-string (param value)
-  "Return and PARAM=VALUE string encoded according to RFC2045."
-  (if (or (string-match (concat "[" ietf-drums-no-ws-ctl-token "]") value)
-         (string-match (concat "[" ietf-drums-tspecials "]") value)
-         (string-match "[ \n\t]" value)
-         (not (string-match (concat "[" ietf-drums-text-token "]") value)))
-      (concat param "=" (format "%S" value))
-    (concat param "=" value)))
-
-(provide 'rfc2045)
-
-;;; rfc2045.el ends here
diff --git a/lisp/gnus/rfc2047.el b/lisp/gnus/rfc2047.el
deleted file mode 100644 (file)
index 4cb10e5..0000000
+++ /dev/null
@@ -1,1166 +0,0 @@
-;;; rfc2047.el --- functions for encoding and decoding rfc2047 messages
-
-;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
-
-;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
-;;     MORIOKA Tomohiko <morioka@jaist.ac.jp>
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; RFC 2047 is "MIME (Multipurpose Internet Mail Extensions) Part
-;; Three:  Message Header Extensions for Non-ASCII Text".
-
-;;; Code:
-
-(eval-when-compile
-  (require 'cl))
-(defvar message-posting-charset)
-
-(require 'mm-util)
-(require 'ietf-drums)
-;; Fixme: Avoid this (used for mail-parse-charset) mm dependence on gnus.
-(require 'mail-prsvr)
-(require 'rfc2045) ;; rfc2045-encode-string
-(autoload 'mm-body-7-or-8 "mm-bodies")
-
-(defvar rfc2047-header-encoding-alist
-  '(("Newsgroups" . nil)
-    ("Followup-To" . nil)
-    ("Message-ID" . nil)
-    ("\\(Resent-\\)?\\(From\\|Cc\\|To\\|Bcc\\|\\(In-\\)?Reply-To\\|Sender\
-\\|Mail-Followup-To\\|Mail-Copies-To\\|Approved\\)" . address-mime)
-    (t . mime))
-  "*Header/encoding method alist.
-The list is traversed sequentially.  The keys can either be
-header regexps or t.
-
-The values can be:
-
-1) nil, in which case no encoding is done;
-2) `mime', in which case the header will be encoded according to RFC2047;
-3) `address-mime', like `mime', but takes account of the rules for address
-   fields (where quoted strings and comments must be treated separately);
-4) a charset, in which case it will be encoded as that charset;
-5) `default', in which case the field will be encoded as the rest
-   of the article.")
-
-(defvar rfc2047-charset-encoding-alist
-  '((us-ascii . nil)
-    (iso-8859-1 . Q)
-    (iso-8859-2 . Q)
-    (iso-8859-3 . Q)
-    (iso-8859-4 . Q)
-    (iso-8859-5 . B)
-    (koi8-r . B)
-    (iso-8859-7 . B)
-    (iso-8859-8 . B)
-    (iso-8859-9 . Q)
-    (iso-8859-14 . Q)
-    (iso-8859-15 . Q)
-    (iso-2022-jp . B)
-    (iso-2022-kr . B)
-    (gb2312 . B)
-    (gbk . B)
-    (gb18030 . B)
-    (big5 . B)
-    (cn-big5 . B)
-    (cn-gb . B)
-    (cn-gb-2312 . B)
-    (euc-kr . B)
-    (iso-2022-jp-2 . B)
-    (iso-2022-int-1 . B)
-    (viscii . Q))
-  "Alist of MIME charsets to RFC2047 encodings.
-Valid encodings are nil, `Q' and `B'.  These indicate binary (no) encoding,
-quoted-printable and base64 respectively.")
-
-(defvar rfc2047-encode-function-alist
-  '((Q . rfc2047-q-encode-string)
-    (B . rfc2047-b-encode-string)
-    (nil . identity))
-  "Alist of RFC2047 encodings to encoding functions.")
-
-(defvar rfc2047-encode-encoded-words t
-  "Whether encoded words should be encoded again.")
-
-(defvar rfc2047-allow-irregular-q-encoded-words t
-  "*Whether to decode irregular Q-encoded words.")
-
-(eval-and-compile ;; Necessary to hard code them in `rfc2047-decode-region'.
-  (defconst rfc2047-encoded-word-regexp
-    "=\\?\\([^][\000-\040()<>@,;:*\\\"/?.=]+\\)\\(?:\\*[^?]+\\)?\\?\
-\\(B\\?[+/0-9A-Za-z]*=*\
-\\|Q\\?[ ->@-~]*\
-\\)\\?="
-    "Regexp that matches encoded word."
-    ;; The patterns for the B encoding and the Q encoding, i.e. the ones
-    ;; beginning with "B" and "Q" respectively, are restricted into only
-    ;; the characters that those encodings may generally use.
-    )
-  (defconst rfc2047-encoded-word-regexp-loose
-    "=\\?\\([^][\000-\040()<>@,;:*\\\"/?.=]+\\)\\(?:\\*[^?]+\\)?\\?\
-\\(B\\?[+/0-9A-Za-z]*=*\
-\\|Q\\?\\(?:\\?+[ -<>@-~]\\)?\\(?:[ ->@-~]+\\?+[ -<>@-~]\\)*[ ->@-~]*\\?*\
-\\)\\?="
-    "Regexp that matches encoded word allowing loose Q encoding."
-    ;; The pattern for the Q encoding, i.e. the one beginning with "Q",
-    ;; is similar to:
-    ;; "Q\\?\\(\\?+[^\n=?]\\)?\\([^\n?]+\\?+[^\n=?]\\)*[^\n?]*\\?*"
-    ;;      <--------1-------><----------2,3----------><--4--><-5->
-    ;; They mean:
-    ;; 1. After "Q?", allow "?"s that follow a character other than "=".
-    ;; 2. Allow "=" after "Q?"; it isn't regarded as the terminator.
-    ;; 3. In the middle of an encoded word, allow "?"s that follow a
-    ;;    character other than "=".
-    ;; 4. Allow any characters other than "?" in the middle of an
-    ;;    encoded word.
-    ;; 5. At the end, allow "?"s.
-    ))
-
-;;;
-;;; Functions for encoding RFC2047 messages
-;;;
-
-(defun rfc2047-qp-or-base64 ()
-  "Return the type with which to encode the buffer.
-This is either `base64' or `quoted-printable'."
-  (save-excursion
-    (let ((limit (min (point-max) (+ 2000 (point-min))))
-         (n8bit 0))
-      (goto-char (point-min))
-      (skip-chars-forward "\x20-\x7f\r\n\t" limit)
-      (while (< (point) limit)
-       (incf n8bit)
-       (forward-char 1)
-       (skip-chars-forward "\x20-\x7f\r\n\t" limit))
-      (if (or (< (* 6 n8bit) (- limit (point-min)))
-             ;; Don't base64, say, a short line with a single
-             ;; non-ASCII char when splitting parts by charset.
-             (= n8bit 1))
-         'quoted-printable
-       'base64))))
-
-(defun rfc2047-narrow-to-field ()
-  "Narrow the buffer to the header on the current line."
-  (beginning-of-line)
-  (narrow-to-region
-   (point)
-   (progn
-     (forward-line 1)
-     (if (re-search-forward "^[^ \n\t]" nil t)
-        (point-at-bol)
-       (point-max))))
-  (goto-char (point-min)))
-
-(defun rfc2047-field-value ()
-  "Return the value of the field at point."
-  (save-excursion
-    (save-restriction
-      (rfc2047-narrow-to-field)
-      (re-search-forward ":[ \t\n]*" nil t)
-      (buffer-substring-no-properties (point) (point-max)))))
-
-(defun rfc2047-quote-special-characters-in-quoted-strings (&optional
-                                                          encodable-regexp)
-  "Quote special characters with `\\'s in quoted strings.
-Quoting will not be done in a quoted string if it contains characters
-matching ENCODABLE-REGEXP or it is within parentheses."
-  (goto-char (point-min))
-  (let ((tspecials (concat "[" ietf-drums-tspecials "]"))
-       (start (point))
-       beg end)
-    (with-syntax-table (standard-syntax-table)
-      (while (not (eobp))
-       (if (ignore-errors
-             (forward-list 1)
-             (eq (char-before) ?\)))
-           (forward-list -1)
-         (goto-char (point-max)))
-       (save-restriction
-         (narrow-to-region start (point))
-         (goto-char start)
-         (while (search-forward "\"" nil t)
-           (setq beg (match-beginning 0))
-           (unless (eq (char-before beg) ?\\)
-             (goto-char beg)
-             (setq beg (1+ beg))
-             (condition-case nil
-                 (progn
-                   (forward-sexp)
-                   (setq end (1- (point)))
-                   (goto-char beg)
-                   (if (and encodable-regexp
-                            (re-search-forward encodable-regexp end t))
-                       (goto-char (1+ end))
-                     (save-restriction
-                       (narrow-to-region beg end)
-                       (while (re-search-forward tspecials nil 'move)
-                         (if (eq (char-before) ?\\)
-                             (if (looking-at tspecials) ;; Already quoted.
-                                 (forward-char)
-                               (insert "\\"))
-                           (goto-char (match-beginning 0))
-                           (insert "\\")
-                           (forward-char))))
-                     (forward-char)))
-               (error
-                (goto-char beg)))))
-         (goto-char (point-max)))
-       (forward-list 1)
-       (setq start (point))))))
-
-(defvar rfc2047-encoding-type 'address-mime
-  "The type of encoding done by `rfc2047-encode-region'.
-This should be dynamically bound around calls to
-`rfc2047-encode-region' to either `mime' or `address-mime'.  See
-`rfc2047-header-encoding-alist', for definitions.")
-
-(defun rfc2047-encode-message-header ()
-  "Encode the message header according to `rfc2047-header-encoding-alist'.
-Should be called narrowed to the head of the message."
-  (interactive "*")
-  (save-excursion
-    (goto-char (point-min))
-    (let (alist elem method charsets)
-      (while (not (eobp))
-       (save-restriction
-         (rfc2047-narrow-to-field)
-         (setq method nil
-               alist rfc2047-header-encoding-alist
-               charsets (mm-find-mime-charset-region (point-min) (point-max)))
-         ;; M$ Outlook boycotts decoding of a header if it consists
-         ;; of two or more encoded words and those charsets differ;
-         ;; it seems to decode all words in a header from a charset
-         ;; found first in the header.  So, we unify the charsets into
-         ;; a single one used for encoding the whole text in a header.
-         (let ((mm-coding-system-priorities
-                (if (= (length charsets) 1)
-                    (cons (mm-charset-to-coding-system (car charsets))
-                          mm-coding-system-priorities)
-                  mm-coding-system-priorities)))
-           (while (setq elem (pop alist))
-             (when (or (and (stringp (car elem))
-                            (looking-at (car elem)))
-                       (eq (car elem) t))
-               (setq alist nil
-                     method (cdr elem))))
-           (if (not (rfc2047-encodable-p))
-               (prog2
-                   (when (eq method 'address-mime)
-                     (rfc2047-quote-special-characters-in-quoted-strings))
-                   (if (and (eq (mm-body-7-or-8) '8bit)
-                            (mm-multibyte-p)
-                            (mm-coding-system-p
-                             (car message-posting-charset)))
-                       ;; 8 bit must be decoded.
-                       (encode-coding-region
-                        (point-min) (point-max)
-                        (mm-charset-to-coding-system
-                         (car message-posting-charset))))
-                 ;; No encoding necessary, but folding is nice
-                 (when nil
-                   (rfc2047-fold-region
-                    (save-excursion
-                      (goto-char (point-min))
-                      (skip-chars-forward "^:")
-                      (when (looking-at ": ")
-                        (forward-char 2))
-                      (point))
-                    (point-max))))
-             ;; We found something that may perhaps be encoded.
-             (re-search-forward "^[^:]+: *" nil t)
-             (cond
-              ((eq method 'address-mime)
-               (rfc2047-encode-region (point) (point-max)))
-              ((eq method 'mime)
-               (let ((rfc2047-encoding-type 'mime))
-                 (rfc2047-encode-region (point) (point-max))))
-              ((eq method 'default)
-               (if (and (default-value 'enable-multibyte-characters)
-                        mail-parse-charset)
-                   (encode-coding-region (point) (point-max)
-                                         mail-parse-charset)))
-              ;; We get this when CC'ing messages to newsgroups with
-              ;; 8-bit names.  The group name mail copy just got
-              ;; unconditionally encoded.  Previously, it would ask
-              ;; whether to encode, which was quite confusing for the
-              ;; user.  If the new behavior is wrong, tell me.  I have
-              ;; left the old code commented out below.
-              ;; -- Per Abrahamsen <abraham@dina.kvl.dk> Date: 2001-10-07.
-              ;; Modified by Dave Love, with the commented-out code changed
-              ;; in accordance with changes elsewhere.
-              ((null method)
-               (rfc2047-encode-region (point) (point-max)))
-;;;           ((null method)
-;;;            (if (or (message-options-get
-;;;                     'rfc2047-encode-message-header-encode-any)
-;;;                    (message-options-set
-;;;                     'rfc2047-encode-message-header-encode-any
-;;;                     (y-or-n-p
-;;;                      "Some texts are not encoded. Encode anyway?")))
-;;;                (rfc2047-encode-region (point-min) (point-max))
-;;;              (error "Cannot send unencoded text")))
-              ((mm-coding-system-p method)
-               (when (default-value 'enable-multibyte-characters)
-                 (encode-coding-region (point) (point-max) method)))
-              ;; Hm.
-              (t)))
-           (goto-char (point-max))))))))
-
-;; Fixme: This, and the require below may not be the Right Thing, but
-;; should be safe just before release.  -- fx 2001-02-08
-
-(defun rfc2047-encodable-p ()
-  "Return non-nil if any characters in current buffer need encoding in headers.
-The buffer may be narrowed."
-  (require 'message)                   ; for message-posting-charset
-  (let ((charsets
-        (mm-find-mime-charset-region (point-min) (point-max))))
-    (goto-char (point-min))
-    (or (and rfc2047-encode-encoded-words
-            (prog1
-                (re-search-forward rfc2047-encoded-word-regexp nil t)
-              (goto-char (point-min))))
-       (and charsets
-            (not (equal charsets (list (car message-posting-charset))))))))
-
-;; Use this syntax table when parsing into regions that may need
-;; encoding.  Double quotes are string delimiters, backslash is
-;; character quoting, and all other RFC 2822 special characters are
-;; treated as punctuation so we can use forward-sexp/forward-word to
-;; skip to the end of regions appropriately.  Nb. ietf-drums does
-;; things differently.
-(defconst rfc2047-syntax-table
-  ;; (make-char-table 'syntax-table '(2)) only works in Emacs.
-  (let ((table (make-syntax-table)))
-    ;; The following is done to work for setting all elements of the table;
-    ;; it appears to be the cleanest way.
-    ;; Play safe and don't assume the form of the word syntax entry --
-    ;; copy it from ?a.
-    (set-char-table-range table t (aref (standard-syntax-table) ?a))
-    (modify-syntax-entry ?\\ "\\" table)
-    (modify-syntax-entry ?\" "\"" table)
-    (modify-syntax-entry ?\( "(" table)
-    (modify-syntax-entry ?\) ")" table)
-    (modify-syntax-entry ?\< "." table)
-    (modify-syntax-entry ?\> "." table)
-    (modify-syntax-entry ?\[ "." table)
-    (modify-syntax-entry ?\] "." table)
-    (modify-syntax-entry ?: "." table)
-    (modify-syntax-entry ?\; "." table)
-    (modify-syntax-entry ?, "." table)
-    (modify-syntax-entry ?@ "." table)
-    table))
-
-(defun rfc2047-encode-region (b e &optional dont-fold)
-  "Encode words in region B to E that need encoding.
-By default, the region is treated as containing RFC2822 addresses.
-Dynamically bind `rfc2047-encoding-type' to change that."
-  (save-restriction
-    (narrow-to-region b e)
-    (let ((encodable-regexp (if rfc2047-encode-encoded-words
-                               "[^\000-\177]+\\|=\\?"
-                             "[^\000-\177]+"))
-         start                         ; start of current token
-         end begin csyntax
-         ;; Whether there's an encoded word before the current token,
-         ;; either immediately or separated by space.
-         last-encoded
-         (orig-text (buffer-substring-no-properties b e)))
-      (if (eq 'mime rfc2047-encoding-type)
-         ;; Simple case.  Continuous words in which all those contain
-         ;; non-ASCII characters are encoded collectively.  Encoding
-         ;; ASCII words, including `Re:' used in Subject headers, is
-         ;; avoided for interoperability with non-MIME clients and
-         ;; for making it easy to find keywords.
-         (progn
-           (goto-char (point-min))
-           (while (progn (skip-chars-forward " \t\n")
-                         (not (eobp)))
-             (setq start (point))
-             (while (and (looking-at "[ \t\n]*\\([^ \t\n]+\\)")
-                         (progn
-                           (setq end (match-end 0))
-                           (re-search-forward encodable-regexp end t)))
-               (goto-char end))
-             (if (> (point) start)
-                 (rfc2047-encode start (point))
-               (goto-char end))))
-       ;; `address-mime' case -- take care of quoted words, comments.
-       (rfc2047-quote-special-characters-in-quoted-strings encodable-regexp)
-       (with-syntax-table rfc2047-syntax-table
-         (goto-char (point-min))
-         (condition-case err           ; in case of unbalanced quotes
-             ;; Look for rfc2822-style: sequences of atoms, quoted
-             ;; strings, specials, whitespace.  (Specials mustn't be
-             ;; encoded.)
-             (while (not (eobp))
-               ;; Skip whitespace.
-               (skip-chars-forward " \t\n")
-               (setq start (point))
-               (cond
-                ((not (char-after)))   ; eob
-                ;; else token start
-                ((eq ?\" (setq csyntax (char-syntax (char-after))))
-                 ;; Quoted word.
-                 (forward-sexp)
-                 (setq end (point))
-                 ;; Does it need encoding?
-                 (goto-char start)
-                 (if (re-search-forward encodable-regexp end 'move)
-                     ;; It needs encoding.  Strip the quotes first,
-                     ;; since encoded words can't occur in quotes.
-                     (progn
-                       (goto-char end)
-                       (delete-char -1)
-                       (goto-char start)
-                       (delete-char 1)
-                       (when last-encoded
-                         ;; There was a preceding quoted word.  We need
-                         ;; to include any separating whitespace in this
-                         ;; word to avoid it getting lost.
-                         (skip-chars-backward " \t")
-                         ;; A space is needed between the encoded words.
-                         (insert ? )
-                         (setq start (point)
-                               end (1+ end)))
-                       ;; Adjust the end position for the deleted quotes.
-                       (rfc2047-encode start (- end 2))
-                       (setq last-encoded t)) ; record that it was encoded
-                   (setq last-encoded  nil)))
-                ((eq ?. csyntax)
-                 ;; Skip other delimiters, but record that they've
-                 ;; potentially separated quoted words.
-                 (forward-char)
-                 (setq last-encoded nil))
-                ((eq ?\) csyntax)
-                 (error "Unbalanced parentheses"))
-                ((eq ?\( csyntax)
-                 ;; Look for the end of parentheses.
-                 (forward-list)
-                 ;; Encode text as an unstructured field.
-                 (let ((rfc2047-encoding-type 'mime))
-                   (rfc2047-encode-region (1+ start) (1- (point))))
-                 (skip-chars-forward ")"))
-                (t                 ; normal token/whitespace sequence
-                 ;; Find the end.
-                 ;; Skip one ASCII word, or encode continuous words
-                 ;; in which all those contain non-ASCII characters.
-                 (setq end nil)
-                 (while (not (or end (eobp)))
-                   (when (looking-at "[\000-\177]+")
-                     (setq begin (point)
-                           end (match-end 0))
-                     (when (progn
-                             (while (and (or (re-search-forward
-                                              "[ \t\n]\\|\\Sw" end 'move)
-                                             (setq end nil))
-                                         (eq ?\\ (char-syntax (char-before))))
-                               ;; Skip backslash-quoted characters.
-                               (forward-char))
-                             end)
-                       (setq end (match-beginning 0))
-                       (if rfc2047-encode-encoded-words
-                           (progn
-                             (goto-char begin)
-                             (when (search-forward "=?" end 'move)
-                               (goto-char (match-beginning 0))
-                               (setq end nil)))
-                         (goto-char end))))
-                   ;; Where the value nil of `end' means there may be
-                   ;; text to have to be encoded following the point.
-                   ;; Otherwise, the point reached to the end of ASCII
-                   ;; words separated by whitespace or a special char.
-                   (unless end
-                     (when (looking-at encodable-regexp)
-                       (goto-char (setq begin (match-end 0)))
-                       (while (and (looking-at "[ \t\n]+\\([^ \t\n]+\\)")
-                                   (setq end (match-end 0))
-                                   (progn
-                                     (while (re-search-forward
-                                             encodable-regexp end t))
-                                     (< begin (point)))
-                                   (goto-char begin)
-                                   (or (not (re-search-forward "\\Sw" end t))
-                                       (progn
-                                         (goto-char (match-beginning 0))
-                                         nil)))
-                         (goto-char end))
-                       (when (looking-at "[^ \t\n]+")
-                         (setq end (match-end 0))
-                         (if (re-search-forward "\\Sw+" end t)
-                             ;; There are special characters better
-                             ;; to be encoded so that MTAs may parse
-                             ;; them safely.
-                             (cond ((= end (point)))
-                                   ((looking-at (concat "\\sw*\\("
-                                                        encodable-regexp
-                                                        "\\)"))
-                                    (setq end nil))
-                                   (t
-                                    (goto-char (1- (match-end 0)))
-                                    (unless (= (point) (match-beginning 0))
-                                      ;; Separate encodable text and
-                                      ;; delimiter.
-                                      (insert " "))))
-                           (goto-char end)
-                           (skip-chars-forward " \t\n")
-                           (if (and (looking-at "[^ \t\n]+")
-                                    (string-match encodable-regexp
-                                                  (match-string 0)))
-                               (setq end nil)
-                             (goto-char end)))))))
-                 (skip-chars-backward " \t\n")
-                 (setq end (point))
-                 (goto-char start)
-                 (if (re-search-forward encodable-regexp end 'move)
-                     (progn
-                       (unless (memq (char-before start) '(nil ?\t ? ))
-                         (if (progn
-                               (goto-char start)
-                               (skip-chars-backward "^ \t\n")
-                               (and (looking-at "\\Sw+")
-                                    (= (match-end 0) start)))
-                             ;; Also encode bogus delimiters.
-                             (setq start (point))
-                           ;; Separate encodable text and delimiter.
-                           (goto-char start)
-                           (insert " ")
-                           (setq start (1+ start)
-                                 end (1+ end))))
-                       (rfc2047-encode start end)
-                       (setq last-encoded t))
-                   (setq last-encoded nil)))))
-           (error
-            (if (or debug-on-quit debug-on-error)
-                (signal (car err) (cdr err))
-              (error "Invalid data for rfc2047 encoding: %s"
-                     (replace-regexp-in-string "[ \t\n]+" " " orig-text))))))))
-    (unless dont-fold
-      (rfc2047-fold-region b (point)))
-    (goto-char (point-max))))
-
-(defun rfc2047-encode-string (string &optional dont-fold)
-  "Encode words in STRING.
-By default, the string is treated as containing addresses (see
-`rfc2047-encoding-type')."
-  (mm-with-multibyte-buffer
-    (insert string)
-    (rfc2047-encode-region (point-min) (point-max) dont-fold)
-    (buffer-string)))
-
-;; From RFC 2047:
-;; 2. Syntax of encoded-words
-;;    [...]
-;;    While there is no limit to the length of a multiple-line header
-;;    field, each line of a header field that contains one or more
-;;    'encoded-word's is limited to 76 characters.
-;;
-;; In `rfc2047-encode-parameter' it is bound to nil, so don't defconst it.
-(defvar rfc2047-encode-max-chars 76
-  "Maximum characters of each header line that contain encoded-words.
-According to RFC 2047, it is 76.  If it is nil, encoded-words
-will not be folded.  Too small value may cause an error.  You
-should not change this value.")
-
-(defun rfc2047-encode-1 (column string cs encoder start crest tail
-                               &optional eword)
-  "Subroutine used by `rfc2047-encode'."
-  (cond ((string-equal string "")
-        (or eword ""))
-       ((not rfc2047-encode-max-chars)
-        (concat start
-                (funcall encoder (if cs
-                                     (encode-coding-string string cs)
-                                   string))
-                "?="))
-       ((>= column rfc2047-encode-max-chars)
-        (when eword
-          (cond ((string-match "\n[ \t]+\\'" eword)
-                 ;; Remove a superfluous empty line.
-                 (setq eword (substring eword 0 (match-beginning 0))))
-                ((string-match "(+\\'" eword)
-                 ;; Break the line before the open parenthesis.
-                 (setq crest (concat crest (match-string 0 eword))
-                       eword (substring eword 0 (match-beginning 0))))))
-        (rfc2047-encode-1 (length crest) string cs encoder start " " tail
-                          (concat eword "\n" crest)))
-       (t
-        (let ((index 0)
-              (limit (1- (length string)))
-              (prev "")
-              next len)
-          (while (and prev
-                      (<= index limit))
-            (setq next (concat start
-                               (funcall encoder
-                                        (if cs
-                                            (encode-coding-string
-                                             (substring string 0 (1+ index))
-                                             cs)
-                                          (substring string 0 (1+ index))))
-                               "?=")
-                  len (+ column (length next)))
-            (if (> len rfc2047-encode-max-chars)
-                (setq next prev
-                      prev nil)
-              (if (or (< index limit)
-                      (<= (+ len (or (string-match "\n" tail)
-                                     (length tail)))
-                          rfc2047-encode-max-chars))
-                  (setq prev next
-                        index (1+ index))
-                (if (string-match "\\`)+" tail)
-                    ;; Break the line after the close parenthesis.
-                    (setq tail (concat (substring tail 0 (match-end 0))
-                                       "\n "
-                                       (substring tail (match-end 0)))
-                          prev next
-                          index (1+ index))
-                  (setq next prev
-                        prev nil)))))
-          (if (> index limit)
-              (concat eword next tail)
-            (if (= 0 index)
-                (if (and eword
-                         (string-match "(+\\'" eword))
-                    (setq crest (concat crest (match-string 0 eword))
-                          eword (substring eword 0 (match-beginning 0)))
-                  (setq eword (concat eword next)))
-              (setq crest " "
-                    eword (concat eword next)))
-            (when (string-match "\n[ \t]+\\'" eword)
-              ;; Remove a superfluous empty line.
-              (setq eword (substring eword 0 (match-beginning 0))))
-            (rfc2047-encode-1 (length crest) (substring string index)
-                              cs encoder start " " tail
-                              (concat eword "\n" crest)))))))
-
-(defun rfc2047-encode (b e)
-  "Encode the word(s) in the region B to E.
-Point moves to the end of the region."
-  (let ((mime-charset (or (mm-find-mime-charset-region b e) (list 'us-ascii)))
-       cs encoding tail crest eword)
-    ;; Use utf-8 as a last resort if determining charset of text fails.
-    (if (memq nil mime-charset)
-       (setq mime-charset (list 'utf-8)))
-    (cond ((> (length mime-charset) 1)
-          (error "Can't rfc2047-encode `%s'"
-                 (buffer-substring-no-properties b e)))
-         ((= (length mime-charset) 1)
-          (setq mime-charset (car mime-charset)
-                cs (mm-charset-to-coding-system mime-charset))
-          (unless (and (mm-multibyte-p)
-                       (mm-coding-system-p cs))
-            (setq cs nil))
-          (save-restriction
-            (narrow-to-region b e)
-            (setq encoding
-                  (or (cdr (assq mime-charset
-                                 rfc2047-charset-encoding-alist))
-                      ;; For the charsets that don't have a preferred
-                      ;; encoding, choose the one that's shorter.
-                      (if (eq (rfc2047-qp-or-base64) 'base64)
-                          'B
-                        'Q)))
-            (widen)
-            (goto-char e)
-            (skip-chars-forward "^ \t\n")
-            ;; `tail' may contain a close parenthesis.
-            (setq tail (buffer-substring-no-properties e (point)))
-            (goto-char b)
-            (setq b (point-marker)
-                  e (set-marker (make-marker) e))
-            (rfc2047-fold-region (point-at-bol) b)
-            (goto-char b)
-            (skip-chars-backward "^ \t\n")
-            (unless (= 0 (skip-chars-backward " \t"))
-              ;; `crest' may contain whitespace and an open parenthesis.
-              (setq crest (buffer-substring-no-properties (point) b)))
-            (setq eword (rfc2047-encode-1
-                         (- b (point-at-bol))
-                         (replace-regexp-in-string
-                          "\n\\([ \t]?\\)" "\\1"
-                          (buffer-substring-no-properties b e))
-                         cs
-                         (or (cdr (assq encoding
-                                        rfc2047-encode-function-alist))
-                             'identity)
-                         (concat "=?" (downcase (symbol-name mime-charset))
-                                 "?" (upcase (symbol-name encoding)) "?")
-                         (or crest " ")
-                         tail))
-            (delete-region (if (eq (aref eword 0) ?\n)
-                               (if (bolp)
-                                   ;; The line was folded before encoding.
-                                   (1- (point))
-                                 (point))
-                             (goto-char b))
-                           (+ e (length tail)))
-            ;; `eword' contains `crest' and `tail'.
-            (insert eword)
-            (set-marker b nil)
-            (set-marker e nil)
-            (unless (or (/= 0 (length tail))
-                        (eobp)
-                        (looking-at "[ \t\n)]"))
-              (insert " "))))
-         (t
-          (goto-char e)))))
-
-(defun rfc2047-fold-field ()
-  "Fold the current header field."
-  (save-excursion
-    (save-restriction
-      (rfc2047-narrow-to-field)
-      (rfc2047-fold-region (point-min) (point-max)))))
-
-(defun rfc2047-fold-region (b e)
-  "Fold long lines in region B to E."
-  (save-restriction
-    (narrow-to-region b e)
-    (goto-char (point-min))
-    (let ((break nil)
-         (qword-break nil)
-         (first t)
-         (bol (save-restriction
-                (widen)
-                (point-at-bol))))
-      (while (not (eobp))
-       (when (and (or break qword-break)
-                  (> (- (point) bol) 76))
-         (goto-char (or break qword-break))
-         (setq break nil
-               qword-break nil)
-         (skip-chars-backward " \t")
-         (if (looking-at "[ \t]")
-             (insert ?\n)
-           (insert "\n "))
-         (setq bol (1- (point)))
-         ;; Don't break before the first non-LWSP characters.
-         (skip-chars-forward " \t")
-         (unless (eobp)
-           (forward-char 1)))
-       (cond
-        ((eq (char-after) ?\n)
-         (forward-char 1)
-         (setq bol (point)
-               break nil
-               qword-break nil)
-         (skip-chars-forward " \t")
-         (unless (or (eobp) (eq (char-after) ?\n))
-           (forward-char 1)))
-        ((eq (char-after) ?\r)
-         (forward-char 1))
-        ((memq (char-after) '(?  ?\t))
-         (skip-chars-forward " \t")
-         (unless first ;; Don't break just after the header name.
-           (setq break (point))))
-        ((not break)
-         (if (not (looking-at "=\\?[^=]"))
-             (if (eq (char-after) ?=)
-                 (forward-char 1)
-               (skip-chars-forward "^ \t\n\r="))
-           ;; Don't break at the start of the field.
-           (unless (= (point) b)
-             (setq qword-break (point)))
-           (skip-chars-forward "^ \t\n\r")))
-        (t
-         (skip-chars-forward "^ \t\n\r")))
-       (setq first nil))
-      (when (and (or break qword-break)
-                (> (- (point) bol) 76))
-       (goto-char (or break qword-break))
-       (setq break nil
-             qword-break nil)
-       (if (or (> 0 (skip-chars-backward " \t"))
-               (looking-at "[ \t]"))
-           (insert ?\n)
-         (insert "\n "))
-       (setq bol (1- (point)))
-       ;; Don't break before the first non-LWSP characters.
-       (skip-chars-forward " \t")
-       (unless (eobp)
-         (forward-char 1))))))
-
-(defun rfc2047-unfold-field ()
-  "Fold the current line."
-  (save-excursion
-    (save-restriction
-      (rfc2047-narrow-to-field)
-      (rfc2047-unfold-region (point-min) (point-max)))))
-
-(defun rfc2047-unfold-region (b e)
-  "Unfold lines in region B to E."
-  (save-restriction
-    (narrow-to-region b e)
-    (goto-char (point-min))
-    (let ((bol (save-restriction
-                (widen)
-                (point-at-bol)))
-         (eol (point-at-eol)))
-      (forward-line 1)
-      (while (not (eobp))
-       (if (and (looking-at "[ \t]")
-                (< (- (point-at-eol) bol) 76))
-           (delete-region eol (progn
-                                (goto-char eol)
-                                (skip-chars-forward "\r\n")
-                                (point)))
-         (setq bol (point-at-bol)))
-       (setq eol (point-at-eol))
-       (forward-line 1)))))
-
-(defun rfc2047-b-encode-string (string)
-  "Base64-encode the header contained in STRING."
-  (base64-encode-string string t))
-
-(autoload 'quoted-printable-encode-region "qp")
-
-(defun rfc2047-q-encode-string (string)
-  "Quoted-printable-encode the header in STRING."
-  (mm-with-unibyte-buffer
-    (insert string)
-    (quoted-printable-encode-region
-     (point-min) (point-max) nil
-     ;; = (\075), _ (\137), ? (\077) are used in the encoded word.
-     ;; Avoid using 8bit characters.
-     ;; This list excludes `especials' (see the RFC2047 syntax),
-     ;; meaning that some characters in non-structured fields will
-     ;; get encoded when they con't need to be.  The following is
-     ;; what it used to be.
-     ;;;  ;; Equivalent to "^\000-\007\011\013\015-\037\200-\377=_?"
-     ;;;  "\010\012\014\040-\074\076\100-\136\140-\177")
-     "-\b\n\f !#-'*+0-9A-Z\\^`-~\d")
-    (subst-char-in-region (point-min) (point-max) ?  ?_)
-    (buffer-string)))
-
-(defun rfc2047-encode-parameter (param value)
-  "Return and PARAM=VALUE string encoded in the RFC2047-like style.
-This is a substitution for the `rfc2231-encode-string' function, that
-is the standard but many mailers don't support it."
-  (let ((rfc2047-encoding-type 'mime)
-       (rfc2047-encode-max-chars nil))
-    (rfc2045-encode-string param (rfc2047-encode-string value t))))
-
-;;;
-;;; Functions for decoding RFC2047 messages
-;;;
-
-(defvar rfc2047-quote-decoded-words-containing-tspecials nil
-  "If non-nil, quote decoded words containing special characters.")
-
-(defvar rfc2047-allow-incomplete-encoded-text t
-  "*Non-nil means allow incomplete encoded-text in successive encoded-words.
-Dividing of encoded-text in the place other than character boundaries
-violates RFC2047 section 5, while we have a capability to decode it.
-If it is non-nil, the decoder will decode B- or Q-encoding in each
-encoded-word, concatenate them, and decode it by charset.  Otherwise,
-the decoder will fully decode each encoded-word before concatenating
-them.")
-
-(defun rfc2047-strip-backslashes-in-quoted-strings ()
-  "Strip backslashes in quoted strings.  `\\\"' remains."
-  (goto-char (point-min))
-  (let (beg)
-    (with-syntax-table (standard-syntax-table)
-      (while (search-forward "\"" nil t)
-       (unless (eq (char-before) ?\\)
-         (setq beg (match-end 0))
-         (goto-char (match-beginning 0))
-         (condition-case nil
-             (progn
-               (forward-sexp)
-               (save-restriction
-                 (narrow-to-region beg (1- (point)))
-                 (goto-char beg)
-                 (while (search-forward "\\" nil 'move)
-                   (unless (memq (char-after) '(?\"))
-                     (delete-char -1))
-                   (forward-char)))
-               (forward-char))
-           (error
-            (goto-char beg))))))))
-
-(defun rfc2047-charset-to-coding-system (charset &optional allow-override)
-  "Return coding-system corresponding to MIME CHARSET.
-If your Emacs implementation can't decode CHARSET, return nil.
-
-If allow-override is given, use `mm-charset-override-alist' to
-map undesired charset names to their replacement.  This should
-only be used for decoding, not for encoding."
-  (when (stringp charset)
-    (setq charset (intern (downcase charset))))
-  (when (or (not charset)
-           (eq 'gnus-all mail-parse-ignored-charsets)
-           (memq 'gnus-all mail-parse-ignored-charsets)
-           (memq charset mail-parse-ignored-charsets))
-    (setq charset mail-parse-charset))
-  (let ((cs (mm-charset-to-coding-system charset nil allow-override)))
-    (cond ((eq cs 'ascii)
-          (setq cs (or (mm-charset-to-coding-system mail-parse-charset)
-                       'raw-text)))
-         ((mm-coding-system-p cs))
-         ((and charset
-               (listp mail-parse-ignored-charsets)
-               (memq 'gnus-unknown mail-parse-ignored-charsets))
-          (setq cs (mm-charset-to-coding-system mail-parse-charset))))
-    (if (eq cs 'ascii)
-       'raw-text
-      cs)))
-
-(autoload 'quoted-printable-decode-string "qp")
-
-(defun rfc2047-decode-encoded-words (words)
-  "Decode successive encoded-words in WORDS and return a decoded string.
-Each element of WORDS looks like (CHARSET ENCODING ENCODED-TEXT
-ENCODED-WORD)."
-  (let (word charset cs encoding text rest)
-    (while words
-      (setq word (pop words))
-      (if (and (setq cs (rfc2047-charset-to-coding-system
-                        (setq charset (car word)) t))
-              (condition-case code
-                  (cond ((char-equal ?B (nth 1 word))
-                         (setq text (base64-decode-string
-                                     (rfc2047-pad-base64 (nth 2 word)))))
-                        ((char-equal ?Q (nth 1 word))
-                         (setq text (quoted-printable-decode-string
-                                     (subst-char-in-string
-                                      ?_ ?  (nth 2 word) t)))))
-                (error
-                 (message "%s" (error-message-string code))
-                 nil)))
-         (if (and rfc2047-allow-incomplete-encoded-text
-                  (eq cs (caar rest)))
-             ;; Concatenate text of which the charset is the same.
-             (setcdr (car rest) (concat (cdar rest) text))
-           (push (cons cs text) rest))
-       ;; Don't decode encoded-word.
-       (push (cons nil (nth 3 word)) rest)))
-    (while rest
-      (setq words (concat
-                  (or (and (setq cs (caar rest))
-                           (condition-case code
-                               (decode-coding-string (cdar rest) cs)
-                             (error
-                              (message "%s" (error-message-string code))
-                              nil)))
-                      (concat (when (cdr rest) " ")
-                              (cdar rest)
-                              (when (and words
-                                         (not (eq (string-to-char words) ? )))
-                                " ")))
-                  words)
-           rest (cdr rest)))
-    words))
-
-;; Fixme: This should decode in place, not cons intermediate strings.
-;; Also check whether it needs to worry about delimiting fields like
-;; encoding.
-
-;; In fact it's reported that (invalid) encoding of mailboxes in
-;; addr-specs is in use, so delimiting fields might help.  Probably
-;; not decoding a word which isn't properly delimited is good enough
-;; and worthwhile (is it more correct or not?), e.g. something like
-;; `=?iso-8859-1?q?foo?=@'.
-
-(defun rfc2047-decode-region (start end &optional address-mime)
-  "Decode MIME-encoded words in region between START and END.
-If ADDRESS-MIME is non-nil, strip backslashes which precede characters
-other than `\"' and `\\' in quoted strings."
-  (interactive "r")
-  (let ((case-fold-search t)
-       (eword-regexp
-        (if rfc2047-allow-irregular-q-encoded-words
-            (eval-when-compile
-              (concat "[\n\t ]*\\(" rfc2047-encoded-word-regexp-loose "\\)"))
-          (eval-when-compile
-            (concat "[\n\t ]*\\(" rfc2047-encoded-word-regexp "\\)"))))
-       b e match words)
-    (save-excursion
-      (save-restriction
-       (narrow-to-region start end)
-       (when address-mime
-         (rfc2047-strip-backslashes-in-quoted-strings))
-       (goto-char (setq b start))
-       ;; Look for the encoded-words.
-       (while (setq match (re-search-forward eword-regexp nil t))
-         (setq e (match-beginning 1)
-               end (match-end 0)
-               words nil)
-         (while match
-           (push (list (match-string 2) ;; charset
-                       (char-after (match-beginning 3)) ;; encoding
-                       (substring (match-string 3) 2) ;; encoded-text
-                       (match-string 1)) ;; encoded-word
-                 words)
-           ;; Look for the subsequent encoded-words.
-           (when (setq match (looking-at eword-regexp))
-             (goto-char (setq end (match-end 0)))))
-         ;; Replace the encoded-words with the decoded one.
-         (delete-region e end)
-         (insert (rfc2047-decode-encoded-words (nreverse words)))
-         (save-restriction
-           (narrow-to-region e (point))
-           (goto-char e)
-           ;; Remove newlines between decoded words, though such
-           ;; things essentially must not be there.
-           (while (re-search-forward "[\n\r]+" nil t)
-             (replace-match " "))
-           (setq end (point-max))
-           ;; Quote decoded words if there are special characters
-           ;; which might violate RFC2822.
-           (when (and rfc2047-quote-decoded-words-containing-tspecials
-                      (let ((regexp (car (rassq
-                                          'address-mime
-                                          rfc2047-header-encoding-alist))))
-                        (when regexp
-                          (save-restriction
-                            (widen)
-                            (and
-                             ;; Don't quote words if already quoted.
-                             (not (and (eq (char-before e) ?\")
-                                       (eq (char-after end) ?\")))
-                             (progn
-                               (beginning-of-line)
-                               (while (and (memq (char-after) '(?  ?\t))
-                                           (zerop (forward-line -1))))
-                               (looking-at regexp)))))))
-             (let (quoted)
-               (goto-char e)
-               (skip-chars-forward " \t")
-               (setq start (point))
-               (setq quoted (eq (char-after) ?\"))
-               (goto-char (point-max))
-               (skip-chars-backward " \t" start)
-               (if (setq quoted (and quoted
-                                     (> (point) (1+ start))
-                                     (eq (char-before) ?\")))
-                   (progn
-                     (backward-char)
-                     (setq start (1+ start)
-                           end (point-marker)))
-                 (setq end (point-marker)))
-               (goto-char start)
-               (while (search-forward "\"" end t)
-                 (when (prog2
-                           (backward-char)
-                           (zerop (% (skip-chars-backward "\\\\") 2))
-                         (goto-char (match-beginning 0)))
-                   (insert "\\"))
-                 (forward-char))
-               (when (and (not quoted)
-                          (progn
-                            (goto-char start)
-                            (re-search-forward
-                             (concat "[" ietf-drums-tspecials "]")
-                             end t)))
-                 (goto-char start)
-                 (insert "\"")
-                 (goto-char end)
-                 (insert "\""))
-               (set-marker end nil)))
-           (goto-char (point-max)))
-         (when (and (mm-multibyte-p)
-                    mail-parse-charset
-                    (not (eq mail-parse-charset 'us-ascii))
-                    (not (eq mail-parse-charset 'gnus-decoded)))
-           (decode-coding-region b e mail-parse-charset))
-         (setq b (point)))
-       (when (and (mm-multibyte-p)
-                  mail-parse-charset
-                  (not (eq mail-parse-charset 'us-ascii))
-                  (not (eq mail-parse-charset 'gnus-decoded)))
-         (decode-coding-region b (point-max) mail-parse-charset))))))
-
-(defun rfc2047-decode-address-region (start end)
-  "Decode MIME-encoded words in region between START and END.
-Backslashes which precede characters other than `\"' and `\\' in quoted
-strings are stripped."
-  (rfc2047-decode-region start end t))
-
-(defun rfc2047-decode-string (string &optional address-mime)
-  "Decode MIME-encoded STRING and return the result.
-If ADDRESS-MIME is non-nil, strip backslashes which precede characters
-other than `\"' and `\\' in quoted strings."
-  (if (string-match "=\\?" string)
-      (with-temp-buffer
-       ;; We used to only call mm-enable-multibyte if `m' is non-nil,
-       ;; but this can't be the right criterion.  Don't just revert this
-       ;; change if it encounters a bug.  Please help me fix it
-       ;; right instead.  --Stef
-       ;; The string returned should always be multibyte in a multibyte
-       ;; session, i.e. the buffer should be multibyte before
-       ;; `buffer-string' is called.
-       (mm-enable-multibyte)
-       (insert string)
-       (inline
-         (rfc2047-decode-region (point-min) (point-max) address-mime))
-       (buffer-string))
-    (when address-mime
-      (setq string
-           (with-temp-buffer
-             (when (multibyte-string-p string)
-               (mm-enable-multibyte))
-             (insert string)
-             (rfc2047-strip-backslashes-in-quoted-strings)
-             (buffer-string))))
-    ;; Fixme: As above, `m' here is inappropriate.
-    (if (and ;; m
-        mail-parse-charset
-        (not (eq mail-parse-charset 'us-ascii))
-        (not (eq mail-parse-charset 'gnus-decoded)))
-       ;; `decode-coding-string' in Emacs offers a third optional
-       ;; arg NOCOPY to avoid consing a new string if the decoding
-       ;; is "trivial".  Unfortunately it currently doesn't
-       ;; consider anything else than a nil coding system
-       ;; trivial.
-       ;; `rfc2047-decode-string' is called multiple times for each
-       ;; article during summary buffer generation, and we really
-       ;; want to avoid unnecessary consing.  So we bypass
-       ;; `decode-coding-string' if the string is purely ASCII.
-       (if (eq (detect-coding-string string t) 'undecided)
-           ;; string is purely ASCII
-           string
-         (decode-coding-string string mail-parse-charset))
-      (string-to-multibyte string))))
-
-(defun rfc2047-decode-address-string (string)
-  "Decode MIME-encoded STRING and return the result.
-Backslashes which precede characters other than `\"' and `\\' in quoted
-strings are stripped."
-  (rfc2047-decode-string string t))
-
-(defun rfc2047-pad-base64 (string)
-  "Pad STRING to quartets."
-  ;; Be more liberal to accept buggy base64 strings. If
-  ;; base64-decode-string accepts buggy strings, this function could
-  ;; be aliased to identity.
-  (if (= 0 (mod (length string) 4))
-      string
-    (when (string-match "=+$" string)
-      (setq string (substring string 0 (match-beginning 0))))
-    (case (mod (length string) 4)
-      (0 string)
-      (1 string) ;; Error, don't pad it.
-      (2 (concat string "=="))
-      (3 (concat string "=")))))
-
-(provide 'rfc2047)
-
-;;; rfc2047.el ends here
diff --git a/lisp/gnus/rfc2231.el b/lisp/gnus/rfc2231.el
deleted file mode 100644 (file)
index 128779a..0000000
+++ /dev/null
@@ -1,308 +0,0 @@
-;;; rfc2231.el --- Functions for decoding rfc2231 headers
-
-;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
-
-;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;;; Code:
-
-(eval-when-compile (require 'cl))
-(require 'ietf-drums)
-(require 'rfc2047)
-(autoload 'mm-encode-body "mm-bodies")
-(autoload 'mail-header-remove-whitespace "mail-parse")
-(autoload 'mail-header-remove-comments "mail-parse")
-
-(defun rfc2231-get-value (ct attribute)
-  "Return the value of ATTRIBUTE from CT."
-  (cdr (assq attribute (cdr ct))))
-
-(defun rfc2231-parse-qp-string (string)
-  "Parse QP-encoded string using `rfc2231-parse-string'.
-N.B.  This is in violation with RFC2047, but it seem to be in common use."
-  (rfc2231-parse-string (rfc2047-decode-string string)))
-
-(defun rfc2231-parse-string (string &optional signal-error)
-  "Parse STRING and return a list.
-The list will be on the form
- `(name (attribute . value) (attribute . value)...)'.
-
-If the optional SIGNAL-ERROR is non-nil, signal an error when this
-function fails in parsing of parameters.  Otherwise, this function
-must never cause a Lisp error."
-  (with-temp-buffer
-    (let ((ttoken (ietf-drums-token-to-list ietf-drums-text-token))
-         (stoken (ietf-drums-token-to-list ietf-drums-tspecials))
-         (ntoken (ietf-drums-token-to-list "0-9"))
-         c type attribute encoded number parameters value)
-      (ietf-drums-init
-       (condition-case nil
-          (mail-header-remove-whitespace
-           (mail-header-remove-comments string))
-        ;; The most likely cause of an error is unbalanced parentheses
-        ;; or double-quotes.  If all parentheses and double-quotes are
-        ;; quoted meaninglessly with backslashes, removing them might
-        ;; make it parsable.  Let's try...
-        (error
-         (let (mod)
-           (when (and (string-match "\\\\\"" string)
-                      (not (string-match "\\`\"\\|[^\\]\"" string)))
-             (setq string (replace-regexp-in-string "\\\\\"" "\"" string)
-                   mod t))
-           (when (and (string-match "\\\\(" string)
-                      (string-match "\\\\)" string)
-                      (not (string-match "\\`(\\|[^\\][()]" string)))
-             (setq string (replace-regexp-in-string
-                           "\\\\\\([()]\\)" "\\1" string)
-                   mod t))
-           (or (and mod
-                    (ignore-errors
-                      (mail-header-remove-whitespace
-                       (mail-header-remove-comments string))))
-               ;; Finally, attempt to extract only type.
-               (if (string-match
-                    (concat "\\`[\t\n ]*\\([^" ietf-drums-tspecials "\t\n ]+"
-                            "\\(?:/[^" ietf-drums-tspecials
-                            "\t\n ]+\\)?\\)\\(?:[\t\n ;]\\|\\'\\)")
-                    string)
-                   (match-string 1 string)
-                 ""))))))
-      (let ((table (copy-syntax-table ietf-drums-syntax-table)))
-       (modify-syntax-entry ?\' "w" table)
-       (modify-syntax-entry ?* " " table)
-       (modify-syntax-entry ?\; " " table)
-       (modify-syntax-entry ?= " " table)
-       ;; The following isn't valid, but one should be liberal
-       ;; in what one receives.
-       (modify-syntax-entry ?\: "w" table)
-       (set-syntax-table table))
-      (setq c (char-after))
-      (when (and (memq c ttoken)
-                (not (memq c stoken))
-                (setq type (ignore-errors
-                             (downcase
-                              (buffer-substring (point) (progn
-                                                          (forward-sexp 1)
-                                                          (point)))))))
-       ;; Do the params
-       (condition-case err
-           (progn
-             (while (not (eobp))
-               (setq c (char-after))
-               (unless (eq c ?\;)
-                 (error "Invalid header: %s" string))
-               (forward-char 1)
-               ;; If c in nil, then this is an invalid header, but
-               ;; since elm generates invalid headers on this form,
-               ;; we allow it.
-               (when (setq c (char-after))
-                 (if (and (memq c ttoken)
-                          (not (memq c stoken)))
-                     (setq attribute
-                           (intern
-                            (downcase
-                             (buffer-substring
-                              (point) (progn (forward-sexp 1) (point))))))
-                   (error "Invalid header: %s" string))
-                 (setq c (char-after))
-                 (if (eq c ?*)
-                     (progn
-                       (forward-char 1)
-                       (setq c (char-after))
-                       (if (not (memq c ntoken))
-                           (setq encoded t
-                                 number nil)
-                         (setq number
-                               (string-to-number
-                                (buffer-substring
-                                 (point) (progn (forward-sexp 1) (point)))))
-                         (setq c (char-after))
-                         (when (eq c ?*)
-                           (setq encoded t)
-                           (forward-char 1)
-                           (setq c (char-after)))))
-                   (setq number nil
-                         encoded nil))
-                 (unless (eq c ?=)
-                   (error "Invalid header: %s" string))
-                 (forward-char 1)
-                 (setq c (char-after))
-                 (cond
-                  ((eq c ?\")
-                   (setq value (buffer-substring (1+ (point))
-                                                 (progn
-                                                   (forward-sexp 1)
-                                                   (1- (point)))))
-                   (when encoded
-                     (setq value (mapconcat (lambda (c) (format "%%%02x" c))
-                                            value ""))))
-                  ((and (or (memq c ttoken)
-                            ;; EXTENSION: Support non-ascii chars.
-                            (> c ?\177))
-                        (not (memq c stoken)))
-                   (setq value
-                         (buffer-substring
-                          (point)
-                          (progn
-                            ;; Jump over asterisk, non-ASCII
-                            ;; and non-boundary characters.
-                            (while (and c
-                                        (or (eq c ?*)
-                                            (> c ?\177)
-                                            (not (eq (char-syntax c) ? ))))
-                              (forward-char 1)
-                              (setq c (char-after)))
-                            (point)))))
-                  (t
-                   (error "Invalid header: %s" string)))
-                 (push (list attribute value number encoded)
-                       parameters))))
-         (error
-          (setq parameters nil)
-          (when signal-error
-            (signal (car err) (cdr err)))))
-
-       ;; Now collect and concatenate continuation parameters.
-       (let ((cparams nil)
-             elem)
-         (loop for (attribute value part encoded)
-               in (sort parameters (lambda (e1 e2)
-                                     (< (or (caddr e1) 0)
-                                        (or (caddr e2) 0))))
-               do (cond
-                   ;; First part.
-                   ((or (not (setq elem (assq attribute cparams)))
-                        (and (numberp part)
-                             (zerop part)))
-                    (push (list attribute value encoded) cparams))
-                   ;; Repetition of a part; do nothing.
-                   ((and elem
-                         (null number))
-                    )
-                   ;; Concatenate continuation parts.
-                   (t
-                    (setcar (cdr elem) (concat (cadr elem) value)))))
-         ;; Finally decode encoded values.
-         (cons type (mapcar
-                     (lambda (elem)
-                       (cons (car elem)
-                             (if (nth 2 elem)
-                                 (rfc2231-decode-encoded-string (nth 1 elem))
-                               (nth 1 elem))))
-                     (nreverse cparams))))))))
-
-(defun rfc2231-decode-encoded-string (string)
-  "Decode an RFC2231-encoded string.
-These look like:
- \"us-ascii\\='en-us\\='This%20is%20%2A%2A%2Afun%2A%2A%2A\",
- \"us-ascii\\='\\='This%20is%20%2A%2A%2Afun%2A%2A%2A\",
- \"\\='en-us\\='This%20is%20%2A%2A%2Afun%2A%2A%2A\",
- \"\\='\\='This%20is%20%2A%2A%2Afun%2A%2A%2A\", or
- \"This is ***fun***\"."
-  (string-match "\\`\\(?:\\([^']+\\)?'\\([^']+\\)?'\\)?\\(.+\\)" string)
-  (let ((coding-system (mm-charset-to-coding-system
-                       (match-string 1 string) nil t))
-       ;;(language (match-string 2 string))
-       (value (match-string 3 string)))
-    (mm-with-unibyte-buffer
-      (insert value)
-      (goto-char (point-min))
-      (while (re-search-forward "%\\([0-9A-Fa-f][0-9A-Fa-f]\\)" nil t)
-       (insert
-        (prog1
-            (string-to-number (match-string 1) 16)
-          (delete-region (match-beginning 0) (match-end 0)))))
-      ;; Decode using the charset, if any.
-      (if (memq coding-system '(nil ascii))
-         (buffer-string)
-       (decode-coding-string (buffer-string) coding-system)))))
-
-(defun rfc2231-encode-string (param value)
-  "Return and PARAM=VALUE string encoded according to RFC2231.
-Use `mml-insert-parameter' or `mml-insert-parameter-string' to insert
-the result of this function."
-  (let ((control (ietf-drums-token-to-list ietf-drums-no-ws-ctl-token))
-       (tspecial (ietf-drums-token-to-list ietf-drums-tspecials))
-       (special (ietf-drums-token-to-list "*'%\n\t"))
-       (ascii (ietf-drums-token-to-list ietf-drums-text-token))
-       (num -1)
-       ;; Don't make lines exceeding 76 column.
-       (limit (- 74 (length param)))
-       spacep encodep charsetp charset broken)
-    (mm-with-multibyte-buffer
-      (insert value)
-      (goto-char (point-min))
-      (while (not (eobp))
-       (cond
-        ((or (memq (following-char) control)
-             (memq (following-char) tspecial)
-             (memq (following-char) special))
-         (setq encodep t))
-        ((eq (following-char) ? )
-         (setq spacep t))
-        ((not (memq (following-char) ascii))
-         (setq charsetp t)))
-       (forward-char 1))
-      (when charsetp
-       (setq charset (mm-encode-body)))
-      (mm-disable-multibyte)
-      (cond
-       ((or encodep charsetp
-           (progn
-             (end-of-line)
-             (> (current-column) (if spacep (- limit 2) limit))))
-       (setq limit (- limit 6))
-       (goto-char (point-min))
-       (insert (symbol-name (or charset 'us-ascii)) "''")
-       (while (not (eobp))
-         (if (or (not (memq (following-char) ascii))
-                 (memq (following-char) control)
-                 (memq (following-char) tspecial)
-                 (memq (following-char) special)
-                 (eq (following-char) ? ))
-             (progn
-               (when (>= (current-column) (1- limit))
-                 (insert ";\n")
-                 (setq broken t))
-               (insert "%" (format "%02x" (following-char)))
-               (delete-char 1))
-           (when (> (current-column) limit)
-             (insert ";\n")
-             (setq broken t))
-           (forward-char 1)))
-       (goto-char (point-min))
-       (if (not broken)
-           (insert param "*=")
-         (while (not (eobp))
-           (insert (if (>= num 0) " " "")
-                   param "*" (format "%d" (incf num)) "*=")
-           (forward-line 1))))
-       (spacep
-       (goto-char (point-min))
-       (insert param "=\"")
-       (goto-char (point-max))
-       (insert "\""))
-       (t
-       (goto-char (point-min))
-       (insert param "=")))
-      (buffer-string))))
-
-(provide 'rfc2231)
-
-;;; rfc2231.el ends here
diff --git a/lisp/gnus/rtree.el b/lisp/gnus/rtree.el
deleted file mode 100644 (file)
index 662e043..0000000
+++ /dev/null
@@ -1,281 +0,0 @@
-;;; rtree.el --- functions for manipulating range trees
-
-;; Copyright (C) 2010-2016 Free Software Foundation, Inc.
-
-;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; A "range tree" is a binary tree that stores ranges.  They are
-;; similar to interval trees, but do not allow overlapping intervals.
-
-;; A range is an ordered list of number intervals, like this:
-
-;; ((10 . 25) 56 78 (98 . 201))
-
-;; Common operations, like lookup, deletion and insertion are O(n) in
-;; a range, but an rtree is O(log n) in all these operations.
-;; Transformation between a range and an rtree is O(n).
-
-;; The rtrees are quite simple.  The structure of each node is
-
-;; (cons (cons low high) (cons left right))
-
-;; That is, they are three cons cells, where the car of the top cell
-;; is the actual range, and the cdr has the left and right child.  The
-;; rtrees aren't automatically balanced, but are balanced when
-;; created, and can be rebalanced when deemed necessary.
-
-;;; Code:
-
-(eval-when-compile
-  (require 'cl))
-
-(defmacro rtree-make-node ()
-  `(list (list nil) nil))
-
-(defmacro rtree-set-left (node left)
-  `(setcar (cdr ,node) ,left))
-
-(defmacro rtree-set-right (node right)
-  `(setcdr (cdr ,node) ,right))
-
-(defmacro rtree-set-range (node range)
-  `(setcar ,node ,range))
-
-(defmacro rtree-low (node)
-  `(caar ,node))
-
-(defmacro rtree-high (node)
-  `(cdar ,node))
-
-(defmacro rtree-set-low (node number)
-  `(setcar (car ,node) ,number))
-
-(defmacro rtree-set-high (node number)
-  `(setcdr (car ,node) ,number))
-
-(defmacro rtree-left (node)
-  `(cadr ,node))
-
-(defmacro rtree-right (node)
-  `(cddr ,node))
-
-(defmacro rtree-range (node)
-  `(car ,node))
-
-(defsubst rtree-normalize-range (range)
-  (when (numberp range)
-    (setq range (cons range range)))
-  range)
-
-(define-obsolete-function-alias 'rtree-normalise-range
-  'rtree-normalize-range "25.1")
-
-(defun rtree-make (range)
-  "Make an rtree from RANGE."
-  ;; Normalize the range.
-  (unless (listp (cdr-safe range))
-    (setq range (list range)))
-  (rtree-make-1 (cons nil range) (length range)))
-
-(defun rtree-make-1 (range length)
-  (let ((mid (/ length 2))
-       (node (rtree-make-node)))
-    (when (> mid 0)
-      (rtree-set-left node (rtree-make-1 range mid)))
-    (rtree-set-range node (rtree-normalize-range (cadr range)))
-    (setcdr range (cddr range))
-    (when (> (- length mid 1) 0)
-      (rtree-set-right node (rtree-make-1 range (- length mid 1))))
-    node))
-
-(defun rtree-memq (tree number)
-  "Return non-nil if NUMBER is present in TREE."
-  (while (and tree
-             (not (and (>= number (rtree-low tree))
-                       (<= number (rtree-high tree)))))
-    (setq tree
-         (if (< number (rtree-low tree))
-             (rtree-left tree)
-           (rtree-right tree))))
-  tree)
-
-(defun rtree-add (tree number)
-  "Add NUMBER to TREE."
-  (while tree
-    (cond
-     ;; It's already present, so we don't have to do anything.
-     ((and (>= number (rtree-low tree))
-          (<= number (rtree-high tree)))
-      (setq tree nil))
-     ((< number (rtree-low tree))
-      (cond
-       ;; Extend the low range.
-       ((= number (1- (rtree-low tree)))
-       (rtree-set-low tree number)
-       ;; Check whether we need to merge this node with the child.
-       (when (and (rtree-left tree)
-                  (= (rtree-high (rtree-left tree)) (1- number)))
-         ;; Extend the range to the low from the child.
-         (rtree-set-low tree (rtree-low (rtree-left tree)))
-         ;; The child can't have a right child, so just transplant the
-         ;; child's left tree to our left tree.
-         (rtree-set-left tree (rtree-left (rtree-left tree))))
-       (setq tree nil))
-       ;; Descend further to the left.
-       ((rtree-left tree)
-       (setq tree (rtree-left tree)))
-       ;; Add a new node.
-       (t
-       (let ((new-node (rtree-make-node)))
-         (rtree-set-low new-node number)
-         (rtree-set-high new-node number)
-         (rtree-set-left tree new-node)
-         (setq tree nil)))))
-     (t
-      (cond
-       ;; Extend the high range.
-       ((= number (1+ (rtree-high tree)))
-       (rtree-set-high tree number)
-       ;; Check whether we need to merge this node with the child.
-       (when (and (rtree-right tree)
-                  (= (rtree-low (rtree-right tree)) (1+ number)))
-         ;; Extend the range to the high from the child.
-         (rtree-set-high tree (rtree-high (rtree-right tree)))
-         ;; The child can't have a left child, so just transplant the
-         ;; child's left right to our right tree.
-         (rtree-set-right tree (rtree-right (rtree-right tree))))
-       (setq tree nil))
-       ;; Descend further to the right.
-       ((rtree-right tree)
-       (setq tree (rtree-right tree)))
-       ;; Add a new node.
-       (t
-       (let ((new-node (rtree-make-node)))
-         (rtree-set-low new-node number)
-         (rtree-set-high new-node number)
-         (rtree-set-right tree new-node)
-         (setq tree nil))))))))
-
-(defun rtree-delq (tree number)
-  "Remove NUMBER from TREE destructively.  Returns the new tree."
-  (let ((result tree)
-       prev)
-    (while tree
-      (cond
-       ((< number (rtree-low tree))
-       (setq prev tree
-             tree (rtree-left tree)))
-       ((> number (rtree-high tree))
-       (setq prev tree
-             tree (rtree-right tree)))
-       ;; The number is in this node.
-       (t
-       (cond
-        ;; The only entry; delete the node.
-        ((= (rtree-low tree) (rtree-high tree))
-         (cond
-          ;; Two children.  Replace with successor value.
-          ((and (rtree-left tree) (rtree-right tree))
-           (let ((parent tree)
-                 (successor (rtree-right tree)))
-             (while (rtree-left successor)
-               (setq parent successor
-                     successor (rtree-left successor)))
-             ;; We now have the leftmost child of our right child.
-             (rtree-set-range tree (rtree-range successor))
-             ;; Transplant the child (if any) to the parent.
-             (rtree-set-left parent (rtree-right successor))))
-          (t
-           (let ((rest (or (rtree-left tree)
-                           (rtree-right tree))))
-             ;; One or zero children.  Remove the node.
-             (cond
-              ((null prev)
-               (setq result rest))
-              ((eq (rtree-left prev) tree)
-               (rtree-set-left prev rest))
-              (t
-               (rtree-set-right prev rest)))))))
-        ;; The lowest in the range; just adjust.
-        ((= number (rtree-low tree))
-         (rtree-set-low tree (1+ number)))
-        ;; The highest in the range; just adjust.
-        ((= number (rtree-high tree))
-         (rtree-set-high tree (1- number)))
-        ;; We have to split this range.
-        (t
-         (let ((new-node (rtree-make-node)))
-           (rtree-set-low new-node (rtree-low tree))
-           (rtree-set-high new-node (1- number))
-           (rtree-set-low tree (1+ number))
-           (cond
-            ;; Two children; insert the new node as the predecessor
-            ;; node.
-            ((and (rtree-left tree) (rtree-right tree))
-             (let ((predecessor (rtree-left tree)))
-               (while (rtree-right predecessor)
-                 (setq predecessor (rtree-right predecessor)))
-               (rtree-set-right predecessor new-node)))
-            ((rtree-left tree)
-             (rtree-set-right new-node tree)
-             (rtree-set-left new-node (rtree-left tree))
-             (rtree-set-left tree nil)
-             (cond
-              ((null prev)
-               (setq result new-node))
-              ((eq (rtree-left prev) tree)
-               (rtree-set-left prev new-node))
-              (t
-               (rtree-set-right prev new-node))))
-            (t
-             (rtree-set-left tree new-node))))))
-       (setq tree nil))))
-    result))
-
-(defun rtree-extract (tree)
-  "Convert TREE to range form."
-  (let (stack result)
-    (while (or stack
-              tree)
-      (if tree
-         (progn
-           (push tree stack)
-           (setq tree (rtree-right tree)))
-       (setq tree (pop stack))
-       (push (if (= (rtree-low tree)
-                    (rtree-high tree))
-                 (rtree-low tree)
-               (rtree-range tree))
-             result)
-       (setq tree (rtree-left tree))))
-    result))
-
-(defun rtree-length (tree)
-  "Return the number of numbers stored in TREE."
-  (if (null tree)
-      0
-    (+ (rtree-length (rtree-left tree))
-       (1+ (- (rtree-high tree)
-             (rtree-low tree)))
-       (rtree-length (rtree-right tree)))))
-
-(provide 'rtree)
-
-;;; rtree.el ends here
diff --git a/lisp/gnus/sieve-manage.el b/lisp/gnus/sieve-manage.el
deleted file mode 100644 (file)
index 695bbd8..0000000
+++ /dev/null
@@ -1,575 +0,0 @@
-;;; sieve-manage.el --- Implementation of the managesieve protocol in elisp
-
-;; Copyright (C) 2001-2016 Free Software Foundation, Inc.
-
-;; Author: Simon Josefsson <simon@josefsson.org>
-;;         Albert Krewinkel <tarleb@moltkeplatz.de>
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This library provides an elisp API for the managesieve network
-;; protocol.
-;;
-;; It uses the SASL library for authentication, which means it
-;; supports DIGEST-MD5, CRAM-MD5, SCRAM-MD5, NTLM, PLAIN and LOGIN
-;; methods.  STARTTLS is not well tested, but should be easy to get to
-;; work if someone wants.
-;;
-;; The API should be fairly obvious for anyone familiar with the
-;; managesieve protocol, interface functions include:
-;;
-;; `sieve-manage-open'
-;; open connection to managesieve server, returning a buffer to be
-;; used by all other API functions.
-;;
-;; `sieve-manage-opened'
-;; check if a server is open or not
-;;
-;; `sieve-manage-close'
-;; close a server connection.
-;;
-;; `sieve-manage-listscripts'
-;; `sieve-manage-deletescript'
-;; `sieve-manage-getscript'
-;; performs managesieve protocol actions
-;;
-;; and that's it.  Example of a managesieve session in *scratch*:
-;;
-;; (with-current-buffer (sieve-manage-open "mail.example.com")
-;;   (sieve-manage-authenticate)
-;;   (sieve-manage-listscripts))
-;;
-;; => ((active . "main") "vacation")
-;;
-;; References:
-;;
-;; draft-martin-managesieve-02.txt,
-;; "A Protocol for Remotely Managing Sieve Scripts",
-;; by Tim Martin.
-;;
-;; Release history:
-;;
-;; 2001-10-31 Committed to Oort Gnus.
-;; 2002-07-27 Added DELETESCRIPT.  Suggested by Ned Ludd.
-;; 2002-08-03 Use SASL library.
-;; 2013-06-05 Enabled STARTTLS support, fixed bit rot.
-
-;;; Code:
-
-(if (locate-library "password-cache")
-    (require 'password-cache)
-  (require 'password))
-
-(eval-when-compile (require 'cl))
-(require 'sasl)
-(require 'starttls)
-(autoload 'sasl-find-mechanism "sasl")
-(autoload 'auth-source-search "auth-source")
-
-;; User customizable variables:
-
-(defgroup sieve-manage nil
-  "Low-level Managesieve protocol issues."
-  :group 'mail
-  :prefix "sieve-")
-
-(defcustom sieve-manage-log "*sieve-manage-log*"
-  "Name of buffer for managesieve session trace."
-  :type 'string
-  :group 'sieve-manage)
-
-(defcustom sieve-manage-server-eol "\r\n"
-  "The EOL string sent from the server."
-  :type 'string
-  :group 'sieve-manage)
-
-(defcustom sieve-manage-client-eol "\r\n"
-  "The EOL string we send to the server."
-  :type 'string
-  :group 'sieve-manage)
-
-(defcustom sieve-manage-authenticators '(digest-md5
-                                        cram-md5
-                                        scram-md5
-                                        ntlm
-                                        plain
-                                        login)
-  "Priority of authenticators to consider when authenticating to server."
-  ;; FIXME Improve this.  It's not `set'.
-  ;; It's like (repeat (choice (const ...))), where each choice can
-  ;; only appear once.
-  :type '(repeat symbol)
-  :group 'sieve-manage)
-
-(defcustom sieve-manage-authenticator-alist
-  '((cram-md5   sieve-manage-cram-md5-p       sieve-manage-cram-md5-auth)
-    (digest-md5 sieve-manage-digest-md5-p     sieve-manage-digest-md5-auth)
-    (scram-md5  sieve-manage-scram-md5-p      sieve-manage-scram-md5-auth)
-    (ntlm       sieve-manage-ntlm-p           sieve-manage-ntlm-auth)
-    (plain      sieve-manage-plain-p          sieve-manage-plain-auth)
-    (login      sieve-manage-login-p          sieve-manage-login-auth))
-  "Definition of authenticators.
-
-\(NAME CHECK AUTHENTICATE)
-
-NAME names the authenticator.  CHECK is a function returning non-nil if
-the server support the authenticator and AUTHENTICATE is a function
-for doing the actual authentication."
-  :type '(repeat (list (symbol :tag "Name") (function :tag "Check function")
-                      (function :tag "Authentication function")))
-  :group 'sieve-manage)
-
-(defcustom sieve-manage-default-port "sieve"
-  "Default port number or service name for managesieve protocol."
-  :type '(choice integer string)
-  :version "24.4"
-  :group 'sieve-manage)
-
-(defcustom sieve-manage-default-stream 'network
-  "Default stream type to use for `sieve-manage'."
-  :version "24.1"
-  :type 'symbol
-  :group 'sieve-manage)
-
-;; Internal variables:
-
-(defconst sieve-manage-local-variables '(sieve-manage-server
-                                        sieve-manage-port
-                                        sieve-manage-auth
-                                        sieve-manage-stream
-                                        sieve-manage-process
-                                        sieve-manage-client-eol
-                                        sieve-manage-server-eol
-                                        sieve-manage-capability))
-(defconst sieve-manage-coding-system-for-read 'binary)
-(defconst sieve-manage-coding-system-for-write 'binary)
-(defvar sieve-manage-stream nil)
-(defvar sieve-manage-auth nil)
-(defvar sieve-manage-server nil)
-(defvar sieve-manage-port nil)
-(defvar sieve-manage-state 'closed
-  "Managesieve state.
-Valid states are `closed', `initial', `nonauth', and `auth'.")
-(defvar sieve-manage-process nil)
-(defvar sieve-manage-capability nil)
-
-;; Internal utility functions
-(autoload 'mm-enable-multibyte "mm-util")
-
-(defun sieve-manage-make-process-buffer ()
-  (with-current-buffer
-      (generate-new-buffer (format " *sieve %s:%s*"
-                                   sieve-manage-server
-                                   sieve-manage-port))
-    (mapc 'make-local-variable sieve-manage-local-variables)
-    (mm-enable-multibyte)
-    (buffer-disable-undo)
-    (current-buffer)))
-
-(defun sieve-manage-erase (&optional p buffer)
-  (let ((buffer (or buffer (current-buffer))))
-    (and sieve-manage-log
-        (with-current-buffer (get-buffer-create sieve-manage-log)
-          (mm-enable-multibyte)
-          (buffer-disable-undo)
-          (goto-char (point-max))
-          (insert-buffer-substring buffer (with-current-buffer buffer
-                                            (point-min))
-                                   (or p (with-current-buffer buffer
-                                           (point-max)))))))
-  (delete-region (point-min) (or p (point-max))))
-
-(defun sieve-manage-open-server (server port &optional stream buffer)
-  "Open network connection to SERVER on PORT.
-Return the buffer associated with the connection."
-  (with-current-buffer buffer
-    (sieve-manage-erase)
-    (setq sieve-manage-state 'initial)
-    (destructuring-bind (proc . props)
-        (open-network-stream
-         "SIEVE" buffer server port
-         :type stream
-         :capability-command "CAPABILITY\r\n"
-         :end-of-command "^\\(OK\\|NO\\).*\n"
-         :success "^OK.*\n"
-         :return-list t
-         :starttls-function
-         (lambda (capabilities)
-           (when (string-match "\\bSTARTTLS\\b" capabilities)
-             "STARTTLS\r\n")))
-      (setq sieve-manage-process proc)
-      (setq sieve-manage-capability
-            (sieve-manage-parse-capability (plist-get props :capabilities)))
-      ;; Ignore new capabilities issues after successful STARTTLS
-      (when (and (memq stream '(nil network starttls))
-                 (eq (plist-get props :type) 'tls))
-        (sieve-manage-drop-next-answer))
-      (current-buffer))))
-
-;; Authenticators
-(defun sieve-sasl-auth (buffer mech)
-  "Login to server using the SASL MECH method."
-  (message "sieve: Authenticating using %s..." mech)
-  (with-current-buffer buffer
-    (let* ((auth-info (auth-source-search :host sieve-manage-server
-                                          :port "sieve"
-                                          :max 1
-                                          :create t))
-           (user-name (or (plist-get (nth 0 auth-info) :user) ""))
-           (user-password (or (plist-get (nth 0 auth-info) :secret) ""))
-           (user-password (if (functionp user-password)
-                              (funcall user-password)
-                            user-password))
-           (client (sasl-make-client (sasl-find-mechanism (list mech))
-                                     user-name "sieve" sieve-manage-server))
-           (sasl-read-passphrase
-            ;; We *need* to copy the password, because sasl will modify it
-            ;; somehow.
-            `(lambda (prompt) ,(copy-sequence user-password)))
-           (step (sasl-next-step client nil))
-           (tag (sieve-manage-send
-                 (concat
-                  "AUTHENTICATE \""
-                  mech
-                  "\""
-                  (and (sasl-step-data step)
-                       (concat
-                        " \""
-                        (base64-encode-string
-                         (sasl-step-data step)
-                         'no-line-break)
-                        "\"")))))
-           data rsp)
-      (catch 'done
-        (while t
-          (setq rsp nil)
-          (goto-char (point-min))
-          (while (null (or (progn
-                             (setq rsp (sieve-manage-is-string))
-                             (if (not (and rsp (looking-at
-                                                sieve-manage-server-eol)))
-                                 (setq rsp nil)
-                               (goto-char (match-end 0))
-                               rsp))
-                           (setq rsp (sieve-manage-is-okno))))
-            (accept-process-output sieve-manage-process 1)
-            (goto-char (point-min)))
-          (sieve-manage-erase)
-          (when (sieve-manage-ok-p rsp)
-            (when (and (cadr rsp)
-                       (string-match "^SASL \"\\([^\"]+\\)\"" (cadr rsp)))
-              (sasl-step-set-data
-               step (base64-decode-string (match-string 1 (cadr rsp)))))
-            (if (and (setq step (sasl-next-step client step))
-                     (setq data (sasl-step-data step)))
-                ;; We got data for server but it's finished
-                (error "Server not ready for SASL data: %s" data)
-              ;; The authentication process is finished.
-              (throw 'done t)))
-          (unless (stringp rsp)
-            (error "Server aborted SASL authentication: %s" (caddr rsp)))
-          (sasl-step-set-data step (base64-decode-string rsp))
-          (setq step (sasl-next-step client step))
-          (sieve-manage-send
-           (if (sasl-step-data step)
-               (concat "\""
-                       (base64-encode-string (sasl-step-data step)
-                                             'no-line-break)
-                       "\"")
-             ""))))
-      (message "sieve: Login using %s...done" mech))))
-
-(defun sieve-manage-cram-md5-p (buffer)
-  (sieve-manage-capability "SASL" "CRAM-MD5" buffer))
-
-(defun sieve-manage-cram-md5-auth (buffer)
-  "Login to managesieve server using the CRAM-MD5 SASL method."
-  (sieve-sasl-auth buffer "CRAM-MD5"))
-
-(defun sieve-manage-digest-md5-p (buffer)
-  (sieve-manage-capability "SASL" "DIGEST-MD5" buffer))
-
-(defun sieve-manage-digest-md5-auth (buffer)
-  "Login to managesieve server using the DIGEST-MD5 SASL method."
-  (sieve-sasl-auth buffer "DIGEST-MD5"))
-
-(defun sieve-manage-scram-md5-p (buffer)
-  (sieve-manage-capability "SASL" "SCRAM-MD5" buffer))
-
-(defun sieve-manage-scram-md5-auth (buffer)
-  "Login to managesieve server using the SCRAM-MD5 SASL method."
-  (sieve-sasl-auth buffer "SCRAM-MD5"))
-
-(defun sieve-manage-ntlm-p (buffer)
-  (sieve-manage-capability "SASL" "NTLM" buffer))
-
-(defun sieve-manage-ntlm-auth (buffer)
-  "Login to managesieve server using the NTLM SASL method."
-  (sieve-sasl-auth buffer "NTLM"))
-
-(defun sieve-manage-plain-p (buffer)
-  (sieve-manage-capability "SASL" "PLAIN" buffer))
-
-(defun sieve-manage-plain-auth (buffer)
-  "Login to managesieve server using the PLAIN SASL method."
-  (sieve-sasl-auth buffer "PLAIN"))
-
-(defun sieve-manage-login-p (buffer)
-  (sieve-manage-capability "SASL" "LOGIN" buffer))
-
-(defun sieve-manage-login-auth (buffer)
-  "Login to managesieve server using the LOGIN SASL method."
-  (sieve-sasl-auth buffer "LOGIN"))
-
-;; Managesieve API
-
-(defun sieve-manage-open (server &optional port stream auth buffer)
-  "Open a network connection to a managesieve SERVER (string).
-Optional argument PORT is port number (integer) on remote server.
-Optional argument STREAM is any of `sieve-manage-streams' (a symbol).
-Optional argument AUTH indicates authenticator to use, see
-`sieve-manage-authenticators' for available authenticators.
-If nil, chooses the best stream the server is capable of.
-Optional argument BUFFER is buffer (buffer, or string naming buffer)
-to work in."
-  (setq sieve-manage-port (or port sieve-manage-default-port))
-  (with-current-buffer (or buffer (sieve-manage-make-process-buffer))
-    (setq sieve-manage-server (or server
-                                  sieve-manage-server)
-          sieve-manage-stream (or stream
-                                  sieve-manage-stream
-                                  sieve-manage-default-stream)
-          sieve-manage-auth   (or auth
-                                  sieve-manage-auth))
-    (message "sieve: Connecting to %s..." sieve-manage-server)
-    (sieve-manage-open-server sieve-manage-server
-                              sieve-manage-port
-                              sieve-manage-stream
-                              (current-buffer))
-    (when (sieve-manage-opened (current-buffer))
-      ;; Choose authenticator
-      (when (and (null sieve-manage-auth)
-                 (not (eq sieve-manage-state 'auth)))
-        (dolist (auth sieve-manage-authenticators)
-          (when (funcall (nth 1 (assq auth sieve-manage-authenticator-alist))
-                       buffer)
-            (setq sieve-manage-auth auth)
-            (return)))
-        (unless sieve-manage-auth
-          (error "Couldn't figure out authenticator for server")))
-      (sieve-manage-erase)
-      (current-buffer))))
-
-(defun sieve-manage-authenticate (&optional buffer)
-  "Authenticate on server in BUFFER.
-Return `sieve-manage-state' value."
-  (with-current-buffer (or buffer (current-buffer))
-    (if (eq sieve-manage-state 'nonauth)
-        (when (funcall (nth 2 (assq sieve-manage-auth
-                                    sieve-manage-authenticator-alist))
-                       (current-buffer))
-          (setq sieve-manage-state 'auth))
-      sieve-manage-state)))
-
-(defun sieve-manage-opened (&optional buffer)
-  "Return non-nil if connection to managesieve server in BUFFER is open.
-If BUFFER is nil then the current buffer is used."
-  (and (setq buffer (get-buffer (or buffer (current-buffer))))
-       (buffer-live-p buffer)
-       (with-current-buffer buffer
-        (and sieve-manage-process
-             (memq (process-status sieve-manage-process) '(open run))))))
-
-(defun sieve-manage-close (&optional buffer)
-  "Close connection to managesieve server in BUFFER.
-If BUFFER is nil, the current buffer is used."
-  (with-current-buffer (or buffer (current-buffer))
-    (when (sieve-manage-opened)
-      (sieve-manage-send "LOGOUT")
-      (sit-for 1))
-    (when (and sieve-manage-process
-              (memq (process-status sieve-manage-process) '(open run)))
-      (delete-process sieve-manage-process))
-    (setq sieve-manage-process nil)
-    (sieve-manage-erase)
-    t))
-
-(defun sieve-manage-capability (&optional name value buffer)
-  "Check if capability NAME of server BUFFER match VALUE.
-If it does, return the server value of NAME. If not returns nil.
-If VALUE is nil, do not check VALUE and return server value.
-If NAME is nil, return the full server list of capabilities."
-  (with-current-buffer (or buffer (current-buffer))
-    (if (null name)
-       sieve-manage-capability
-      (let ((server-value (cadr (assoc name sieve-manage-capability))))
-        (when (or (null value)
-                  (and server-value
-                       (string-match value server-value)))
-          server-value)))))
-
-(defun sieve-manage-listscripts (&optional buffer)
-  (with-current-buffer (or buffer (current-buffer))
-    (sieve-manage-send "LISTSCRIPTS")
-    (sieve-manage-parse-listscripts)))
-
-(defun sieve-manage-havespace (name size &optional buffer)
-  (with-current-buffer (or buffer (current-buffer))
-    (sieve-manage-send (format "HAVESPACE \"%s\" %s" name size))
-    (sieve-manage-parse-okno)))
-
-(defun sieve-manage-putscript (name content &optional buffer)
-  (with-current-buffer (or buffer (current-buffer))
-    (sieve-manage-send (format "PUTSCRIPT \"%s\" {%d+}%s%s" name
-                               ;; Here we assume that the coding-system will
-                               ;; replace each char with a single byte.
-                               ;; This is always the case if `content' is
-                               ;; a unibyte string.
-                              (length content)
-                              sieve-manage-client-eol content))
-    (sieve-manage-parse-okno)))
-
-(defun sieve-manage-deletescript (name &optional buffer)
-  (with-current-buffer (or buffer (current-buffer))
-    (sieve-manage-send (format "DELETESCRIPT \"%s\"" name))
-    (sieve-manage-parse-okno)))
-
-(defun sieve-manage-getscript (name output-buffer &optional buffer)
-  (with-current-buffer (or buffer (current-buffer))
-    (sieve-manage-send (format "GETSCRIPT \"%s\"" name))
-    (let ((script (sieve-manage-parse-string)))
-      (sieve-manage-parse-crlf)
-      (with-current-buffer output-buffer
-       (insert script))
-      (sieve-manage-parse-okno))))
-
-(defun sieve-manage-setactive (name &optional buffer)
-  (with-current-buffer (or buffer (current-buffer))
-    (sieve-manage-send (format "SETACTIVE \"%s\"" name))
-    (sieve-manage-parse-okno)))
-
-;; Protocol parsing routines
-
-(defun sieve-manage-wait-for-answer ()
-  (let ((pattern "^\\(OK\\|NO\\).*\n")
-        pos)
-    (while (not pos)
-      (setq pos (search-forward-regexp pattern nil t))
-      (goto-char (point-min))
-      (sleep-for 0 50))
-    pos))
-
-(defun sieve-manage-drop-next-answer ()
-  (sieve-manage-wait-for-answer)
-  (sieve-manage-erase))
-
-(defun sieve-manage-ok-p (rsp)
-  (string= (downcase (or (car-safe rsp) "")) "ok"))
-
-(defun sieve-manage-is-okno ()
-  (when (looking-at (concat
-                    "^\\(OK\\|NO\\)\\( (\\([^)]+\\))\\)?\\( \\(.*\\)\\)?"
-                    sieve-manage-server-eol))
-    (let ((status (match-string 1))
-         (resp-code (match-string 3))
-         (response (match-string 5)))
-      (when response
-       (goto-char (match-beginning 5))
-       (setq response (sieve-manage-is-string)))
-      (list status resp-code response))))
-
-(defun sieve-manage-parse-okno ()
-  (let (rsp)
-    (while (null rsp)
-      (accept-process-output (get-buffer-process (current-buffer)) 1)
-      (goto-char (point-min))
-      (setq rsp (sieve-manage-is-okno)))
-    (sieve-manage-erase)
-    rsp))
-
-(defun sieve-manage-parse-capability (str)
-  "Parse managesieve capability string `STR'.
-Set variable `sieve-manage-capability' to "
-  (let ((capas (delq nil
-                     (mapcar #'split-string-and-unquote
-                             (split-string str "\n")))))
-    (when (string= "OK" (caar (last capas)))
-      (setq sieve-manage-state 'nonauth))
-    capas))
-
-(defun sieve-manage-is-string ()
-  (cond ((looking-at "\"\\([^\"]+\\)\"")
-        (prog1
-            (match-string 1)
-          (goto-char (match-end 0))))
-       ((looking-at (concat "{\\([0-9]+\\+?\\)}" sieve-manage-server-eol))
-        (let ((pos (match-end 0))
-              (len (string-to-number (match-string 1))))
-          (if (< (point-max) (+ pos len))
-              nil
-            (goto-char (+ pos len))
-            (buffer-substring pos (+ pos len)))))))
-
-(defun sieve-manage-parse-string ()
-  (let (rsp)
-    (while (null rsp)
-      (accept-process-output (get-buffer-process (current-buffer)) 1)
-      (goto-char (point-min))
-      (setq rsp (sieve-manage-is-string)))
-    (sieve-manage-erase (point))
-    rsp))
-
-(defun sieve-manage-parse-crlf ()
-  (when (looking-at sieve-manage-server-eol)
-    (sieve-manage-erase (match-end 0))))
-
-(defun sieve-manage-parse-listscripts ()
-  (let (tmp rsp data)
-    (while (null rsp)
-      (while (null (or (setq rsp (sieve-manage-is-okno))
-                      (setq tmp (sieve-manage-is-string))))
-       (accept-process-output (get-buffer-process (current-buffer)) 1)
-       (goto-char (point-min)))
-      (when tmp
-       (while (not (looking-at (concat "\\( ACTIVE\\)?"
-                                       sieve-manage-server-eol)))
-         (accept-process-output (get-buffer-process (current-buffer)) 1)
-         (goto-char (point-min)))
-       (if (match-string 1)
-           (push (cons 'active tmp) data)
-         (push tmp data))
-       (goto-char (match-end 0))
-       (setq tmp nil)))
-    (sieve-manage-erase)
-    (if (sieve-manage-ok-p rsp)
-       data
-      rsp)))
-
-(defun sieve-manage-send (cmdstr)
-  (setq cmdstr (concat cmdstr sieve-manage-client-eol))
-  (and sieve-manage-log
-       (with-current-buffer (get-buffer-create sieve-manage-log)
-        (mm-enable-multibyte)
-        (buffer-disable-undo)
-        (goto-char (point-max))
-        (insert cmdstr)))
-  (process-send-string sieve-manage-process cmdstr))
-
-(provide 'sieve-manage)
-
-;; sieve-manage.el ends here
diff --git a/lisp/gnus/sieve-mode.el b/lisp/gnus/sieve-mode.el
deleted file mode 100644 (file)
index 7575ba6..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-;;; sieve-mode.el --- Sieve code editing commands for Emacs
-
-;; Copyright (C) 2001-2016 Free Software Foundation, Inc.
-
-;; Author: Simon Josefsson <simon@josefsson.org>
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This file contain editing mode functions and font-lock support for
-;; editing Sieve scripts.  It sets up C-mode with support for
-;; sieve-style #-comments and a lightly hacked syntax table.  It was
-;; strongly influenced by awk-mode.el.
-;;
-;; Put something similar to the following in your .emacs to use this file:
-;;
-;; (load "~/lisp/sieve")
-;; (setq auto-mode-alist (cons '("\\.siv\\'" . sieve-mode) auto-mode-alist))
-;;
-;; References:
-;;
-;; RFC 3028,
-;; "Sieve: A Mail Filtering Language",
-;; by Tim Showalter.
-;;
-;; Release history:
-;;
-;; 2001-03-02 version 1.0 posted to gnu.emacs.sources
-;;            version 1.1 change file extension into ".siv" (official one)
-;;                        added keymap and menubar to hook into sieve-manage
-;; 2001-10-31 version 1.2 committed to Oort Gnus
-
-;;; Code:
-
-(autoload 'sieve-manage "sieve")
-(autoload 'sieve-upload "sieve")
-(eval-when-compile
-  (require 'font-lock))
-
-(defgroup sieve nil
-  "Sieve."
-  :group 'languages)
-
-(defcustom sieve-mode-hook nil
-  "Hook run in sieve mode buffers."
-  :group 'sieve
-  :type 'hook)
-
-;; Font-lock
-
-(defvar sieve-control-commands-face 'sieve-control-commands
-  "Face name used for Sieve Control Commands.")
-
-(defface sieve-control-commands
-  '((((type tty) (class color)) (:foreground "blue" :weight light))
-    (((class grayscale) (background light)) (:foreground "LightGray" :bold t))
-    (((class grayscale) (background dark)) (:foreground "DimGray" :bold t))
-    (((class color) (background light)) (:foreground "Orchid"))
-    (((class color) (background dark)) (:foreground "LightSteelBlue"))
-    (t (:bold t)))
-  "Face used for Sieve Control Commands."
-  :group 'sieve)
-;; backward-compatibility alias
-(put 'sieve-control-commands-face 'face-alias 'sieve-control-commands)
-(put 'sieve-control-commands-face 'obsolete-face "22.1")
-
-(defvar sieve-action-commands-face 'sieve-action-commands
-  "Face name used for Sieve Action Commands.")
-
-(defface sieve-action-commands
-  '((((type tty) (class color)) (:foreground "blue" :weight bold))
-    (((class color) (background light)) (:foreground "Blue"))
-    (((class color) (background dark)) (:foreground "LightSkyBlue"))
-    (t (:inverse-video t :bold t)))
-  "Face used for Sieve Action Commands."
-  :group 'sieve)
-;; backward-compatibility alias
-(put 'sieve-action-commands-face 'face-alias 'sieve-action-commands)
-(put 'sieve-action-commands-face 'obsolete-face "22.1")
-
-(defvar sieve-test-commands-face 'sieve-test-commands
-  "Face name used for Sieve Test Commands.")
-
-(defface sieve-test-commands
-  '((((type tty) (class color)) (:foreground "magenta"))
-    (((class grayscale) (background light))
-     (:foreground "LightGray" :bold t :underline t))
-    (((class grayscale) (background dark))
-     (:foreground "Gray50" :bold t :underline t))
-    (((class color) (background light)) (:foreground "CadetBlue"))
-    (((class color) (background dark)) (:foreground "Aquamarine"))
-    (t (:bold t :underline t)))
-  "Face used for Sieve Test Commands."
-  :group 'sieve)
-;; backward-compatibility alias
-(put 'sieve-test-commands-face 'face-alias 'sieve-test-commands)
-(put 'sieve-test-commands-face 'obsolete-face "22.1")
-
-(defvar sieve-tagged-arguments-face 'sieve-tagged-arguments
-  "Face name used for Sieve Tagged Arguments.")
-
-(defface sieve-tagged-arguments
-  '((((type tty) (class color)) (:foreground "cyan" :weight bold))
-    (((class grayscale) (background light)) (:foreground "LightGray" :bold t))
-    (((class grayscale) (background dark)) (:foreground "DimGray" :bold t))
-    (((class color) (background light)) (:foreground "Purple"))
-    (((class color) (background dark)) (:foreground "Cyan"))
-    (t (:bold t)))
-  "Face used for Sieve Tagged Arguments."
-  :group 'sieve)
-;; backward-compatibility alias
-(put 'sieve-tagged-arguments-face 'face-alias 'sieve-tagged-arguments)
-(put 'sieve-tagged-arguments-face 'obsolete-face "22.1")
-
-
-(defconst sieve-font-lock-keywords
-  (eval-when-compile
-    (list
-     ;; control commands
-     (cons (regexp-opt '("require" "if" "else" "elsif" "stop")
-                       'words)
-          'sieve-control-commands-face)
-     ;; action commands
-     (cons (regexp-opt '("fileinto" "redirect" "reject" "keep" "discard")
-                       'words)
-          'sieve-action-commands-face)
-     ;; test commands
-     (cons (regexp-opt '("address" "allof" "anyof" "exists" "false"
-                        "true" "header" "not" "size" "envelope"
-                         "body")
-                       'words)
-          'sieve-test-commands-face)
-     (cons "\\Sw+:\\sw+"
-          'sieve-tagged-arguments-face))))
-
-;; Syntax table
-
-(defvar sieve-mode-syntax-table nil
-  "Syntax table in use in sieve-mode buffers.")
-
-(if sieve-mode-syntax-table
-    ()
-  (setq sieve-mode-syntax-table (make-syntax-table))
-  (modify-syntax-entry ?\\ "\\" sieve-mode-syntax-table)
-  (modify-syntax-entry ?\n ">   " sieve-mode-syntax-table)
-  (modify-syntax-entry ?\f ">   " sieve-mode-syntax-table)
-  (modify-syntax-entry ?\# "<   " sieve-mode-syntax-table)
-  (modify-syntax-entry ?/ "." sieve-mode-syntax-table)
-  (modify-syntax-entry ?* "." sieve-mode-syntax-table)
-  (modify-syntax-entry ?+ "." sieve-mode-syntax-table)
-  (modify-syntax-entry ?- "." sieve-mode-syntax-table)
-  (modify-syntax-entry ?= "." sieve-mode-syntax-table)
-  (modify-syntax-entry ?% "." sieve-mode-syntax-table)
-  (modify-syntax-entry ?< "." sieve-mode-syntax-table)
-  (modify-syntax-entry ?> "." sieve-mode-syntax-table)
-  (modify-syntax-entry ?& "." sieve-mode-syntax-table)
-  (modify-syntax-entry ?| "." sieve-mode-syntax-table)
-  (modify-syntax-entry ?_ "_" sieve-mode-syntax-table)
-  (modify-syntax-entry ?\' "\"" sieve-mode-syntax-table))
-
-;; Key map definition
-
-(defvar sieve-mode-map
-  (let ((map (make-sparse-keymap)))
-    (define-key map "\C-c\C-l" 'sieve-upload)
-    (define-key map "\C-c\C-c" 'sieve-upload-and-kill)
-    (define-key map "\C-c\C-m" 'sieve-manage)
-    map)
-  "Key map used in sieve mode.")
-
-;; Menu definition
-
-(defvar sieve-mode-menu nil
-  "Menubar used in sieve mode.")
-
-;; Code for Sieve editing mode.
-(autoload 'easy-menu-add-item "easymenu")
-
-;;;###autoload
-(define-derived-mode sieve-mode c-mode "Sieve"
-  "Major mode for editing Sieve code.
-This is much like C mode except for the syntax of comments.  Its keymap
-inherits from C mode's and it has the same variables for customizing
-indentation.  It has its own abbrev table and its own syntax table.
-
-Turning on Sieve mode runs `sieve-mode-hook'."
-  (set (make-local-variable 'paragraph-start) (concat "$\\|" page-delimiter))
-  (set (make-local-variable 'paragraph-separate) paragraph-start)
-  (set (make-local-variable 'comment-start) "#")
-  (set (make-local-variable 'comment-end) "")
-  ;;(set (make-local-variable 'comment-start-skip) "\\(^\\|\\s-\\);?#+ *")
-  (set (make-local-variable 'comment-start-skip) "#+ *")
-  (set (make-local-variable 'font-lock-defaults)
-       '(sieve-font-lock-keywords nil nil ((?_ . "w"))))
-  (easy-menu-add-item nil nil sieve-mode-menu))
-
-;; Menu
-
-(easy-menu-define sieve-mode-menu sieve-mode-map
-  "Sieve Menu."
-  '("Sieve"
-    ["Upload script" sieve-upload t]
-    ["Manage scripts on server" sieve-manage t]))
-
-(provide 'sieve-mode)
-
-;; sieve-mode.el ends here
diff --git a/lisp/gnus/sieve.el b/lisp/gnus/sieve.el
deleted file mode 100644 (file)
index 2046e53..0000000
+++ /dev/null
@@ -1,372 +0,0 @@
-;;; sieve.el --- Utilities to manage sieve scripts
-
-;; Copyright (C) 2001-2016 Free Software Foundation, Inc.
-
-;; Author: Simon Josefsson <simon@josefsson.org>
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This file contain utilities to facilitate upload, download and
-;; general management of sieve scripts.  Currently only the
-;; Managesieve protocol is supported (using sieve-manage.el), but when
-;; (useful) alternatives become available, they might be supported as
-;; well.
-;;
-;; The cursor navigation was inspired by biff-mode by Franklin Lee.
-;;
-;; Release history:
-;;
-;; 2001-10-31 Committed to Oort Gnus.
-;; 2002-07-27 Fix down-mouse-2 and down-mouse-3 in manage-mode.  Fix menubar
-;;            in manage-mode.  Change some messages.  Added sieve-deactivate*,
-;;            sieve-remove.  Fixed help text in manage-mode.  Suggested by
-;;            Ned Ludd.
-;;
-;; Todo:
-;;
-;; * Namespace?  This file contains `sieve-manage' and
-;;   `sieve-manage-mode', but there is a sieve-manage.el file as well.
-;;   Can't think of a good solution though, this file need a *-mode,
-;;   and naming it `sieve-mode' would collide with sieve-mode.el.  One
-;;   solution would be to come up with some better name that this file
-;;   can use that doesn't have the managesieve specific "manage" in
-;;   it.  sieve-dired?  i dunno.  we could copy all off sieve.el into
-;;   sieve-manage.el too, but I'd like to separate the interface from
-;;   the protocol implementation since the backends are likely to
-;;   change (well).
-;;
-;; * Define servers?  We could have a customize buffer to create a server,
-;;   with authentication/stream/etc parameters, much like Gnus, and then
-;;   only use names of defined servers when interacting with M-x sieve-*.
-;;   Right now you can't use STARTTLS, which sieve-manage.el provides
-
-;;; Code:
-
-(require 'sieve-manage)
-(require 'sieve-mode)
-
-;; User customizable variables:
-
-(defgroup sieve nil
-  "Manage sieve scripts."
-  :version "22.1"
-  :group 'tools)
-
-(defcustom sieve-new-script "<new script>"
-  "Name of name script indicator."
-  :type 'string
-  :group 'sieve)
-
-(defcustom sieve-buffer "*sieve*"
-  "Name of sieve management buffer."
-  :type 'string
-  :group 'sieve)
-
-(defcustom sieve-template "\
-require \"fileinto\";
-
-# Example script (remove comment character '#' to make it effective!):
-#
-# if header :contains \"from\" \"coyote\" {
-#   discard;
-# } elsif header :contains [\"subject\"] [\"$$$\"] {
-#   discard;
-# } else {
-#  fileinto \"INBOX\";
-# }
-"
-  "Template sieve script."
-  :type 'string
-  :group 'sieve)
-
-;; Internal variables:
-
-(defvar sieve-manage-buffer nil)
-(defvar sieve-buffer-header-end nil)
-(defvar sieve-buffer-script-name nil
-  "The real script name of the buffer.")
-(make-local-variable 'sieve-buffer-script-name)
-
-;; Sieve-manage mode:
-
-(defvar sieve-manage-mode-map
-  (let ((map (make-sparse-keymap)))
-    ;; various
-    (define-key map "?" 'sieve-help)
-    (define-key map "h" 'sieve-help)
-    ;; activating
-    (define-key map "m" 'sieve-activate)
-    (define-key map "u" 'sieve-deactivate)
-    (define-key map "\M-\C-?" 'sieve-deactivate-all)
-    ;; navigation keys
-    (define-key map "\C-p" 'sieve-prev-line)
-    (define-key map [up] 'sieve-prev-line)
-    (define-key map "\C-n" 'sieve-next-line)
-    (define-key map [down] 'sieve-next-line)
-    (define-key map " " 'sieve-next-line)
-    (define-key map "n" 'sieve-next-line)
-    (define-key map "p" 'sieve-prev-line)
-    (define-key map "\C-m" 'sieve-edit-script)
-    (define-key map "f" 'sieve-edit-script)
-    (define-key map "o" 'sieve-edit-script-other-window)
-    (define-key map "r" 'sieve-remove)
-    (define-key map "q" 'sieve-bury-buffer)
-    (define-key map "Q" 'sieve-manage-quit)
-    (define-key map [(down-mouse-2)] 'sieve-edit-script)
-    (define-key map [(down-mouse-3)] 'sieve-manage-mode-menu)
-    map)
-  "Keymap for `sieve-manage-mode'.")
-
-(easy-menu-define sieve-manage-mode-menu sieve-manage-mode-map
-  "Sieve Menu."
-  '("Manage Sieve"
-    ["Edit script" sieve-edit-script t]
-    ["Activate script" sieve-activate t]
-    ["Deactivate script" sieve-deactivate t]))
-
-(define-derived-mode sieve-manage-mode fundamental-mode "Sieve-manage"
-  "Mode used for sieve script management."
-  (buffer-disable-undo (current-buffer))
-  (setq truncate-lines t)
-  (easy-menu-add sieve-manage-mode-menu sieve-manage-mode-map))
-
-(put 'sieve-manage-mode 'mode-class 'special)
-
-;; Commands used in sieve-manage mode:
-
-(defun sieve-manage-quit ()
-  "Quit Manage Sieve and close the connection."
-  (interactive)
-  (sieve-manage-close sieve-manage-buffer)
-  (kill-buffer sieve-manage-buffer)
-  (kill-buffer (current-buffer)))
-
-(defun sieve-bury-buffer ()
-  "Bury the Manage Sieve buffer without closing the connection."
-  (interactive)
-  (bury-buffer))
-
-(defun sieve-activate (&optional pos)
-  (interactive "d")
-  (let ((name (sieve-script-at-point)) err)
-    (when (or (null name) (string-equal name sieve-new-script))
-      (error "No sieve script at point"))
-    (message "Activating script %s..." name)
-    (setq err (sieve-manage-setactive name sieve-manage-buffer))
-    (sieve-refresh-scriptlist)
-    (if (sieve-manage-ok-p err)
-       (message "Activating script %s...done" name)
-      (message "Activating script %s...failed: %s" name (nth 2 err)))))
-
-(defun sieve-deactivate-all (&optional pos)
-  (interactive "d")
-  (let ((name (sieve-script-at-point)) err)
-    (message "Deactivating scripts...")
-    (setq err (sieve-manage-setactive "" sieve-manage-buffer))
-    (sieve-refresh-scriptlist)
-    (if (sieve-manage-ok-p err)
-       (message "Deactivating scripts...done")
-      (message "Deactivating scripts...failed: %s" (nth 2 err)))))
-
-(defalias 'sieve-deactivate 'sieve-deactivate-all)
-
-(defun sieve-remove (&optional pos)
-  (interactive "d")
-  (let ((name (sieve-script-at-point)) err)
-    (when (or (null name) (string-equal name sieve-new-script))
-      (error "No sieve script at point"))
-    (message "Removing sieve script %s..." name)
-    (setq err (sieve-manage-deletescript name sieve-manage-buffer))
-    (unless (sieve-manage-ok-p err)
-      (error "Removing sieve script %s...failed: " err))
-    (sieve-refresh-scriptlist)
-    (message "Removing sieve script %s...done" name)))
-
-(defun sieve-edit-script (&optional pos)
-  (interactive "d")
-  (let ((name (sieve-script-at-point)))
-    (unless name
-      (error "No sieve script at point"))
-    (if (not (string-equal name sieve-new-script))
-       (let ((newbuf (generate-new-buffer name))
-             err)
-         (setq err (sieve-manage-getscript name newbuf sieve-manage-buffer))
-         (switch-to-buffer newbuf)
-         (unless (sieve-manage-ok-p err)
-           (error "Sieve download failed: %s" err)))
-      (switch-to-buffer (get-buffer-create "template.siv"))
-      (insert sieve-template))
-    (sieve-mode)
-    (setq sieve-buffer-script-name name)
-    (goto-char (point-min))
-    (message
-     (substitute-command-keys
-      "Press \\[sieve-upload] to upload script to server."))))
-
-(defmacro sieve-change-region (&rest body)
-  "Turns off sieve-region before executing BODY, then re-enables it after.
-Used to bracket operations which move point in the sieve-buffer."
-  `(progn
-     (sieve-highlight nil)
-     ,@body
-     (sieve-highlight t)))
-(put 'sieve-change-region 'lisp-indent-function 0)
-
-(defun sieve-next-line (&optional arg)
-  (interactive)
-  (unless arg
-    (setq arg 1))
-  (if (save-excursion
-       (forward-line arg)
-       (sieve-script-at-point))
-      (sieve-change-region
-       (forward-line arg))
-    (message "End of list")))
-
-(defun sieve-prev-line (&optional arg)
-  (interactive)
-  (unless arg
-    (setq arg -1))
-  (if (save-excursion
-       (forward-line arg)
-       (sieve-script-at-point))
-      (sieve-change-region
-       (forward-line arg))
-    (message "Beginning of list")))
-
-(defun sieve-help ()
-  "Display help for various sieve commands."
-  (interactive)
-  (if (eq last-command 'sieve-help)
-      ;; would need minor-mode for log-edit-mode
-      (describe-function 'sieve-mode)
-    (message "%s" (substitute-command-keys
-             "`\\[sieve-edit-script]':edit `\\[sieve-activate]':activate `\\[sieve-deactivate]':deactivate `\\[sieve-remove]':remove"))))
-
-;; Create buffer:
-
-(defun sieve-setup-buffer (server port)
-  (setq buffer-read-only nil)
-  (erase-buffer)
-  (buffer-disable-undo)
-  (let* ((port (or port sieve-manage-default-port))
-         (header (format "Server : %s:%s\n\n" server port)))
-    (insert header))
-  (set (make-local-variable 'sieve-buffer-header-end)
-       (point-max)))
-
-(defun sieve-script-at-point (&optional pos)
-  "Return name of sieve script at point POS, or nil."
-  (interactive "d")
-  (get-char-property (or pos (point)) 'script-name))
-
-(defun sieve-highlight (on)
-  "Turn ON or off highlighting on the current language overlay."
-  (overlay-put (car (overlays-at (point))) 'face (if on 'highlight 'default)))
-
-(defun sieve-insert-scripts (scripts)
-  "Format and insert LANGUAGE-LIST strings into current buffer at point."
-  (while scripts
-    (let ((p (point))
-         (ext nil)
-         (script (pop scripts)))
-      (if (consp script)
-         (insert (format " ACTIVE %s" (cdr script)))
-       (insert (format "        %s" script)))
-      (setq ext (make-overlay p (point)))
-      (overlay-put ext 'mouse-face 'highlight)
-      (overlay-put ext 'script-name (if (consp script)
-                                       (cdr script)
-                                     script))
-      (insert "\n"))))
-
-(defun sieve-open-server (server &optional port)
-  "Open SERVER (on PORT) and authenticate."
-  (with-current-buffer
-      (or ;; open server
-       (set (make-local-variable 'sieve-manage-buffer)
-           (sieve-manage-open server port))
-       (error "Error opening server %s" server))
-    (sieve-manage-authenticate)))
-
-(defun sieve-refresh-scriptlist ()
-  (interactive)
-  (with-current-buffer sieve-buffer
-    (setq buffer-read-only nil)
-    (delete-region (or sieve-buffer-header-end (point-max)) (point-max))
-    (goto-char (point-max))
-    ;; get list of script names and print them
-    (let ((scripts (sieve-manage-listscripts sieve-manage-buffer)))
-      (if (null scripts)
-         (insert
-           (substitute-command-keys
-            (format
-             "No scripts on server, press \\[sieve-edit-script] on %s to create a new script.\n"
-             sieve-new-script)))
-       (insert
-         (substitute-command-keys
-          (format (concat "%d script%s on server, press \\[sieve-edit-script] on a script "
-                          "name edits it, or\npress \\[sieve-edit-script] on %s to create "
-                          "a new script.\n") (length scripts)
-                          (if (eq (length scripts) 1) "" "s")
-                          sieve-new-script))))
-      (save-excursion
-       (sieve-insert-scripts (list sieve-new-script))
-       (sieve-insert-scripts scripts)))
-    (sieve-highlight t)
-    (setq buffer-read-only t)))
-
-;;;###autoload
-(defun sieve-manage (server &optional port)
-  (interactive "sServer: ")
-  (switch-to-buffer (get-buffer-create sieve-buffer))
-  (sieve-manage-mode)
-  (sieve-setup-buffer server port)
-  (if (sieve-open-server server port)
-      (sieve-refresh-scriptlist)
-    (message "Could not open server %s" server)))
-
-;;;###autoload
-(defun sieve-upload (&optional name)
-  (interactive)
-  (when (or (get-buffer sieve-buffer) (call-interactively 'sieve-manage))
-    (let ((script (buffer-string)) err)
-      (with-current-buffer (get-buffer sieve-buffer)
-       (setq err (sieve-manage-putscript
-                   (or name sieve-buffer-script-name (buffer-name))
-                   script sieve-manage-buffer))
-       (if (sieve-manage-ok-p err)
-           (message (substitute-command-keys
-                     "Sieve upload done.  Use \\[sieve-manage] to manage scripts."))
-         (message "Sieve upload failed: %s" (nth 2 err)))))))
-
-;;;###autoload
-(defun sieve-upload-and-bury (&optional name)
-  (interactive)
-  (sieve-upload name)
-  (bury-buffer))
-
-;;;###autoload
-(defun sieve-upload-and-kill (&optional name)
-  (interactive)
-  (sieve-upload name)
-  (kill-buffer))
-
-(provide 'sieve)
-
-;; sieve.el ends here
diff --git a/lisp/gnus/starttls.el b/lisp/gnus/starttls.el
deleted file mode 100644 (file)
index 096ed2a..0000000
+++ /dev/null
@@ -1,304 +0,0 @@
-;;; starttls.el --- STARTTLS functions
-
-;; Copyright (C) 1999-2016 Free Software Foundation, Inc.
-
-;; Author: Daiki Ueno <ueno@unixuser.org>
-;; Author: Simon Josefsson <simon@josefsson.org>
-;; Created: 1999/11/20
-;; Keywords: TLS, SSL, OpenSSL, GnuTLS, mail, news
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; This module defines some utility functions for STARTTLS profiles.
-
-;; [RFC 2595] "Using TLS with IMAP, POP3 and ACAP"
-;;     by Chris Newman <chris.newman@innosoft.com> (1999/06)
-
-;; This file now contains a combination of the two previous
-;; implementations both called "starttls.el".  The first one is Daiki
-;; Ueno's starttls.el which uses his own "starttls" command line tool,
-;; and the second one is Simon Josefsson's starttls.el which uses
-;; "gnutls-cli" from GnuTLS.
-;;
-;; If "starttls" is available, it is preferred by the code over
-;; "gnutls-cli", for backwards compatibility.  Use
-;; `starttls-use-gnutls' to toggle between implementations if you have
-;; both tools installed.  It is recommended to use GnuTLS, though, as
-;; it performs more verification of the certificates.
-
-;; The GnuTLS support requires GnuTLS 0.9.90 (released 2003-10-08) or
-;; later, from <http://www.gnu.org/software/gnutls/>, or "starttls"
-;; from <ftp://ftp.opaopa.org/pub/elisp/>.
-
-;; Usage is similar to `open-network-stream'.  For example:
-;;
-;; (when (setq tmp (starttls-open-stream
-;;                     "test" (current-buffer) "yxa.extundo.com" 25))
-;;   (accept-process-output tmp 15)
-;;   (process-send-string tmp "STARTTLS\n")
-;;   (accept-process-output tmp 15)
-;;   (message "STARTTLS output:\n%s" (starttls-negotiate tmp))
-;;   (process-send-string tmp "EHLO foo\n"))
-
-;; An example run yields the following output:
-;;
-;; 220 yxa.extundo.com ESMTP Sendmail 8.12.11/8.12.11/Debian-3; Wed, 26 May 2004 19:12:29 +0200; (No UCE/UBE) logging access from: c494102a.s-bi.bostream.se(OK)-c494102a.s-bi.bostream.se [217.215.27.65]
-;; 220 2.0.0 Ready to start TLS
-;; 250-yxa.extundo.com Hello c494102a.s-bi.bostream.se [217.215.27.65], pleased to meet you
-;; 250-ENHANCEDSTATUSCODES
-;; 250-PIPELINING
-;; 250-EXPN
-;; 250-VERB
-;; 250-8BITMIME
-;; 250-SIZE
-;; 250-DSN
-;; 250-ETRN
-;; 250-AUTH DIGEST-MD5 CRAM-MD5 PLAIN LOGIN
-;; 250-DELIVERBY
-;; 250 HELP
-;; nil
-;;
-;; With the message buffer containing:
-;;
-;; STARTTLS output:
-;; *** Starting TLS handshake
-;; - Server's trusted authorities:
-;;    [0]: C=SE,ST=Stockholm,L=Stockholm,O=YXA,OU=CA,CN=yxa.extundo.com,EMAIL=staff@yxa.extundo.com
-;; - Certificate type: X.509
-;;  - Got a certificate list of 2 certificates.
-;;
-;;  - Certificate[0] info:
-;;  # The hostname in the certificate matches 'yxa.extundo.com'.
-;;  # valid since: Wed May 26 12:16:00 CEST 2004
-;;  # expires at: Wed Jul 26 12:16:00 CEST 2023
-;;  # serial number: 04
-;;  # fingerprint: 7c 04 4b c1 fa 26 9b 5d 90 22 52 3c 65 3d 85 3a
-;;  # version: #1
-;;  # public key algorithm: RSA
-;;  #   Modulus: 1024 bits
-;;  # Subject's DN: C=SE,ST=Stockholm,L=Stockholm,O=YXA,OU=Mail server,CN=yxa.extundo.com,EMAIL=staff@yxa.extundo.com
-;;  # Issuer's DN: C=SE,ST=Stockholm,L=Stockholm,O=YXA,OU=CA,CN=yxa.extundo.com,EMAIL=staff@yxa.extundo.com
-;;
-;;  - Certificate[1] info:
-;;  # valid since: Sun May 23 11:35:00 CEST 2004
-;;  # expires at: Sun Jul 23 11:35:00 CEST 2023
-;;  # serial number: 00
-;;  # fingerprint: fc 76 d8 63 1a c9 0b 3b fa 40 fe ed 47 7a 58 ae
-;;  # version: #3
-;;  # public key algorithm: RSA
-;;  #   Modulus: 1024 bits
-;;  # Subject's DN: C=SE,ST=Stockholm,L=Stockholm,O=YXA,OU=CA,CN=yxa.extundo.com,EMAIL=staff@yxa.extundo.com
-;;  # Issuer's DN: C=SE,ST=Stockholm,L=Stockholm,O=YXA,OU=CA,CN=yxa.extundo.com,EMAIL=staff@yxa.extundo.com
-;;
-;; - Peer's certificate issuer is unknown
-;; - Peer's certificate is NOT trusted
-;; - Version: TLS 1.0
-;; - Key Exchange: RSA
-;; - Cipher: ARCFOUR 128
-;; - MAC: SHA
-;; - Compression: NULL
-
-;;; Code:
-
-(defgroup starttls nil
-  "Support for `Transport Layer Security' protocol."
-  :version "21.1"
-  :group 'mail)
-
-(defcustom starttls-gnutls-program "gnutls-cli"
-  "Name of GnuTLS command line tool.
-This program is used when GnuTLS is used, i.e. when
-`starttls-use-gnutls' is non-nil."
-  :version "22.1"
-  :type 'string
-  :group 'starttls)
-
-(defcustom starttls-program "starttls"
-  "The program to run in a subprocess to open an TLSv1 connection.
-This program is used when the `starttls' command is used,
-i.e. when `starttls-use-gnutls' is nil."
-  :type 'string
-  :group 'starttls)
-
-(defcustom starttls-use-gnutls (not (executable-find starttls-program))
-  "*Whether to use GnuTLS instead of the `starttls' command."
-  :version "22.1"
-  :type 'boolean
-  :group 'starttls)
-
-(defcustom starttls-extra-args nil
-  "Extra arguments to `starttls-program'.
-These apply when the `starttls' command is used, i.e. when
-`starttls-use-gnutls' is nil."
-  :type '(repeat string)
-  :group 'starttls)
-
-(defcustom starttls-extra-arguments nil
-  "Extra arguments to `starttls-gnutls-program'.
-These apply when GnuTLS is used, i.e. when `starttls-use-gnutls' is non-nil.
-
-For example, non-TLS compliant servers may require
-'(\"--protocols\" \"ssl3\").  Invoke \"gnutls-cli --help\" to
-find out which parameters are available."
-  :version "22.1"
-  :type '(repeat string)
-  :group 'starttls)
-
-(defcustom starttls-process-connection-type nil
-  "*Value for `process-connection-type' to use when starting STARTTLS process."
-  :version "22.1"
-  :type 'boolean
-  :group 'starttls)
-
-(defcustom starttls-connect "- Simple Client Mode:\n\n"
-  "*Regular expression indicating successful connection.
-The default is what GnuTLS's \"gnutls-cli\" outputs."
-  ;; GnuTLS cli.c:main() prints this string when it is starting to run
-  ;; in the application read/write phase.  If the logic, or the string
-  ;; itself, is modified, this must be updated.
-  :version "22.1"
-  :type 'regexp
-  :group 'starttls)
-
-(defcustom starttls-failure "\\*\\*\\* Handshake has failed"
-  "*Regular expression indicating failed TLS handshake.
-The default is what GnuTLS's \"gnutls-cli\" outputs."
-  ;; GnuTLS cli.c:do_handshake() prints this string on failure.  If the
-  ;; logic, or the string itself, is modified, this must be updated.
-  :version "22.1"
-  :type 'regexp
-  :group 'starttls)
-
-(defcustom starttls-success "- Compression: "
-  "*Regular expression indicating completed TLS handshakes.
-The default is what GnuTLS's \"gnutls-cli\" outputs."
-  ;; GnuTLS cli.c:do_handshake() calls, on success,
-  ;; common.c:print_info(), that unconditionally print this string
-  ;; last.  If that logic, or the string itself, is modified, this
-  ;; must be updated.
-  :version "22.1"
-  :type 'regexp
-  :group 'starttls)
-
-(defun starttls-negotiate-gnutls (process)
-  "Negotiate TLS on PROCESS opened by `open-starttls-stream'.
-This should typically only be done once.  It typically returns a
-multi-line informational message with information about the
-handshake, or nil on failure."
-  (let (buffer info old-max done-ok done-bad)
-    (if (null (setq buffer (process-buffer process)))
-       ;; XXX How to remove/extract the TLS negotiation junk?
-       (signal-process (process-id process) 'SIGALRM)
-      (with-current-buffer buffer
-       (save-excursion
-         (setq old-max (goto-char (point-max)))
-         (signal-process (process-id process) 'SIGALRM)
-         (while (and (processp process)
-                     (eq (process-status process) 'run)
-                     (save-excursion
-                       (goto-char old-max)
-                       (not (or (setq done-ok (re-search-forward
-                                               starttls-success nil t))
-                                (setq done-bad (re-search-forward
-                                                starttls-failure nil t))))))
-           (accept-process-output process 1 100)
-           (sit-for 0.1))
-         (setq info (buffer-substring-no-properties old-max (point-max)))
-         (delete-region old-max (point-max))
-         (if (or (and done-ok (not done-bad))
-                 ;; Prevent mitm that fake success msg after failure msg.
-                 (and done-ok done-bad (< done-ok done-bad)))
-             info
-           (message "STARTTLS negotiation failed: %s" info)
-           nil))))))
-
-(defun starttls-negotiate (process)
-  (if starttls-use-gnutls
-      (starttls-negotiate-gnutls process)
-    (signal-process (process-id process) 'SIGALRM)))
-
-(defun starttls-open-stream-gnutls (name buffer host port)
-  (message "Opening STARTTLS connection to `%s:%s'..." host port)
-  (let* (done
-        (old-max (with-current-buffer buffer (point-max)))
-        (process-connection-type starttls-process-connection-type)
-        (process (apply #'start-process name buffer
-                        starttls-gnutls-program "-s" host
-                        "-p" (if (integerp port)
-                                 (int-to-string port)
-                               port)
-                        starttls-extra-arguments)))
-    (set-process-query-on-exit-flag process nil)
-    (while (and (processp process)
-               (eq (process-status process) 'run)
-               (with-current-buffer buffer
-                 (goto-char old-max)
-                 (not (setq done (re-search-forward
-                                  starttls-connect nil t)))))
-      (accept-process-output process 0 100)
-      (sit-for 0.1))
-    (if done
-       (with-current-buffer buffer
-         (delete-region old-max done))
-      (delete-process process)
-      (setq process nil))
-    (message "Opening STARTTLS connection to `%s:%s'...%s"
-            host port (if done "done" "failed"))
-    process))
-
-;;;###autoload
-(defun starttls-open-stream (name buffer host port)
-  "Open a TLS connection for a port to a host.
-Returns a subprocess object to represent the connection.
-Input and output work as for subprocesses; `delete-process' closes it.
-Args are NAME BUFFER HOST PORT.
-NAME is name for process.  It is modified if necessary to make it unique.
-BUFFER is the buffer (or `buffer-name') to associate with the process.
- Process output goes at end of that buffer, unless you specify
- an output stream or filter function to handle the output.
- BUFFER may be also nil, meaning that this process is not associated
- with any buffer
-Third arg is name of the host to connect to, or its IP address.
-Fourth arg PORT is an integer specifying a port to connect to.
-If `starttls-use-gnutls' is nil, this may also be a service name, but
-GnuTLS requires a port number."
-  (if starttls-use-gnutls
-      (starttls-open-stream-gnutls name buffer host port)
-    (message "Opening STARTTLS connection to `%s:%s'" host (format "%s" port))
-    (let* ((process-connection-type starttls-process-connection-type)
-          (process (apply #'start-process
-                          name buffer starttls-program
-                          host (format "%s" port)
-                          starttls-extra-args)))
-      (set-process-query-on-exit-flag process nil)
-      process)))
-
-(defun starttls-available-p ()
-  "Say whether the STARTTLS programs are available."
-  (and (not (memq system-type '(windows-nt ms-dos)))
-       (executable-find (if starttls-use-gnutls
-                           starttls-gnutls-program
-                         starttls-program))))
-
-(defalias 'starttls-any-program-available 'starttls-available-p)
-(make-obsolete 'starttls-any-program-available 'starttls-available-p
-              "2011-08-02")
-
-(provide 'starttls)
-
-;;; starttls.el ends here
diff --git a/lisp/gnus/utf7.el b/lisp/gnus/utf7.el
deleted file mode 100644 (file)
index bd04eba..0000000
+++ /dev/null
@@ -1,236 +0,0 @@
-;;; utf7.el --- UTF-7 encoding/decoding for Emacs   -*-coding: utf-8;-*-
-
-;; Copyright (C) 1999-2016 Free Software Foundation, Inc.
-
-;; Author: Jon K Hellan <hellan@acm.org>
-;; Maintainer: bugs@gnus.org
-;; Keywords: mail
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; UTF-7 - A Mail-Safe Transformation Format of Unicode - RFC 2152
-;; This is a transformation format of Unicode that contains only 7-bit
-;; ASCII octets and is intended to be readable by humans in the limiting
-;; case that the document consists of characters from the US-ASCII
-;; repertoire.
-;; In short, runs of characters outside US-ASCII are encoded as base64
-;; inside delimiters.
-;; A variation of UTF-7 is specified in IMAP 4rev1 (RFC 2060) as the way
-;; to represent characters outside US-ASCII in mailbox names in IMAP.
-;; This library supports both variants, but the IMAP variation was the
-;; reason I wrote it.
-;; The routines convert UTF-7 -> UTF-16 (16 bit encoding of Unicode)
-;; -> current character set, and vice versa.
-;; However, until Emacs supports Unicode, the only Emacs character set
-;; supported here is ISO-8859.1, which can trivially be converted to/from
-;; Unicode.
-;; When decoding results in a character outside the Emacs character set,
-;; an error is thrown.  It is up to the application to recover.
-
-;; UTF-7 should be done by providing a coding system.  Mule-UCS does
-;; already, but I don't know if it does the IMAP version and it's not
-;; clear whether that should really be a coding system.  The UTF-16
-;; part of the conversion can be done with coding systems available
-;; with Mule-UCS or some versions of Emacs.  Unfortunately these were
-;; done wrongly (regarding handling of byte-order marks and how the
-;; variants were named), so we don't have a consistent name for the
-;; necessary coding system.  The code below doesn't seem to DTRT
-;; generally.  E.g.:
-;;
-;; (utf7-encode "a+£")
-;;   => "a+ACsAow-"
-;;
-;; $ echo "a+£"|iconv -f utf-8 -t utf-7
-;; a+-+AKM
-;;
-;;  -- fx
-
-
-;;; Code:
-
-(require 'base64)
-(eval-when-compile (require 'cl))
-(require 'mm-util)
-
-(defconst utf7-direct-encoding-chars " -%'-*,-[]-}"
-  "Character ranges which do not need escaping in UTF-7.")
-
-(defconst utf7-imap-direct-encoding-chars
-  (concat utf7-direct-encoding-chars "+\\~")
-  "Character ranges which do not need escaping in the IMAP variant of UTF-7.")
-
-(defconst utf7-utf-16-coding-system
-  (cond ((mm-coding-system-p 'utf-16-be-no-signature) ; Mule-UCS
-        'utf-16-be-no-signature)
-       ((and (mm-coding-system-p 'utf-16-be) ; Emacs
-             ;; Avoid versions with BOM.
-             (= 2 (length (encode-coding-string "a" 'utf-16-be))))
-        'utf-16-be)
-       ((mm-coding-system-p 'utf-16-be-nosig) ; ?
-        'utf-16-be-nosig))
-  "Coding system which encodes big endian UTF-16 without a BOM signature.")
-
-(defsubst utf7-imap-get-pad-length (len modulus)
-  "Return required length of padding for IMAP modified base64 fragment."
-  (mod (- len) modulus))
-
-(defun utf7-encode-internal (&optional for-imap)
-  "Encode text in (temporary) buffer as UTF-7.
-Use IMAP modification if FOR-IMAP is non-nil."
-  (let ((start (point-min))
-       (end (point-max)))
-    (narrow-to-region start end)
-    (goto-char start)
-    (let* ((esc-char (if for-imap ?& ?+))
-          (direct-encoding-chars
-           (if for-imap utf7-imap-direct-encoding-chars
-             utf7-direct-encoding-chars))
-          (not-direct-encoding-chars (concat "^" direct-encoding-chars)))
-      (while (not (eobp))
-       (skip-chars-forward direct-encoding-chars)
-       (unless (eobp)
-         (insert esc-char)
-         (let ((p (point))
-               (fc (following-char))
-               (run-length
-                (skip-chars-forward not-direct-encoding-chars)))
-           (if (and (= fc esc-char)
-                    (= run-length 1))  ; Lone esc-char?
-               (delete-char -1)        ; Now there's one too many
-             (utf7-fragment-encode p (point) for-imap))
-           (insert "-")))))))
-
-(defun utf7-fragment-encode (start end &optional for-imap)
-  "Encode text from START to END in buffer as UTF-7 escape fragment.
-Use IMAP modification if FOR-IMAP is non-nil."
-  (save-restriction
-    (let* ((buf (current-buffer))
-          (base (with-temp-buffer
-                  (set-buffer-multibyte nil)
-                  (insert-buffer-substring buf start end)
-                  (funcall (utf7-get-u16char-converter 'to-utf-16))
-                  (base64-encode-region (point-min) (point-max))
-                  (buffer-string))))
-      (narrow-to-region start end)
-      (delete-region (point-min) (point-max))
-      (insert base))
-    (goto-char (point-min))
-    (let ((pm (point-max)))
-      (when for-imap
-       (while (search-forward "/" nil t)
-         (replace-match ",")))
-      (skip-chars-forward "^= \t\n" pm)
-      (delete-region (point) pm))))
-
-(defun utf7-decode-internal (&optional for-imap)
-  "Decode UTF-7 text in (temporary) buffer.
-Use IMAP modification if FOR-IMAP is non-nil."
-  (let ((start (point-min))
-       (end (point-max)))
-    (goto-char start)
-    (let* ((esc-pattern (concat "^" (char-to-string (if for-imap ?& ?+))))
-          (base64-chars (concat "A-Za-z0-9+"
-                                (char-to-string (if for-imap ?, ?/)))))
-      (while (not (eobp))
-       (skip-chars-forward esc-pattern)
-       (unless (eobp)
-         (forward-char)
-         (let ((p (point))
-               (run-length (skip-chars-forward base64-chars)))
-           (when (and (not (eobp)) (= (following-char) ?-))
-             (delete-char 1))
-           (unless (= run-length 0)    ; Encoded lone esc-char?
-             (save-excursion
-               (utf7-fragment-decode p (point) for-imap)
-               (goto-char p)
-               (delete-char -1)))))))))
-
-(defun utf7-fragment-decode (start end &optional for-imap)
-  "Decode base64 encoded fragment from START to END of UTF-7 text in buffer.
-Use IMAP modification if FOR-IMAP is non-nil."
-  (save-restriction
-    (narrow-to-region start end)
-    (when for-imap
-      (goto-char start)
-      (while (search-forward "," nil 'move-to-end) (replace-match "/")))
-    (let ((pl (utf7-imap-get-pad-length (- end start) 4)))
-      (insert (make-string pl ?=))
-      (base64-decode-region start (+ end pl)))
-    (funcall (utf7-get-u16char-converter 'from-utf-16))))
-
-(defun utf7-get-u16char-converter (which-way)
-  "Return a function to convert between UTF-16 and current character set."
-  (if utf7-utf-16-coding-system
-      (if (eq which-way 'to-utf-16)
-         (lambda ()
-           (encode-coding-region (point-min) (point-max)
-                                 utf7-utf-16-coding-system))
-       (lambda ()
-         (decode-coding-region (point-min) (point-max)
-                               utf7-utf-16-coding-system)))
-    ;; Add test to check if we are really Latin-1.
-    (if (eq which-way 'to-utf-16)
-       'utf7-latin1-u16-char-converter
-      'utf7-u16-latin1-char-converter)))
-
-(defun utf7-latin1-u16-char-converter ()
-  "Convert latin 1 (ISO-8859.1) characters to 16 bit Unicode.
-Characters are converted to raw byte pairs in narrowed buffer."
-  (encode-coding-region (point-min) (point-max) 'iso-8859-1)
-  (goto-char (point-min))
-  (while (not (eobp))
-    (insert 0)
-    (forward-char)))
-
-(defun utf7-u16-latin1-char-converter ()
-  "Convert 16 bit Unicode characters to latin 1 (ISO-8859.1).
-Characters are in raw byte pairs in narrowed buffer."
-  (goto-char (point-min))
-  (while (not (eobp))
-    (if (= 0 (following-char))
-       (delete-char 1)
-       (error "Unable to convert from Unicode"))
-    (forward-char))
-  (decode-coding-region (point-min) (point-max) 'iso-8859-1)
-  (mm-enable-multibyte))
-
-;;;###autoload
-(defun utf7-encode (string &optional for-imap)
-  "Encode UTF-7 STRING.  Use IMAP modification if FOR-IMAP is non-nil."
-  (if (and (coding-system-p 'utf-7) (coding-system-p 'utf-7-imap))
-      ;; Emacs 23 with proper support for IMAP
-      (encode-coding-string string (if for-imap 'utf-7-imap 'utf-7))
-    (mm-with-multibyte-buffer
-     (insert string)
-     (utf7-encode-internal for-imap)
-     (buffer-string))))
-
-(defun utf7-decode (string &optional for-imap)
-  "Decode UTF-7 STRING.  Use IMAP modification if FOR-IMAP is non-nil."
-  (if (and (coding-system-p 'utf-7) (coding-system-p 'utf-7-imap))
-      ;; Emacs 23 with proper support for IMAP
-      (decode-coding-string string (if for-imap 'utf-7-imap 'utf-7))
-    (mm-with-unibyte-buffer
-     (insert string)
-     (utf7-decode-internal for-imap)
-     (mm-enable-multibyte)
-     (buffer-string))))
-
-(provide 'utf7)
-
-;;; utf7.el ends here
diff --git a/lisp/gnus/yenc.el b/lisp/gnus/yenc.el
deleted file mode 100644 (file)
index a4ebd0d..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-;;; yenc.el --- elisp native yenc decoder
-
-;; Copyright (C) 2002-2016 Free Software Foundation, Inc.
-
-;; Author: Jesper Harder <harder@ifa.au.dk>
-;; Keywords: yenc news
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;; Functions for decoding yenc encoded messages.
-;;
-;; Limitations:
-;;
-;; * Does not handle multipart messages.
-;; * No support for external decoders.
-;; * Doesn't check the crc32 checksum (if present).
-
-;;; Code:
-
-(eval-when-compile (require 'cl))
-
-(defconst yenc-begin-line
-  "^=ybegin.*$")
-
-(defconst yenc-decoding-vector
-  [214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
-       231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
-       248 249 250 251 252 253 254 255 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
-       16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
-       39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
-       62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
-       85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
-       106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
-       123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
-       140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
-       157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
-       174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
-       191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
-       208 209 210 211 212 213])
-
-(defun yenc-first-part-p ()
-  "Say whether the buffer contains the first part of a yEnc file."
-  (save-excursion
-    (goto-char (point-min))
-    (re-search-forward "^=ybegin part=1 " nil t)))
-
-(defun yenc-last-part-p ()
-  "Say whether the buffer contains the last part of a yEnc file."
-  (save-excursion
-    (goto-char (point-min))
-    (let (total-size end-size)
-      (when (re-search-forward "^=ybegin.*size=\\([0-9]+\\)" nil t)
-       (setq total-size (match-string 1)))
-      (when (re-search-forward "^=ypart.*end=\\([0-9]+\\)" nil t)
-       (setq end-size (match-string 1)))
-      (and total-size
-          end-size
-          (string= total-size end-size)))))
-
-;;;###autoload
-(defun yenc-decode-region (start end)
-  "Yenc decode region between START and END using an internal decoder."
-  (interactive "r")
-  (let (work-buffer)
-    (unwind-protect
-       (save-excursion
-         (goto-char start)
-         (when (re-search-forward yenc-begin-line end t)
-           (let ((first (match-end 0))
-                 (header-alist (yenc-parse-line (match-string 0)))
-                 bytes last footer-alist char)
-             (when (re-search-forward "^=ypart.*$" end t)
-               (setq first (match-end 0)))
-             (when (re-search-forward "^=yend.*$" end t)
-               (setq last (match-beginning 0))
-               (setq footer-alist (yenc-parse-line (match-string 0)))
-               (setq work-buffer (generate-new-buffer " *yenc-work*"))
-               (with-current-buffer work-buffer
-                 (set-buffer-multibyte nil))
-               (while (< first last)
-                 (setq char (char-after first))
-                 (cond ((or (eq char ?\r)
-                            (eq char ?\n)))
-                       ((eq char ?=)
-                        (setq char (char-after (incf first)))
-                        (with-current-buffer work-buffer
-                          (insert-char (mod (- char 106) 256) 1)))
-                       (t
-                        (with-current-buffer work-buffer
-                          ;;(insert-char (mod (- char 42) 256) 1)
-                          (insert-char (aref yenc-decoding-vector char) 1))))
-                 (incf first))
-               (setq bytes (buffer-size work-buffer))
-               (unless (and (= (cdr (assq 'size header-alist)) bytes)
-                            (= (cdr (assq 'size footer-alist)) bytes))
-                 (message "Warning: Size mismatch while decoding."))
-               (goto-char start)
-               (delete-region start end)
-               (insert-buffer-substring work-buffer))))
-         (and work-buffer (kill-buffer work-buffer))))))
-
-;;;###autoload
-(defun yenc-extract-filename ()
-  "Extract file name from an yenc header."
-  (save-excursion
-    (when (re-search-forward yenc-begin-line nil t)
-      (cdr (assoc 'name (yenc-parse-line (match-string 0)))))))
-
-(defun yenc-parse-line (str)
-  "Extract file name and size from STR."
-  (let (result name)
-    (when (string-match "^=y.*size=\\([0-9]+\\)" str)
-      (push (cons 'size (string-to-number (match-string 1 str))) result))
-    (when (string-match "^=y.*name=\\(.*\\)$" str)
-      (setq name (match-string 1 str))
-      ;; Remove trailing white space
-      (when (string-match " +$" name)
-       (setq name (substring name 0 (match-beginning 0))))
-      (push (cons 'name name) result))
-    result))
-
-(provide 'yenc)
-
-;;; yenc.el ends here
diff --git a/lisp/image/compface.el b/lisp/image/compface.el
new file mode 100644 (file)
index 0000000..e2f607b
--- /dev/null
@@ -0,0 +1,55 @@
+;;; compface.el --- functions for converting X-Face headers
+
+;; Copyright (C) 2002-2016 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+;; Keywords: news
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+;;;###
+(defun uncompface (face)
+  "Convert FACE to pbm.
+Requires the external programs `uncompface', and `icontopbm'.  On a
+GNU/Linux system these might be in packages with names like `compface'
+or `faces-xface' and `netpbm' or `libgr-progs', for instance."
+  (with-temp-buffer
+    (set-buffer-multibyte nil)
+    (insert face)
+    (let ((coding-system-for-read 'raw-text)
+         ;; At least "icontopbm" doesn't work with Windows because
+         ;; the line-break code is converted into CRLF by default.
+         (coding-system-for-write 'binary))
+      (and (eq 0 (apply 'call-process-region (point-min) (point-max)
+                       "uncompface"
+                       'delete '(t nil) nil))
+          (progn
+            (goto-char (point-min))
+            (insert "/* Format_version=1, Width=48, Height=48, Depth=1,\
+ Valid_bits_per_item=16 */\n")
+            ;; Emacs doesn't understand un-raw pbm files.
+            (eq 0 (call-process-region (point-min) (point-max)
+                                       "icontopbm"
+                                       'delete '(t nil))))
+          (buffer-string)))))
+
+(provide 'compface)
+
+;;; compface.el ends here
diff --git a/lisp/image/gravatar.el b/lisp/image/gravatar.el
new file mode 100644 (file)
index 0000000..81503b7
--- /dev/null
@@ -0,0 +1,157 @@
+;;; gravatar.el --- Get Gravatars
+
+;; Copyright (C) 2010-2016 Free Software Foundation, Inc.
+
+;; Author: Julien Danjou <julien@danjou.info>
+;; Keywords: news
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'url)
+(require 'url-cache)
+(require 'image)
+
+(defgroup gravatar nil
+  "Gravatar."
+  :version "24.1"
+  :group 'comm)
+
+(defcustom gravatar-automatic-caching t
+  "Whether to cache retrieved gravatars."
+  :type 'boolean
+  :group 'gravatar)
+
+;; FIXME a time value is not the nicest format for a custom variable.
+(defcustom gravatar-cache-ttl (days-to-time 30)
+  "Time to live for gravatar cache entries."
+  :type '(repeat integer)
+  :group 'gravatar)
+
+;; FIXME Doc is tautological.  What are the options?
+(defcustom gravatar-rating "g"
+  "Default rating for gravatar."
+  :type 'string
+  :group 'gravatar)
+
+(defcustom gravatar-size 32
+  "Default size in pixels for gravatars."
+  :type 'integer
+  :group 'gravatar)
+
+(defconst gravatar-base-url
+  "http://www.gravatar.com/avatar"
+  "Base URL for getting gravatars.")
+
+(defun gravatar-hash (mail-address)
+  "Create an hash from MAIL-ADDRESS."
+  (md5 (downcase mail-address)))
+
+(defun gravatar-build-url (mail-address)
+  "Return an URL to retrieve MAIL-ADDRESS gravatar."
+  (format "%s/%s?d=404&r=%s&s=%d"
+          gravatar-base-url
+          (gravatar-hash mail-address)
+          gravatar-rating
+          gravatar-size))
+
+(defun gravatar-cache-expired (url)
+  "Check if URL is cached for more than `gravatar-cache-ttl'."
+  (cond (url-standalone-mode
+         (not (file-exists-p (url-cache-create-filename url))))
+        (t (let ((cache-time (url-is-cached url)))
+             (if cache-time
+                 (time-less-p
+                  (time-add
+                   cache-time
+                   gravatar-cache-ttl)
+                  (current-time))
+               t)))))
+
+(defun gravatar-get-data ()
+  "Get data from current buffer."
+  (save-excursion
+    (goto-char (point-min))
+    (when (re-search-forward "^HTTP/.+ 200 OK$" nil (line-end-position))
+      (when (search-forward "\n\n" nil t)
+        (buffer-substring (point) (point-max))))))
+
+(defun gravatar-data->image ()
+  "Get data of current buffer and return an image.
+If no image available, return 'error."
+  (let ((data (gravatar-get-data)))
+    (if data
+       (create-image data nil t)
+      'error)))
+
+(autoload 'help-function-arglist "help-fns")
+
+;;;###autoload
+(defun gravatar-retrieve (mail-address cb &optional cbargs)
+  "Retrieve MAIL-ADDRESS gravatar and call CB on retrieval.
+You can provide a list of argument to pass to CB in CBARGS."
+  (let ((url (gravatar-build-url mail-address)))
+    (if (gravatar-cache-expired url)
+       (let ((args (list url
+                         'gravatar-retrieved
+                         (list cb (when cbargs cbargs)))))
+         (when (> (length (if (featurep 'xemacs)
+                              (cdr (split-string (function-arglist 'url-retrieve)))
+                            (help-function-arglist 'url-retrieve)))
+                  4)
+           (setq args (nconc args (list t))))
+         (apply #'url-retrieve args))
+      (apply cb
+               (with-temp-buffer
+                 (mm-disable-multibyte)
+                 (url-cache-extract (url-cache-create-filename url))
+                 (gravatar-data->image))
+               cbargs))))
+
+;;;###autoload
+(defun gravatar-retrieve-synchronously (mail-address)
+  "Retrieve MAIL-ADDRESS gravatar and returns it."
+  (let ((url (gravatar-build-url mail-address)))
+    (if (gravatar-cache-expired url)
+        (with-current-buffer (url-retrieve-synchronously url)
+         (when gravatar-automatic-caching
+            (url-store-in-cache (current-buffer)))
+          (let ((data (gravatar-data->image)))
+            (kill-buffer (current-buffer))
+            data))
+      (with-temp-buffer
+        (mm-disable-multibyte)
+        (url-cache-extract (url-cache-create-filename url))
+        (gravatar-data->image)))))
+
+
+(defun gravatar-retrieved (status cb &optional cbargs)
+  "Callback function used by `gravatar-retrieve'."
+  ;; Store gravatar?
+  (when gravatar-automatic-caching
+    (url-store-in-cache (current-buffer)))
+  (if (plist-get status :error)
+      ;; Error happened.
+      (apply cb 'error cbargs)
+    (apply cb (gravatar-data->image) cbargs))
+  (kill-buffer (current-buffer)))
+
+(provide 'gravatar)
+
+;;; gravatar.el ends here
diff --git a/lisp/international/rfc1843.el b/lisp/international/rfc1843.el
new file mode 100644 (file)
index 0000000..508629f
--- /dev/null
@@ -0,0 +1,131 @@
+;;; rfc1843.el --- HZ (rfc1843) decoding
+
+;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
+
+;; Author: Shenghuo Zhu <zsh@cs.rochester.edu>
+;; Keywords: news HZ HZ+ mail i18n
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Test:
+;; (rfc1843-decode-string  "~{<:Ky2;S{#,NpJ)l6HK!#~}")
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+
+(defvar rfc1843-word-regexp
+  "~\\({\\([\041-\167][\041-\176]\\| \\)+\\)\\(~}\\|$\\)")
+
+(defvar rfc1843-word-regexp-strictly
+  "~\\({\\([\041-\167][\041-\176]\\)+\\)\\(~}\\|$\\)")
+
+(defvar rfc1843-hzp-word-regexp
+  "~\\({\\([\041-\167][\041-\176]\\| \\)+\\|\
+[<>]\\([\041-\175][\041-\176]\\| \\)+\\)\\(~}\\|$\\)")
+
+(defvar rfc1843-hzp-word-regexp-strictly
+  "~\\({\\([\041-\167][\041-\176]\\)+\\|\
+[<>]\\([\041-\175][\041-\176]\\)+\\)\\(~}\\|$\\)")
+
+(defcustom rfc1843-decode-loosely nil
+  "Loosely check HZ encoding if non-nil.
+When it is set non-nil, only buffers or strings with strictly
+HZ-encoded are decoded."
+  :type 'boolean
+  :group 'mime)
+
+(defcustom rfc1843-decode-hzp t
+  "HZ+ decoding support if non-nil.
+HZ+ specification (also known as HZP) is to provide a standardized
+7-bit representation of mixed Big5, GB, and ASCII text for convenient
+e-mail transmission, news posting, etc.
+The document of HZ+ 0.78 specification can be found at
+ftp://ftp.math.psu.edu/pub/simpson/chinese/hzp/hzp.doc"
+  :type 'boolean
+  :group 'mime)
+
+(defcustom rfc1843-newsgroups-regexp "chinese\\|hz"
+  "Regexp of newsgroups in which might be HZ encoded."
+  :type 'string
+  :group 'mime)
+
+(defun rfc1843-decode-region (from to)
+  "Decode HZ in the region between FROM and TO."
+  (interactive "r")
+  (let (str firstc)
+    (save-excursion
+      (goto-char from)
+      (if (or rfc1843-decode-loosely
+             (re-search-forward (if rfc1843-decode-hzp
+                                    rfc1843-hzp-word-regexp-strictly
+                                  rfc1843-word-regexp-strictly) to t))
+         (save-restriction
+           (narrow-to-region from to)
+           (goto-char (point-min))
+           (while (re-search-forward (if rfc1843-decode-hzp
+                                         rfc1843-hzp-word-regexp
+                                       rfc1843-word-regexp) (point-max) t)
+             (setq str (buffer-substring-no-properties
+                        (match-beginning 1)
+                        (match-end 1)))
+             (setq firstc (aref str 0))
+             (insert (decode-coding-string
+                      (rfc1843-decode
+                       (prog1
+                           (substring str 1)
+                         (delete-region (match-beginning 0) (match-end 0)))
+                       firstc)
+                      (if (eq firstc ?{) 'cn-gb-2312 'cn-big5))))
+           (goto-char (point-min))
+           (while (search-forward "~" (point-max) t)
+             (cond ((eq (char-after) ?\n)
+                    (delete-char -1)
+                    (delete-char 1))
+                   ((eq (char-after) ?~)
+                    (delete-char 1)))))))))
+
+(defun rfc1843-decode-string (string)
+  "Decode HZ STRING and return the results."
+  (let ((m enable-multibyte-characters))
+    (with-temp-buffer
+      (when m
+       (set-buffer-multibyte 'to))
+      (insert string)
+      (inline
+       (rfc1843-decode-region (point-min) (point-max)))
+      (buffer-string))))
+
+(defun rfc1843-decode (word &optional firstc)
+  "Decode HZ WORD and return it."
+  (let ((i -1) (s (substring word 0)) v)
+    (if (or (not firstc) (eq firstc ?{))
+       (while (< (incf i) (length s))
+         (if (eq (setq v (aref s i)) ? ) nil
+           (aset s i (+ 128 v))))
+      (while (< (incf i) (length s))
+       (if (eq (setq v (aref s i)) ? ) nil
+         (setq v (+ (* 94 v) (aref s (1+ i)) -3135))
+         (aset s i (+ (/ v 157) (if (eq firstc ?<) 201 161)))
+         (setq v (% v 157))
+         (aset s (incf i) (+ v (if (< v 63) 64 98))))))
+    s))
+
+(provide 'rfc1843)
+
+;;; rfc1843.el ends here
diff --git a/lisp/international/utf7.el b/lisp/international/utf7.el
new file mode 100644 (file)
index 0000000..bd04eba
--- /dev/null
@@ -0,0 +1,236 @@
+;;; utf7.el --- UTF-7 encoding/decoding for Emacs   -*-coding: utf-8;-*-
+
+;; Copyright (C) 1999-2016 Free Software Foundation, Inc.
+
+;; Author: Jon K Hellan <hellan@acm.org>
+;; Maintainer: bugs@gnus.org
+;; Keywords: mail
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; UTF-7 - A Mail-Safe Transformation Format of Unicode - RFC 2152
+;; This is a transformation format of Unicode that contains only 7-bit
+;; ASCII octets and is intended to be readable by humans in the limiting
+;; case that the document consists of characters from the US-ASCII
+;; repertoire.
+;; In short, runs of characters outside US-ASCII are encoded as base64
+;; inside delimiters.
+;; A variation of UTF-7 is specified in IMAP 4rev1 (RFC 2060) as the way
+;; to represent characters outside US-ASCII in mailbox names in IMAP.
+;; This library supports both variants, but the IMAP variation was the
+;; reason I wrote it.
+;; The routines convert UTF-7 -> UTF-16 (16 bit encoding of Unicode)
+;; -> current character set, and vice versa.
+;; However, until Emacs supports Unicode, the only Emacs character set
+;; supported here is ISO-8859.1, which can trivially be converted to/from
+;; Unicode.
+;; When decoding results in a character outside the Emacs character set,
+;; an error is thrown.  It is up to the application to recover.
+
+;; UTF-7 should be done by providing a coding system.  Mule-UCS does
+;; already, but I don't know if it does the IMAP version and it's not
+;; clear whether that should really be a coding system.  The UTF-16
+;; part of the conversion can be done with coding systems available
+;; with Mule-UCS or some versions of Emacs.  Unfortunately these were
+;; done wrongly (regarding handling of byte-order marks and how the
+;; variants were named), so we don't have a consistent name for the
+;; necessary coding system.  The code below doesn't seem to DTRT
+;; generally.  E.g.:
+;;
+;; (utf7-encode "a+£")
+;;   => "a+ACsAow-"
+;;
+;; $ echo "a+£"|iconv -f utf-8 -t utf-7
+;; a+-+AKM
+;;
+;;  -- fx
+
+
+;;; Code:
+
+(require 'base64)
+(eval-when-compile (require 'cl))
+(require 'mm-util)
+
+(defconst utf7-direct-encoding-chars " -%'-*,-[]-}"
+  "Character ranges which do not need escaping in UTF-7.")
+
+(defconst utf7-imap-direct-encoding-chars
+  (concat utf7-direct-encoding-chars "+\\~")
+  "Character ranges which do not need escaping in the IMAP variant of UTF-7.")
+
+(defconst utf7-utf-16-coding-system
+  (cond ((mm-coding-system-p 'utf-16-be-no-signature) ; Mule-UCS
+        'utf-16-be-no-signature)
+       ((and (mm-coding-system-p 'utf-16-be) ; Emacs
+             ;; Avoid versions with BOM.
+             (= 2 (length (encode-coding-string "a" 'utf-16-be))))
+        'utf-16-be)
+       ((mm-coding-system-p 'utf-16-be-nosig) ; ?
+        'utf-16-be-nosig))
+  "Coding system which encodes big endian UTF-16 without a BOM signature.")
+
+(defsubst utf7-imap-get-pad-length (len modulus)
+  "Return required length of padding for IMAP modified base64 fragment."
+  (mod (- len) modulus))
+
+(defun utf7-encode-internal (&optional for-imap)
+  "Encode text in (temporary) buffer as UTF-7.
+Use IMAP modification if FOR-IMAP is non-nil."
+  (let ((start (point-min))
+       (end (point-max)))
+    (narrow-to-region start end)
+    (goto-char start)
+    (let* ((esc-char (if for-imap ?& ?+))
+          (direct-encoding-chars
+           (if for-imap utf7-imap-direct-encoding-chars
+             utf7-direct-encoding-chars))
+          (not-direct-encoding-chars (concat "^" direct-encoding-chars)))
+      (while (not (eobp))
+       (skip-chars-forward direct-encoding-chars)
+       (unless (eobp)
+         (insert esc-char)
+         (let ((p (point))
+               (fc (following-char))
+               (run-length
+                (skip-chars-forward not-direct-encoding-chars)))
+           (if (and (= fc esc-char)
+                    (= run-length 1))  ; Lone esc-char?
+               (delete-char -1)        ; Now there's one too many
+             (utf7-fragment-encode p (point) for-imap))
+           (insert "-")))))))
+
+(defun utf7-fragment-encode (start end &optional for-imap)
+  "Encode text from START to END in buffer as UTF-7 escape fragment.
+Use IMAP modification if FOR-IMAP is non-nil."
+  (save-restriction
+    (let* ((buf (current-buffer))
+          (base (with-temp-buffer
+                  (set-buffer-multibyte nil)
+                  (insert-buffer-substring buf start end)
+                  (funcall (utf7-get-u16char-converter 'to-utf-16))
+                  (base64-encode-region (point-min) (point-max))
+                  (buffer-string))))
+      (narrow-to-region start end)
+      (delete-region (point-min) (point-max))
+      (insert base))
+    (goto-char (point-min))
+    (let ((pm (point-max)))
+      (when for-imap
+       (while (search-forward "/" nil t)
+         (replace-match ",")))
+      (skip-chars-forward "^= \t\n" pm)
+      (delete-region (point) pm))))
+
+(defun utf7-decode-internal (&optional for-imap)
+  "Decode UTF-7 text in (temporary) buffer.
+Use IMAP modification if FOR-IMAP is non-nil."
+  (let ((start (point-min))
+       (end (point-max)))
+    (goto-char start)
+    (let* ((esc-pattern (concat "^" (char-to-string (if for-imap ?& ?+))))
+          (base64-chars (concat "A-Za-z0-9+"
+                                (char-to-string (if for-imap ?, ?/)))))
+      (while (not (eobp))
+       (skip-chars-forward esc-pattern)
+       (unless (eobp)
+         (forward-char)
+         (let ((p (point))
+               (run-length (skip-chars-forward base64-chars)))
+           (when (and (not (eobp)) (= (following-char) ?-))
+             (delete-char 1))
+           (unless (= run-length 0)    ; Encoded lone esc-char?
+             (save-excursion
+               (utf7-fragment-decode p (point) for-imap)
+               (goto-char p)
+               (delete-char -1)))))))))
+
+(defun utf7-fragment-decode (start end &optional for-imap)
+  "Decode base64 encoded fragment from START to END of UTF-7 text in buffer.
+Use IMAP modification if FOR-IMAP is non-nil."
+  (save-restriction
+    (narrow-to-region start end)
+    (when for-imap
+      (goto-char start)
+      (while (search-forward "," nil 'move-to-end) (replace-match "/")))
+    (let ((pl (utf7-imap-get-pad-length (- end start) 4)))
+      (insert (make-string pl ?=))
+      (base64-decode-region start (+ end pl)))
+    (funcall (utf7-get-u16char-converter 'from-utf-16))))
+
+(defun utf7-get-u16char-converter (which-way)
+  "Return a function to convert between UTF-16 and current character set."
+  (if utf7-utf-16-coding-system
+      (if (eq which-way 'to-utf-16)
+         (lambda ()
+           (encode-coding-region (point-min) (point-max)
+                                 utf7-utf-16-coding-system))
+       (lambda ()
+         (decode-coding-region (point-min) (point-max)
+                               utf7-utf-16-coding-system)))
+    ;; Add test to check if we are really Latin-1.
+    (if (eq which-way 'to-utf-16)
+       'utf7-latin1-u16-char-converter
+      'utf7-u16-latin1-char-converter)))
+
+(defun utf7-latin1-u16-char-converter ()
+  "Convert latin 1 (ISO-8859.1) characters to 16 bit Unicode.
+Characters are converted to raw byte pairs in narrowed buffer."
+  (encode-coding-region (point-min) (point-max) 'iso-8859-1)
+  (goto-char (point-min))
+  (while (not (eobp))
+    (insert 0)
+    (forward-char)))
+
+(defun utf7-u16-latin1-char-converter ()
+  "Convert 16 bit Unicode characters to latin 1 (ISO-8859.1).
+Characters are in raw byte pairs in narrowed buffer."
+  (goto-char (point-min))
+  (while (not (eobp))
+    (if (= 0 (following-char))
+       (delete-char 1)
+       (error "Unable to convert from Unicode"))
+    (forward-char))
+  (decode-coding-region (point-min) (point-max) 'iso-8859-1)
+  (mm-enable-multibyte))
+
+;;;###autoload
+(defun utf7-encode (string &optional for-imap)
+  "Encode UTF-7 STRING.  Use IMAP modification if FOR-IMAP is non-nil."
+  (if (and (coding-system-p 'utf-7) (coding-system-p 'utf-7-imap))
+      ;; Emacs 23 with proper support for IMAP
+      (encode-coding-string string (if for-imap 'utf-7-imap 'utf-7))
+    (mm-with-multibyte-buffer
+     (insert string)
+     (utf7-encode-internal for-imap)
+     (buffer-string))))
+
+(defun utf7-decode (string &optional for-imap)
+  "Decode UTF-7 STRING.  Use IMAP modification if FOR-IMAP is non-nil."
+  (if (and (coding-system-p 'utf-7) (coding-system-p 'utf-7-imap))
+      ;; Emacs 23 with proper support for IMAP
+      (decode-coding-string string (if for-imap 'utf-7-imap 'utf-7))
+    (mm-with-unibyte-buffer
+     (insert string)
+     (utf7-decode-internal for-imap)
+     (mm-enable-multibyte)
+     (buffer-string))))
+
+(provide 'utf7)
+
+;;; utf7.el ends here
diff --git a/lisp/mail/flow-fill.el b/lisp/mail/flow-fill.el
new file mode 100644 (file)
index 0000000..d288142
--- /dev/null
@@ -0,0 +1,240 @@
+;;; flow-fill.el --- interpret RFC2646 "flowed" text
+
+;; Copyright (C) 2000-2016 Free Software Foundation, Inc.
+
+;; Author: Simon Josefsson <jas@pdc.kth.se>
+;; Keywords: mail
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This implement decoding of RFC2646 formatted text, including the
+;; quoted-depth wins rules.
+
+;; Theory of operation: search for lines ending with SPC, save quote
+;; length of line, remove SPC and concatenate line with the following
+;; line if quote length of following line matches current line.
+
+;; When no further concatenations are possible, we've found a
+;; paragraph and we let `fill-region' fill the long line into several
+;; lines with the quote prefix as `fill-prefix'.
+
+;; Todo: implement basic `fill-region' (Emacs and XEmacs
+;;       implementations differ..)
+
+;;; History:
+
+;; 2000-02-17  posted on ding mailing list
+;; 2000-02-19  use `point-at-{b,e}ol' in XEmacs
+;; 2000-03-11  no compile warnings for point-at-bol stuff
+;; 2000-03-26  committed to gnus cvs
+;; 2000-10-23  don't flow "-- " lines, make "quote-depth wins" rule
+;;             work when first line is at level 0.
+;; 2002-01-12  probably incomplete encoding support
+;; 2003-12-08  started working on test harness.
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+
+(defcustom fill-flowed-display-column 'fill-column
+  "Column beyond which format=flowed lines are wrapped, when displayed.
+This can be a Lisp expression or an integer."
+  :version "22.1"
+  :group 'mime-display
+  :type '(choice (const :tag "Standard `fill-column'" fill-column)
+                (const :tag "Fit Window" (- (window-width) 5))
+                (sexp)
+                (integer)))
+
+(defcustom fill-flowed-encode-column 66
+  "Column beyond which format=flowed lines are wrapped, in outgoing messages.
+This can be a Lisp expression or an integer.
+RFC 2646 suggests 66 characters for readability."
+  :version "22.1"
+  :group 'mime-display
+  :type '(choice (const :tag "Standard fill-column" fill-column)
+                (const :tag "RFC 2646 default (66)" 66)
+                (sexp)
+                (integer)))
+
+;;;###autoload
+(defun fill-flowed-encode (&optional buffer)
+  (with-current-buffer (or buffer (current-buffer))
+    ;; No point in doing this unless hard newlines is used.
+    (when use-hard-newlines
+      (let ((start (point-min)) end)
+       ;; Go through each paragraph, filling it and adding SPC
+       ;; as the last character on each line.
+       (while (setq end (text-property-any start (point-max) 'hard 't))
+         (save-restriction
+           (narrow-to-region start end)
+           (let ((fill-column (eval fill-flowed-encode-column)))
+             (fill-flowed-fill-buffer))
+           (goto-char (point-min))
+           (while (re-search-forward "\n" nil t)
+             (replace-match " \n" t t))
+           (goto-char (setq start (1+ (point-max)))))))
+      t)))
+
+(defun fill-flowed-fill-buffer ()
+  (let ((prefix nil)
+       (prev-prefix nil)
+       (start (point-min)))
+    (goto-char (point-min))
+    (while (not (eobp))
+      (setq prefix (and (looking-at "[> ]+")
+                       (match-string 0)))
+      (if (equal prefix prev-prefix)
+         (forward-line 1)
+       (save-restriction
+         (narrow-to-region start (point))
+         (let ((fill-prefix prev-prefix))
+           (fill-region (point-min) (point-max) t 'nosqueeze 'to-eop))
+         (goto-char (point-max)))
+       (setq prev-prefix prefix
+             start (point))))
+    (save-restriction
+      (narrow-to-region start (point))
+      (let ((fill-prefix prev-prefix))
+       (fill-region (point-min) (point-max) t 'nosqueeze 'to-eop)))))
+
+;;;###autoload
+(defun fill-flowed (&optional buffer delete-space)
+  (with-current-buffer (or (current-buffer) buffer)
+    (goto-char (point-min))
+    ;; Remove space stuffing.
+    (while (re-search-forward "^\\( \\|>+ $\\)" nil t)
+      (delete-char -1)
+      (forward-line 1))
+    (goto-char (point-min))
+    (while (re-search-forward " $" nil t)
+      (when (save-excursion
+             (beginning-of-line)
+             (looking-at "^\\(>*\\)\\( ?\\)"))
+       (let ((quote (match-string 1))
+             sig)
+         (if (string= quote "")
+             (setq quote nil))
+         (when (and quote (string= (match-string 2) ""))
+           (save-excursion
+             ;; insert SP after quote for pleasant reading of quoted lines
+             (beginning-of-line)
+             (when (> (skip-chars-forward ">") 0)
+               (insert " "))))
+         ;; XXX slightly buggy handling of "-- "
+         (while (and (save-excursion
+                       (ignore-errors (backward-char 3))
+                       (setq sig (looking-at "-- "))
+                       (looking-at "[^-][^-] "))
+                     (save-excursion
+                       (unless (eobp)
+                         (forward-char 1)
+                         (looking-at (format "^\\(%s\\)\\([^>\n\r]\\)"
+                                             (or quote " ?"))))))
+           (save-excursion
+             (replace-match (if (string= (match-string 2) " ")
+                                "" "\\2")))
+           (backward-delete-char -1)
+           (when delete-space
+             (delete-char -1))
+           (end-of-line))
+         (unless sig
+           (condition-case nil
+               (let ((fill-prefix (when quote (concat quote " ")))
+                     (fill-column (eval fill-flowed-display-column))
+                     adaptive-fill-mode)
+                 (fill-region (point-at-bol)
+                              (min (1+ (point-at-eol))
+                                   (point-max))
+                              'left 'nosqueeze))
+             (error
+              (forward-line 1)
+              nil))))))))
+
+;; Test vectors.
+
+(defvar show-trailing-whitespace)
+
+(defvar fill-flowed-encode-tests
+  `(
+    ;; The syntax of each list element is:
+    ;; (INPUT . EXPECTED-OUTPUT)
+    (,(concat
+       "> Thou villainous ill-breeding spongy dizzy-eyed \n"
+       "> reeky elf-skinned pigeon-egg! \n"
+       ">> Thou artless swag-bellied milk-livered \n"
+       ">> dismal-dreaming idle-headed scut!\n"
+       ">>> Thou errant folly-fallen spleeny reeling-ripe \n"
+       ">>> unmuzzled ratsbane!\n"
+       ">>>> Henceforth, the coding style is to be strictly \n"
+       ">>>> enforced, including the use of only upper case.\n"
+       ">>>>> I've noticed a lack of adherence to the coding \n"
+       ">>>>> styles, of late.\n"
+       ">>>>>> Any complaints?")
+     .
+     ,(concat
+       "> Thou villainous ill-breeding spongy dizzy-eyed reeky elf-skinned\n"
+       "> pigeon-egg! \n"
+       ">> Thou artless swag-bellied milk-livered dismal-dreaming idle-headed\n"
+       ">> scut!\n"
+       ">>> Thou errant folly-fallen spleeny reeling-ripe unmuzzled ratsbane!\n"
+       ">>>> Henceforth, the coding style is to be strictly enforced,\n"
+       ">>>> including the use of only upper case.\n"
+       ">>>>> I've noticed a lack of adherence to the coding styles, of late.\n"
+       ">>>>>> Any complaints?\n"
+       ))
+    ;; (,(concat
+    ;;    "\n"
+    ;;    "> foo\n"
+    ;;    "> \n"
+    ;;    "> \n"
+    ;;    "> bar\n")
+    ;;  .
+    ;;  ,(concat
+    ;;    "\n"
+    ;;    "> foo bar\n"))
+    ))
+
+(defun fill-flowed-test ()
+  (interactive "")
+  (switch-to-buffer (get-buffer-create "*Format=Flowed test output*"))
+  (erase-buffer)
+  (setq show-trailing-whitespace t)
+  (dolist (test fill-flowed-encode-tests)
+    (let (start output)
+      (insert "***** BEGIN TEST INPUT *****\n")
+      (insert (car test))
+      (insert "***** END TEST INPUT *****\n\n")
+      (insert "***** BEGIN TEST OUTPUT *****\n")
+      (setq start (point))
+      (insert (car test))
+      (save-restriction
+       (narrow-to-region start (point))
+       (fill-flowed))
+      (setq output (buffer-substring start (point-max)))
+      (insert "***** END TEST OUTPUT *****\n")
+      (unless (string= output (cdr test))
+       (insert "\n***** BEGIN TEST EXPECTED OUTPUT *****\n")
+       (insert (cdr test))
+       (insert "***** END TEST EXPECTED OUTPUT *****\n"))
+      (insert "\n\n")))
+  (goto-char (point-max)))
+
+(provide 'flow-fill)
+
+;;; flow-fill.el ends here
diff --git a/lisp/mail/ietf-drums.el b/lisp/mail/ietf-drums.el
new file mode 100644 (file)
index 0000000..03349d1
--- /dev/null
@@ -0,0 +1,291 @@
+;;; ietf-drums.el --- Functions for parsing RFC822bis headers
+
+;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; DRUMS is an IETF Working Group that works (or worked) on the
+;; successor to RFC822, "Standard For The Format Of Arpa Internet Text
+;; Messages".  This library is based on
+;; draft-ietf-drums-msg-fmt-05.txt, released on 1998-08-05.
+
+;; Pending a real regression self test suite, Simon Josefsson added
+;; various self test expressions snipped from bug reports, and their
+;; expected value, below.  I you believe it could be useful, please
+;; add your own test cases, or write a real self test suite, or just
+;; remove this.
+
+;; <m3oekvfd50.fsf@whitebox.m5r.de>
+;; (ietf-drums-parse-address "'foo' <foo@example.com>")
+;; => ("foo@example.com" . "'foo'")
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+
+(defvar ietf-drums-no-ws-ctl-token "\001-\010\013\014\016-\037\177"
+  "US-ASCII control characters excluding CR, LF and white space.")
+(defvar ietf-drums-text-token "\001-\011\013\014\016-\177"
+  "US-ASCII characters excluding CR and LF.")
+(defvar ietf-drums-specials-token "()<>[]:;@\\,.\""
+  "Special characters.")
+(defvar ietf-drums-quote-token "\\"
+  "Quote character.")
+(defvar ietf-drums-wsp-token " \t"
+  "White space.")
+(defvar ietf-drums-fws-regexp
+  (concat "[" ietf-drums-wsp-token "]*\n[" ietf-drums-wsp-token "]+")
+  "Folding white space.")
+(defvar ietf-drums-atext-token "-^a-zA-Z0-9!#$%&'*+/=?_`{|}~"
+  "Textual token.")
+(defvar ietf-drums-dot-atext-token "-^a-zA-Z0-9!#$%&'*+/=?_`{|}~."
+  "Textual token including full stop.")
+(defvar ietf-drums-qtext-token
+  (concat ietf-drums-no-ws-ctl-token "\041\043-\133\135-\177")
+  "Non-white-space control characters, plus the rest of ASCII excluding
+backslash and doublequote.")
+(defvar ietf-drums-tspecials "][()<>@,;:\\\"/?="
+  "Tspecials.")
+
+(defvar ietf-drums-syntax-table
+  (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table)))
+    (modify-syntax-entry ?\\ "/" table)
+    (modify-syntax-entry ?< "(" table)
+    (modify-syntax-entry ?> ")" table)
+    (modify-syntax-entry ?@ "w" table)
+    (modify-syntax-entry ?/ "w" table)
+    (modify-syntax-entry ?* "_" table)
+    (modify-syntax-entry ?\; "_" table)
+    (modify-syntax-entry ?\' "_" table)
+    table))
+
+(defun ietf-drums-token-to-list (token)
+  "Translate TOKEN into a list of characters."
+  (let ((i 0)
+       b e c out range)
+    (while (< i (length token))
+      (setq c (aref token i))
+      (incf i)
+      (cond
+       ((eq c ?-)
+       (if b
+           (setq range t)
+         (push c out)))
+       (range
+       (while (<= b c)
+         (push (make-char 'ascii b) out)
+         (incf b))
+       (setq range nil))
+       ((= i (length token))
+       (push (make-char 'ascii c) out))
+       (t
+       (when b
+         (push (make-char 'ascii b) out))
+       (setq b c))))
+    (nreverse out)))
+
+(defsubst ietf-drums-init (string)
+  (set-syntax-table ietf-drums-syntax-table)
+  (insert string)
+  (ietf-drums-unfold-fws)
+  (goto-char (point-min)))
+
+(defun ietf-drums-remove-comments (string)
+  "Remove comments from STRING."
+  (with-temp-buffer
+    (let (c)
+      (ietf-drums-init string)
+      (while (not (eobp))
+       (setq c (char-after))
+       (cond
+        ((eq c ?\")
+         (condition-case err
+             (forward-sexp 1)
+           (error (goto-char (point-max)))))
+        ((eq c ?\()
+         (delete-region
+              (point)
+              (condition-case nil
+                  (with-syntax-table (copy-syntax-table ietf-drums-syntax-table)
+                    (modify-syntax-entry ?\" "w")
+                    (forward-sexp 1)
+                    (point))
+                (error (point-max)))))
+        (t
+         (forward-char 1))))
+      (buffer-string))))
+
+(defun ietf-drums-remove-whitespace (string)
+  "Remove whitespace from STRING."
+  (with-temp-buffer
+    (ietf-drums-init string)
+    (let (c)
+      (while (not (eobp))
+       (setq c (char-after))
+       (cond
+        ((eq c ?\")
+         (forward-sexp 1))
+        ((eq c ?\()
+         (forward-sexp 1))
+        ((memq c '(?\  ?\t ?\n))
+         (delete-char 1))
+        (t
+         (forward-char 1))))
+      (buffer-string))))
+
+(defun ietf-drums-get-comment (string)
+  "Return the first comment in STRING."
+  (with-temp-buffer
+    (ietf-drums-init string)
+    (let (result c)
+      (while (not (eobp))
+       (setq c (char-after))
+       (cond
+        ((eq c ?\")
+         (forward-sexp 1))
+        ((eq c ?\()
+         (setq result
+               (buffer-substring
+                (1+ (point))
+                (progn (forward-sexp 1) (1- (point))))))
+        (t
+         (forward-char 1))))
+      result)))
+
+(defun ietf-drums-strip (string)
+  "Remove comments and whitespace from STRING."
+  (ietf-drums-remove-whitespace (ietf-drums-remove-comments string)))
+
+(defun ietf-drums-parse-address (string)
+  "Parse STRING and return a MAILBOX / DISPLAY-NAME pair."
+  (with-temp-buffer
+    (let (display-name mailbox c display-string)
+      (ietf-drums-init string)
+      (while (not (eobp))
+       (setq c (char-after))
+       (cond
+        ((or (eq c ? )
+             (eq c ?\t))
+         (forward-char 1))
+        ((eq c ?\()
+         (forward-sexp 1))
+        ((eq c ?\")
+         (push (buffer-substring
+                (1+ (point)) (progn (forward-sexp 1) (1- (point))))
+               display-name))
+        ((looking-at (concat "[" ietf-drums-atext-token "@" "]"))
+         (push (buffer-substring (point) (progn (forward-sexp 1) (point)))
+               display-name))
+        ((eq c ?<)
+         (setq mailbox
+               (ietf-drums-remove-whitespace
+                (ietf-drums-remove-comments
+                 (buffer-substring
+                  (1+ (point))
+                  (progn (forward-sexp 1) (1- (point))))))))
+        (t
+         (forward-char 1))))
+      ;; If we found no display-name, then we look for comments.
+      (if display-name
+         (setq display-string
+               (mapconcat 'identity (reverse display-name) " "))
+       (setq display-string (ietf-drums-get-comment string)))
+      (if (not mailbox)
+         (when (and display-string
+                    (string-match "@" display-string))
+           (cons
+            (mapconcat 'identity (nreverse display-name) "")
+            (ietf-drums-get-comment string)))
+       (cons mailbox display-string)))))
+
+(defun ietf-drums-parse-addresses (string &optional rawp)
+  "Parse STRING and return a list of MAILBOX / DISPLAY-NAME pairs.
+If RAWP, don't actually parse the addresses, but instead return
+a list of address strings."
+  (if (null string)
+      nil
+    (with-temp-buffer
+      (ietf-drums-init string)
+      (let ((beg (point))
+           pairs c address)
+       (while (not (eobp))
+         (setq c (char-after))
+         (cond
+          ((memq c '(?\" ?< ?\())
+           (condition-case nil
+               (forward-sexp 1)
+             (error
+              (skip-chars-forward "^,"))))
+          ((eq c ?,)
+           (setq address
+                 (if rawp
+                     (buffer-substring beg (point))
+                   (condition-case nil
+                       (ietf-drums-parse-address
+                        (buffer-substring beg (point)))
+                     (error nil))))
+           (if address (push address pairs))
+           (forward-char 1)
+           (setq beg (point)))
+          (t
+           (forward-char 1))))
+       (setq address
+             (if rawp
+                 (buffer-substring beg (point))
+               (condition-case nil
+                   (ietf-drums-parse-address
+                    (buffer-substring beg (point)))
+                 (error nil))))
+       (if address (push address pairs))
+       (nreverse pairs)))))
+
+(defun ietf-drums-unfold-fws ()
+  "Unfold folding white space in the current buffer."
+  (goto-char (point-min))
+  (while (re-search-forward ietf-drums-fws-regexp nil t)
+    (replace-match " " t t))
+  (goto-char (point-min)))
+
+(defun ietf-drums-parse-date (string)
+  "Return an Emacs time spec from STRING."
+  (apply 'encode-time (parse-time-string string)))
+
+(defun ietf-drums-narrow-to-header ()
+  "Narrow to the header section in the current buffer."
+  (narrow-to-region
+   (goto-char (point-min))
+   (if (re-search-forward "^\r?$" nil 1)
+       (match-beginning 0)
+     (point-max)))
+  (goto-char (point-min)))
+
+(defun ietf-drums-quote-string (string)
+  "Quote string if it needs quoting to be displayed in a header."
+  (if (string-match (concat "[^" ietf-drums-atext-token "]") string)
+      (concat "\"" string "\"")
+    string))
+
+(defun ietf-drums-make-address (name address)
+  (if name
+      (concat (ietf-drums-quote-string name) " <" address ">")
+    address))
+
+(provide 'ietf-drums)
+
+;;; ietf-drums.el ends here
diff --git a/lisp/mail/mail-parse.el b/lisp/mail/mail-parse.el
new file mode 100644 (file)
index 0000000..4fc7e46
--- /dev/null
@@ -0,0 +1,75 @@
+;;; mail-parse.el --- Interface functions for parsing mail
+
+;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file contains wrapper functions for a wide range of mail
+;; parsing functions.  The idea is that there are low-level libraries
+;; that implement according to various specs (RFC2231, DRUMS, USEFOR),
+;; but that programmers that want to parse some header (say,
+;; Content-Type) will want to use the latest spec.
+;;
+;; So while each low-level library (rfc2231.el, for instance) decodes
+;; faithfully according to that (proposed) standard, this library is
+;; the interface library.  If some later RFC supersedes RFC2231, one
+;; would just have to write a new low-level library, adjust the
+;; aliases in this library, and the users and programmers won't notice
+;; any changes.
+
+;;; Code:
+
+(require 'mail-prsvr)
+(require 'ietf-drums)
+(require 'rfc2231)
+(require 'rfc2047)
+(require 'rfc2045)
+
+(defalias 'mail-header-parse-content-type 'rfc2231-parse-qp-string)
+(defalias 'mail-header-parse-content-disposition 'rfc2231-parse-qp-string)
+(defalias 'mail-content-type-get 'rfc2231-get-value)
+(defalias 'mail-header-encode-parameter 'rfc2047-encode-parameter)
+
+(defalias 'mail-header-remove-comments 'ietf-drums-remove-comments)
+(defalias 'mail-header-remove-whitespace 'ietf-drums-remove-whitespace)
+(defalias 'mail-header-strip 'ietf-drums-strip)
+(defalias 'mail-header-get-comment 'ietf-drums-get-comment)
+(defalias 'mail-header-parse-address 'ietf-drums-parse-address)
+(defalias 'mail-header-parse-addresses 'ietf-drums-parse-addresses)
+(defalias 'mail-header-parse-date 'ietf-drums-parse-date)
+(defalias 'mail-narrow-to-head 'ietf-drums-narrow-to-header)
+(defalias 'mail-quote-string 'ietf-drums-quote-string)
+(defalias 'mail-header-make-address 'ietf-drums-make-address)
+
+(defalias 'mail-header-fold-field 'rfc2047-fold-field)
+(defalias 'mail-header-unfold-field 'rfc2047-unfold-field)
+(defalias 'mail-header-narrow-to-field 'rfc2047-narrow-to-field)
+(defalias 'mail-header-field-value 'rfc2047-field-value)
+
+(defalias 'mail-encode-encoded-word-region 'rfc2047-encode-region)
+(defalias 'mail-encode-encoded-word-buffer 'rfc2047-encode-message-header)
+(defalias 'mail-encode-encoded-word-string 'rfc2047-encode-string)
+(defalias 'mail-decode-encoded-word-region 'rfc2047-decode-region)
+(defalias 'mail-decode-encoded-word-string 'rfc2047-decode-string)
+(defalias 'mail-decode-encoded-address-region 'rfc2047-decode-address-region)
+(defalias 'mail-decode-encoded-address-string 'rfc2047-decode-address-string)
+
+(provide 'mail-parse)
+
+;;; mail-parse.el ends here
diff --git a/lisp/mail/mail-prsvr.el b/lisp/mail/mail-prsvr.el
new file mode 100644 (file)
index 0000000..789c002
--- /dev/null
@@ -0,0 +1,43 @@
+;;; mail-prsvr.el --- Interface variables for parsing mail
+
+;; Copyright (C) 1999-2016 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(defvar mail-parse-charset nil
+  "Default charset used by low-level libraries.
+This variable should never be set.  Instead, it should be bound by
+functions that wish to call mail-parse functions and let them know
+what the desired charset is to be.")
+
+(defvar mail-parse-mule-charset nil
+  "Default MULE charset used by low-level libraries.
+This variable should never be set.")
+
+(defvar mail-parse-ignored-charsets nil
+  "Ignored charsets used by low-level libraries.
+This variable should never be set.  Instead, it should be bound by
+functions that wish to call mail-parse functions and let them know
+what the desired charsets is to be ignored.")
+
+(provide 'mail-prsvr)
+
+;;; mail-prsvr.el ends here
diff --git a/lisp/mail/qp.el b/lisp/mail/qp.el
new file mode 100644 (file)
index 0000000..a295e0c
--- /dev/null
@@ -0,0 +1,177 @@
+;;; qp.el --- Quoted-Printable functions
+
+;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+;; Keywords: mail, extensions
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Functions for encoding and decoding quoted-printable text as
+;; defined in RFC 2045.
+
+;;; Code:
+
+;;;###autoload
+(defun quoted-printable-decode-region (from to &optional coding-system)
+  "Decode quoted-printable in the region between FROM and TO, per RFC 2045.
+If CODING-SYSTEM is non-nil, decode bytes into characters with that
+coding-system.
+
+Interactively, you can supply the CODING-SYSTEM argument
+with \\[universal-coding-system-argument].
+
+The CODING-SYSTEM argument is a historical hangover and is deprecated.
+QP encodes raw bytes and should be decoded into raw bytes.  Decoding
+them into characters should be done separately."
+  (interactive
+   ;; Let the user determine the coding system with "C-x RET c".
+   (list (region-beginning) (region-end) coding-system-for-read))
+  (when (and coding-system
+            (not (coding-system-p coding-system))) ; e.g. `ascii' from Gnus
+    (setq coding-system nil))
+  (save-excursion
+    (save-restriction
+      ;; RFC 2045:  ``An "=" followed by two hexadecimal digits, one
+      ;; or both of which are lowercase letters in "abcdef", is
+      ;; formally illegal. A robust implementation might choose to
+      ;; recognize them as the corresponding uppercase letters.''
+      (let ((case-fold-search t))
+       (narrow-to-region from to)
+       ;; Do this in case we're called from Gnus, say, in a buffer
+       ;; which already contains non-ASCII characters which would
+       ;; then get doubly-decoded below.
+       (if coding-system
+           (encode-coding-region (point-min) (point-max) coding-system))
+       (goto-char (point-min))
+       (while (and (skip-chars-forward "^=")
+                   (not (eobp)))
+         (cond ((eq (char-after (1+ (point))) ?\n)
+                (delete-char 2))
+               ((looking-at "\\(=[0-9A-F][0-9A-F]\\)+")
+                ;; Decode this sequence at once; i.e. by a single
+                ;; deletion and insertion.
+                (let* ((n (/ (- (match-end 0) (point)) 3))
+                       (str (make-string n 0)))
+                  (dotimes (i n)
+                     (let ((n1 (char-after (1+ (point))))
+                           (n2 (char-after (+ 2 (point)))))
+                       (aset str i
+                             (+ (* 16 (- n1 (if (<= n1 ?9) ?0
+                                              (if (<= n1 ?F) (- ?A 10)
+                                                (- ?a 10)))))
+                                (- n2 (if (<= n2 ?9) ?0
+                                        (if (<= n2 ?F) (- ?A 10)
+                                          (- ?a 10)))))))
+                    (forward-char 3))
+                  (delete-region (match-beginning 0) (match-end 0))
+                  (insert str)))
+               (t
+                (message "Malformed quoted-printable text")
+                (forward-char)))))
+      (if coding-system
+         (decode-coding-region (point-min) (point-max) coding-system)))))
+
+(defun quoted-printable-decode-string (string &optional coding-system)
+  "Decode the quoted-printable encoded STRING and return the result.
+If CODING-SYSTEM is non-nil, decode the string with coding-system.
+Use of CODING-SYSTEM is deprecated; this function should deal with
+raw bytes, and coding conversion should be done separately."
+  (with-temp-buffer
+    (set-buffer-multibyte nil)
+    (insert string)
+    (quoted-printable-decode-region (point-min) (point-max) coding-system)
+    (buffer-string)))
+
+(defun quoted-printable-encode-region (from to &optional fold class)
+  "Quoted-printable encode the region between FROM and TO per RFC 2045.
+
+If FOLD, fold long lines at 76 characters (as required by the RFC).
+If CLASS is non-nil, translate the characters not matched by that
+regexp class, which is in the form expected by `skip-chars-forward'.
+You should probably avoid non-ASCII characters in this arg.
+
+If `mm-use-ultra-safe-encoding' is set, fold lines unconditionally and
+encode lines starting with \"From\"."
+  (interactive "r")
+  (unless class
+    ;; Avoid using 8bit characters. = is \075.
+    ;; Equivalent to "^\000-\007\013\015-\037\200-\377="
+    (setq class "\010-\012\014\040-\074\076-\177"))
+  (save-excursion
+    (goto-char from)
+    (if (re-search-forward (string-to-multibyte "[^\x0-\x7f\x80-\xff]")
+                          to t)
+       (error "Multibyte character in QP encoding region"))
+    (save-restriction
+      (narrow-to-region from to)
+      ;; Encode all the non-ascii and control characters.
+      (goto-char (point-min))
+      (while (and (skip-chars-forward class)
+                 (not (eobp)))
+       (insert
+        (prog1
+            (format "=%02X" (char-after))
+          (delete-char 1))))
+      ;; Encode white space at the end of lines.
+      (goto-char (point-min))
+      (while (re-search-forward "[ \t]+$" nil t)
+       (goto-char (match-beginning 0))
+       (while (not (eolp))
+         (insert
+          (prog1
+              (format "=%02X" (char-after))
+            (delete-char 1)))))
+      (let ((ultra
+            (and (boundp 'mm-use-ultra-safe-encoding)
+                 mm-use-ultra-safe-encoding)))
+       (when (or fold ultra)
+         (let ((tab-width 1)           ; HTAB is one character.
+               (case-fold-search nil))
+           (goto-char (point-min))
+           (while (not (eobp))
+             ;; In ultra-safe mode, encode "From " at the beginning
+             ;; of a line.
+             (when ultra
+               (if (looking-at "From ")
+                   (replace-match "From=20" nil t)
+                 (if (looking-at "-")
+                     (replace-match "=2D" nil t))))
+             (end-of-line)
+             ;; Fold long lines.
+             (while (> (current-column) 76) ; tab-width must be 1.
+               (beginning-of-line)
+               (forward-char 75)       ; 75 chars plus an "="
+               (search-backward "=" (- (point) 2) t)
+               (insert "=\n")
+               (end-of-line))
+             (forward-line))))))))
+
+(defun quoted-printable-encode-string (string)
+  "Encode the STRING as quoted-printable and return the result."
+  (with-temp-buffer
+    (if (multibyte-string-p string)
+       (set-buffer-multibyte 'to)
+      (set-buffer-multibyte nil))
+    (insert string)
+    (quoted-printable-encode-region (point-min) (point-max))
+    (buffer-string)))
+
+(provide 'qp)
+
+;;; qp.el ends here
diff --git a/lisp/mail/rfc2045.el b/lisp/mail/rfc2045.el
new file mode 100644 (file)
index 0000000..c2ddf90
--- /dev/null
@@ -0,0 +1,41 @@
+;;; rfc2045.el --- Functions for decoding rfc2045 headers
+
+;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;; RFC 2045 is: "Multipurpose Internet Mail Extensions (MIME) Part
+;; One:  Format of Internet Message Bodies".
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'ietf-drums)
+
+(defun rfc2045-encode-string (param value)
+  "Return and PARAM=VALUE string encoded according to RFC2045."
+  (if (or (string-match (concat "[" ietf-drums-no-ws-ctl-token "]") value)
+         (string-match (concat "[" ietf-drums-tspecials "]") value)
+         (string-match "[ \n\t]" value)
+         (not (string-match (concat "[" ietf-drums-text-token "]") value)))
+      (concat param "=" (format "%S" value))
+    (concat param "=" value)))
+
+(provide 'rfc2045)
+
+;;; rfc2045.el ends here
diff --git a/lisp/mail/rfc2047.el b/lisp/mail/rfc2047.el
new file mode 100644 (file)
index 0000000..4cb10e5
--- /dev/null
@@ -0,0 +1,1166 @@
+;;; rfc2047.el --- functions for encoding and decoding rfc2047 messages
+
+;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+;;     MORIOKA Tomohiko <morioka@jaist.ac.jp>
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; RFC 2047 is "MIME (Multipurpose Internet Mail Extensions) Part
+;; Three:  Message Header Extensions for Non-ASCII Text".
+
+;;; Code:
+
+(eval-when-compile
+  (require 'cl))
+(defvar message-posting-charset)
+
+(require 'mm-util)
+(require 'ietf-drums)
+;; Fixme: Avoid this (used for mail-parse-charset) mm dependence on gnus.
+(require 'mail-prsvr)
+(require 'rfc2045) ;; rfc2045-encode-string
+(autoload 'mm-body-7-or-8 "mm-bodies")
+
+(defvar rfc2047-header-encoding-alist
+  '(("Newsgroups" . nil)
+    ("Followup-To" . nil)
+    ("Message-ID" . nil)
+    ("\\(Resent-\\)?\\(From\\|Cc\\|To\\|Bcc\\|\\(In-\\)?Reply-To\\|Sender\
+\\|Mail-Followup-To\\|Mail-Copies-To\\|Approved\\)" . address-mime)
+    (t . mime))
+  "*Header/encoding method alist.
+The list is traversed sequentially.  The keys can either be
+header regexps or t.
+
+The values can be:
+
+1) nil, in which case no encoding is done;
+2) `mime', in which case the header will be encoded according to RFC2047;
+3) `address-mime', like `mime', but takes account of the rules for address
+   fields (where quoted strings and comments must be treated separately);
+4) a charset, in which case it will be encoded as that charset;
+5) `default', in which case the field will be encoded as the rest
+   of the article.")
+
+(defvar rfc2047-charset-encoding-alist
+  '((us-ascii . nil)
+    (iso-8859-1 . Q)
+    (iso-8859-2 . Q)
+    (iso-8859-3 . Q)
+    (iso-8859-4 . Q)
+    (iso-8859-5 . B)
+    (koi8-r . B)
+    (iso-8859-7 . B)
+    (iso-8859-8 . B)
+    (iso-8859-9 . Q)
+    (iso-8859-14 . Q)
+    (iso-8859-15 . Q)
+    (iso-2022-jp . B)
+    (iso-2022-kr . B)
+    (gb2312 . B)
+    (gbk . B)
+    (gb18030 . B)
+    (big5 . B)
+    (cn-big5 . B)
+    (cn-gb . B)
+    (cn-gb-2312 . B)
+    (euc-kr . B)
+    (iso-2022-jp-2 . B)
+    (iso-2022-int-1 . B)
+    (viscii . Q))
+  "Alist of MIME charsets to RFC2047 encodings.
+Valid encodings are nil, `Q' and `B'.  These indicate binary (no) encoding,
+quoted-printable and base64 respectively.")
+
+(defvar rfc2047-encode-function-alist
+  '((Q . rfc2047-q-encode-string)
+    (B . rfc2047-b-encode-string)
+    (nil . identity))
+  "Alist of RFC2047 encodings to encoding functions.")
+
+(defvar rfc2047-encode-encoded-words t
+  "Whether encoded words should be encoded again.")
+
+(defvar rfc2047-allow-irregular-q-encoded-words t
+  "*Whether to decode irregular Q-encoded words.")
+
+(eval-and-compile ;; Necessary to hard code them in `rfc2047-decode-region'.
+  (defconst rfc2047-encoded-word-regexp
+    "=\\?\\([^][\000-\040()<>@,;:*\\\"/?.=]+\\)\\(?:\\*[^?]+\\)?\\?\
+\\(B\\?[+/0-9A-Za-z]*=*\
+\\|Q\\?[ ->@-~]*\
+\\)\\?="
+    "Regexp that matches encoded word."
+    ;; The patterns for the B encoding and the Q encoding, i.e. the ones
+    ;; beginning with "B" and "Q" respectively, are restricted into only
+    ;; the characters that those encodings may generally use.
+    )
+  (defconst rfc2047-encoded-word-regexp-loose
+    "=\\?\\([^][\000-\040()<>@,;:*\\\"/?.=]+\\)\\(?:\\*[^?]+\\)?\\?\
+\\(B\\?[+/0-9A-Za-z]*=*\
+\\|Q\\?\\(?:\\?+[ -<>@-~]\\)?\\(?:[ ->@-~]+\\?+[ -<>@-~]\\)*[ ->@-~]*\\?*\
+\\)\\?="
+    "Regexp that matches encoded word allowing loose Q encoding."
+    ;; The pattern for the Q encoding, i.e. the one beginning with "Q",
+    ;; is similar to:
+    ;; "Q\\?\\(\\?+[^\n=?]\\)?\\([^\n?]+\\?+[^\n=?]\\)*[^\n?]*\\?*"
+    ;;      <--------1-------><----------2,3----------><--4--><-5->
+    ;; They mean:
+    ;; 1. After "Q?", allow "?"s that follow a character other than "=".
+    ;; 2. Allow "=" after "Q?"; it isn't regarded as the terminator.
+    ;; 3. In the middle of an encoded word, allow "?"s that follow a
+    ;;    character other than "=".
+    ;; 4. Allow any characters other than "?" in the middle of an
+    ;;    encoded word.
+    ;; 5. At the end, allow "?"s.
+    ))
+
+;;;
+;;; Functions for encoding RFC2047 messages
+;;;
+
+(defun rfc2047-qp-or-base64 ()
+  "Return the type with which to encode the buffer.
+This is either `base64' or `quoted-printable'."
+  (save-excursion
+    (let ((limit (min (point-max) (+ 2000 (point-min))))
+         (n8bit 0))
+      (goto-char (point-min))
+      (skip-chars-forward "\x20-\x7f\r\n\t" limit)
+      (while (< (point) limit)
+       (incf n8bit)
+       (forward-char 1)
+       (skip-chars-forward "\x20-\x7f\r\n\t" limit))
+      (if (or (< (* 6 n8bit) (- limit (point-min)))
+             ;; Don't base64, say, a short line with a single
+             ;; non-ASCII char when splitting parts by charset.
+             (= n8bit 1))
+         'quoted-printable
+       'base64))))
+
+(defun rfc2047-narrow-to-field ()
+  "Narrow the buffer to the header on the current line."
+  (beginning-of-line)
+  (narrow-to-region
+   (point)
+   (progn
+     (forward-line 1)
+     (if (re-search-forward "^[^ \n\t]" nil t)
+        (point-at-bol)
+       (point-max))))
+  (goto-char (point-min)))
+
+(defun rfc2047-field-value ()
+  "Return the value of the field at point."
+  (save-excursion
+    (save-restriction
+      (rfc2047-narrow-to-field)
+      (re-search-forward ":[ \t\n]*" nil t)
+      (buffer-substring-no-properties (point) (point-max)))))
+
+(defun rfc2047-quote-special-characters-in-quoted-strings (&optional
+                                                          encodable-regexp)
+  "Quote special characters with `\\'s in quoted strings.
+Quoting will not be done in a quoted string if it contains characters
+matching ENCODABLE-REGEXP or it is within parentheses."
+  (goto-char (point-min))
+  (let ((tspecials (concat "[" ietf-drums-tspecials "]"))
+       (start (point))
+       beg end)
+    (with-syntax-table (standard-syntax-table)
+      (while (not (eobp))
+       (if (ignore-errors
+             (forward-list 1)
+             (eq (char-before) ?\)))
+           (forward-list -1)
+         (goto-char (point-max)))
+       (save-restriction
+         (narrow-to-region start (point))
+         (goto-char start)
+         (while (search-forward "\"" nil t)
+           (setq beg (match-beginning 0))
+           (unless (eq (char-before beg) ?\\)
+             (goto-char beg)
+             (setq beg (1+ beg))
+             (condition-case nil
+                 (progn
+                   (forward-sexp)
+                   (setq end (1- (point)))
+                   (goto-char beg)
+                   (if (and encodable-regexp
+                            (re-search-forward encodable-regexp end t))
+                       (goto-char (1+ end))
+                     (save-restriction
+                       (narrow-to-region beg end)
+                       (while (re-search-forward tspecials nil 'move)
+                         (if (eq (char-before) ?\\)
+                             (if (looking-at tspecials) ;; Already quoted.
+                                 (forward-char)
+                               (insert "\\"))
+                           (goto-char (match-beginning 0))
+                           (insert "\\")
+                           (forward-char))))
+                     (forward-char)))
+               (error
+                (goto-char beg)))))
+         (goto-char (point-max)))
+       (forward-list 1)
+       (setq start (point))))))
+
+(defvar rfc2047-encoding-type 'address-mime
+  "The type of encoding done by `rfc2047-encode-region'.
+This should be dynamically bound around calls to
+`rfc2047-encode-region' to either `mime' or `address-mime'.  See
+`rfc2047-header-encoding-alist', for definitions.")
+
+(defun rfc2047-encode-message-header ()
+  "Encode the message header according to `rfc2047-header-encoding-alist'.
+Should be called narrowed to the head of the message."
+  (interactive "*")
+  (save-excursion
+    (goto-char (point-min))
+    (let (alist elem method charsets)
+      (while (not (eobp))
+       (save-restriction
+         (rfc2047-narrow-to-field)
+         (setq method nil
+               alist rfc2047-header-encoding-alist
+               charsets (mm-find-mime-charset-region (point-min) (point-max)))
+         ;; M$ Outlook boycotts decoding of a header if it consists
+         ;; of two or more encoded words and those charsets differ;
+         ;; it seems to decode all words in a header from a charset
+         ;; found first in the header.  So, we unify the charsets into
+         ;; a single one used for encoding the whole text in a header.
+         (let ((mm-coding-system-priorities
+                (if (= (length charsets) 1)
+                    (cons (mm-charset-to-coding-system (car charsets))
+                          mm-coding-system-priorities)
+                  mm-coding-system-priorities)))
+           (while (setq elem (pop alist))
+             (when (or (and (stringp (car elem))
+                            (looking-at (car elem)))
+                       (eq (car elem) t))
+               (setq alist nil
+                     method (cdr elem))))
+           (if (not (rfc2047-encodable-p))
+               (prog2
+                   (when (eq method 'address-mime)
+                     (rfc2047-quote-special-characters-in-quoted-strings))
+                   (if (and (eq (mm-body-7-or-8) '8bit)
+                            (mm-multibyte-p)
+                            (mm-coding-system-p
+                             (car message-posting-charset)))
+                       ;; 8 bit must be decoded.
+                       (encode-coding-region
+                        (point-min) (point-max)
+                        (mm-charset-to-coding-system
+                         (car message-posting-charset))))
+                 ;; No encoding necessary, but folding is nice
+                 (when nil
+                   (rfc2047-fold-region
+                    (save-excursion
+                      (goto-char (point-min))
+                      (skip-chars-forward "^:")
+                      (when (looking-at ": ")
+                        (forward-char 2))
+                      (point))
+                    (point-max))))
+             ;; We found something that may perhaps be encoded.
+             (re-search-forward "^[^:]+: *" nil t)
+             (cond
+              ((eq method 'address-mime)
+               (rfc2047-encode-region (point) (point-max)))
+              ((eq method 'mime)
+               (let ((rfc2047-encoding-type 'mime))
+                 (rfc2047-encode-region (point) (point-max))))
+              ((eq method 'default)
+               (if (and (default-value 'enable-multibyte-characters)
+                        mail-parse-charset)
+                   (encode-coding-region (point) (point-max)
+                                         mail-parse-charset)))
+              ;; We get this when CC'ing messages to newsgroups with
+              ;; 8-bit names.  The group name mail copy just got
+              ;; unconditionally encoded.  Previously, it would ask
+              ;; whether to encode, which was quite confusing for the
+              ;; user.  If the new behavior is wrong, tell me.  I have
+              ;; left the old code commented out below.
+              ;; -- Per Abrahamsen <abraham@dina.kvl.dk> Date: 2001-10-07.
+              ;; Modified by Dave Love, with the commented-out code changed
+              ;; in accordance with changes elsewhere.
+              ((null method)
+               (rfc2047-encode-region (point) (point-max)))
+;;;           ((null method)
+;;;            (if (or (message-options-get
+;;;                     'rfc2047-encode-message-header-encode-any)
+;;;                    (message-options-set
+;;;                     'rfc2047-encode-message-header-encode-any
+;;;                     (y-or-n-p
+;;;                      "Some texts are not encoded. Encode anyway?")))
+;;;                (rfc2047-encode-region (point-min) (point-max))
+;;;              (error "Cannot send unencoded text")))
+              ((mm-coding-system-p method)
+               (when (default-value 'enable-multibyte-characters)
+                 (encode-coding-region (point) (point-max) method)))
+              ;; Hm.
+              (t)))
+           (goto-char (point-max))))))))
+
+;; Fixme: This, and the require below may not be the Right Thing, but
+;; should be safe just before release.  -- fx 2001-02-08
+
+(defun rfc2047-encodable-p ()
+  "Return non-nil if any characters in current buffer need encoding in headers.
+The buffer may be narrowed."
+  (require 'message)                   ; for message-posting-charset
+  (let ((charsets
+        (mm-find-mime-charset-region (point-min) (point-max))))
+    (goto-char (point-min))
+    (or (and rfc2047-encode-encoded-words
+            (prog1
+                (re-search-forward rfc2047-encoded-word-regexp nil t)
+              (goto-char (point-min))))
+       (and charsets
+            (not (equal charsets (list (car message-posting-charset))))))))
+
+;; Use this syntax table when parsing into regions that may need
+;; encoding.  Double quotes are string delimiters, backslash is
+;; character quoting, and all other RFC 2822 special characters are
+;; treated as punctuation so we can use forward-sexp/forward-word to
+;; skip to the end of regions appropriately.  Nb. ietf-drums does
+;; things differently.
+(defconst rfc2047-syntax-table
+  ;; (make-char-table 'syntax-table '(2)) only works in Emacs.
+  (let ((table (make-syntax-table)))
+    ;; The following is done to work for setting all elements of the table;
+    ;; it appears to be the cleanest way.
+    ;; Play safe and don't assume the form of the word syntax entry --
+    ;; copy it from ?a.
+    (set-char-table-range table t (aref (standard-syntax-table) ?a))
+    (modify-syntax-entry ?\\ "\\" table)
+    (modify-syntax-entry ?\" "\"" table)
+    (modify-syntax-entry ?\( "(" table)
+    (modify-syntax-entry ?\) ")" table)
+    (modify-syntax-entry ?\< "." table)
+    (modify-syntax-entry ?\> "." table)
+    (modify-syntax-entry ?\[ "." table)
+    (modify-syntax-entry ?\] "." table)
+    (modify-syntax-entry ?: "." table)
+    (modify-syntax-entry ?\; "." table)
+    (modify-syntax-entry ?, "." table)
+    (modify-syntax-entry ?@ "." table)
+    table))
+
+(defun rfc2047-encode-region (b e &optional dont-fold)
+  "Encode words in region B to E that need encoding.
+By default, the region is treated as containing RFC2822 addresses.
+Dynamically bind `rfc2047-encoding-type' to change that."
+  (save-restriction
+    (narrow-to-region b e)
+    (let ((encodable-regexp (if rfc2047-encode-encoded-words
+                               "[^\000-\177]+\\|=\\?"
+                             "[^\000-\177]+"))
+         start                         ; start of current token
+         end begin csyntax
+         ;; Whether there's an encoded word before the current token,
+         ;; either immediately or separated by space.
+         last-encoded
+         (orig-text (buffer-substring-no-properties b e)))
+      (if (eq 'mime rfc2047-encoding-type)
+         ;; Simple case.  Continuous words in which all those contain
+         ;; non-ASCII characters are encoded collectively.  Encoding
+         ;; ASCII words, including `Re:' used in Subject headers, is
+         ;; avoided for interoperability with non-MIME clients and
+         ;; for making it easy to find keywords.
+         (progn
+           (goto-char (point-min))
+           (while (progn (skip-chars-forward " \t\n")
+                         (not (eobp)))
+             (setq start (point))
+             (while (and (looking-at "[ \t\n]*\\([^ \t\n]+\\)")
+                         (progn
+                           (setq end (match-end 0))
+                           (re-search-forward encodable-regexp end t)))
+               (goto-char end))
+             (if (> (point) start)
+                 (rfc2047-encode start (point))
+               (goto-char end))))
+       ;; `address-mime' case -- take care of quoted words, comments.
+       (rfc2047-quote-special-characters-in-quoted-strings encodable-regexp)
+       (with-syntax-table rfc2047-syntax-table
+         (goto-char (point-min))
+         (condition-case err           ; in case of unbalanced quotes
+             ;; Look for rfc2822-style: sequences of atoms, quoted
+             ;; strings, specials, whitespace.  (Specials mustn't be
+             ;; encoded.)
+             (while (not (eobp))
+               ;; Skip whitespace.
+               (skip-chars-forward " \t\n")
+               (setq start (point))
+               (cond
+                ((not (char-after)))   ; eob
+                ;; else token start
+                ((eq ?\" (setq csyntax (char-syntax (char-after))))
+                 ;; Quoted word.
+                 (forward-sexp)
+                 (setq end (point))
+                 ;; Does it need encoding?
+                 (goto-char start)
+                 (if (re-search-forward encodable-regexp end 'move)
+                     ;; It needs encoding.  Strip the quotes first,
+                     ;; since encoded words can't occur in quotes.
+                     (progn
+                       (goto-char end)
+                       (delete-char -1)
+                       (goto-char start)
+                       (delete-char 1)
+                       (when last-encoded
+                         ;; There was a preceding quoted word.  We need
+                         ;; to include any separating whitespace in this
+                         ;; word to avoid it getting lost.
+                         (skip-chars-backward " \t")
+                         ;; A space is needed between the encoded words.
+                         (insert ? )
+                         (setq start (point)
+                               end (1+ end)))
+                       ;; Adjust the end position for the deleted quotes.
+                       (rfc2047-encode start (- end 2))
+                       (setq last-encoded t)) ; record that it was encoded
+                   (setq last-encoded  nil)))
+                ((eq ?. csyntax)
+                 ;; Skip other delimiters, but record that they've
+                 ;; potentially separated quoted words.
+                 (forward-char)
+                 (setq last-encoded nil))
+                ((eq ?\) csyntax)
+                 (error "Unbalanced parentheses"))
+                ((eq ?\( csyntax)
+                 ;; Look for the end of parentheses.
+                 (forward-list)
+                 ;; Encode text as an unstructured field.
+                 (let ((rfc2047-encoding-type 'mime))
+                   (rfc2047-encode-region (1+ start) (1- (point))))
+                 (skip-chars-forward ")"))
+                (t                 ; normal token/whitespace sequence
+                 ;; Find the end.
+                 ;; Skip one ASCII word, or encode continuous words
+                 ;; in which all those contain non-ASCII characters.
+                 (setq end nil)
+                 (while (not (or end (eobp)))
+                   (when (looking-at "[\000-\177]+")
+                     (setq begin (point)
+                           end (match-end 0))
+                     (when (progn
+                             (while (and (or (re-search-forward
+                                              "[ \t\n]\\|\\Sw" end 'move)
+                                             (setq end nil))
+                                         (eq ?\\ (char-syntax (char-before))))
+                               ;; Skip backslash-quoted characters.
+                               (forward-char))
+                             end)
+                       (setq end (match-beginning 0))
+                       (if rfc2047-encode-encoded-words
+                           (progn
+                             (goto-char begin)
+                             (when (search-forward "=?" end 'move)
+                               (goto-char (match-beginning 0))
+                               (setq end nil)))
+                         (goto-char end))))
+                   ;; Where the value nil of `end' means there may be
+                   ;; text to have to be encoded following the point.
+                   ;; Otherwise, the point reached to the end of ASCII
+                   ;; words separated by whitespace or a special char.
+                   (unless end
+                     (when (looking-at encodable-regexp)
+                       (goto-char (setq begin (match-end 0)))
+                       (while (and (looking-at "[ \t\n]+\\([^ \t\n]+\\)")
+                                   (setq end (match-end 0))
+                                   (progn
+                                     (while (re-search-forward
+                                             encodable-regexp end t))
+                                     (< begin (point)))
+                                   (goto-char begin)
+                                   (or (not (re-search-forward "\\Sw" end t))
+                                       (progn
+                                         (goto-char (match-beginning 0))
+                                         nil)))
+                         (goto-char end))
+                       (when (looking-at "[^ \t\n]+")
+                         (setq end (match-end 0))
+                         (if (re-search-forward "\\Sw+" end t)
+                             ;; There are special characters better
+                             ;; to be encoded so that MTAs may parse
+                             ;; them safely.
+                             (cond ((= end (point)))
+                                   ((looking-at (concat "\\sw*\\("
+                                                        encodable-regexp
+                                                        "\\)"))
+                                    (setq end nil))
+                                   (t
+                                    (goto-char (1- (match-end 0)))
+                                    (unless (= (point) (match-beginning 0))
+                                      ;; Separate encodable text and
+                                      ;; delimiter.
+                                      (insert " "))))
+                           (goto-char end)
+                           (skip-chars-forward " \t\n")
+                           (if (and (looking-at "[^ \t\n]+")
+                                    (string-match encodable-regexp
+                                                  (match-string 0)))
+                               (setq end nil)
+                             (goto-char end)))))))
+                 (skip-chars-backward " \t\n")
+                 (setq end (point))
+                 (goto-char start)
+                 (if (re-search-forward encodable-regexp end 'move)
+                     (progn
+                       (unless (memq (char-before start) '(nil ?\t ? ))
+                         (if (progn
+                               (goto-char start)
+                               (skip-chars-backward "^ \t\n")
+                               (and (looking-at "\\Sw+")
+                                    (= (match-end 0) start)))
+                             ;; Also encode bogus delimiters.
+                             (setq start (point))
+                           ;; Separate encodable text and delimiter.
+                           (goto-char start)
+                           (insert " ")
+                           (setq start (1+ start)
+                                 end (1+ end))))
+                       (rfc2047-encode start end)
+                       (setq last-encoded t))
+                   (setq last-encoded nil)))))
+           (error
+            (if (or debug-on-quit debug-on-error)
+                (signal (car err) (cdr err))
+              (error "Invalid data for rfc2047 encoding: %s"
+                     (replace-regexp-in-string "[ \t\n]+" " " orig-text))))))))
+    (unless dont-fold
+      (rfc2047-fold-region b (point)))
+    (goto-char (point-max))))
+
+(defun rfc2047-encode-string (string &optional dont-fold)
+  "Encode words in STRING.
+By default, the string is treated as containing addresses (see
+`rfc2047-encoding-type')."
+  (mm-with-multibyte-buffer
+    (insert string)
+    (rfc2047-encode-region (point-min) (point-max) dont-fold)
+    (buffer-string)))
+
+;; From RFC 2047:
+;; 2. Syntax of encoded-words
+;;    [...]
+;;    While there is no limit to the length of a multiple-line header
+;;    field, each line of a header field that contains one or more
+;;    'encoded-word's is limited to 76 characters.
+;;
+;; In `rfc2047-encode-parameter' it is bound to nil, so don't defconst it.
+(defvar rfc2047-encode-max-chars 76
+  "Maximum characters of each header line that contain encoded-words.
+According to RFC 2047, it is 76.  If it is nil, encoded-words
+will not be folded.  Too small value may cause an error.  You
+should not change this value.")
+
+(defun rfc2047-encode-1 (column string cs encoder start crest tail
+                               &optional eword)
+  "Subroutine used by `rfc2047-encode'."
+  (cond ((string-equal string "")
+        (or eword ""))
+       ((not rfc2047-encode-max-chars)
+        (concat start
+                (funcall encoder (if cs
+                                     (encode-coding-string string cs)
+                                   string))
+                "?="))
+       ((>= column rfc2047-encode-max-chars)
+        (when eword
+          (cond ((string-match "\n[ \t]+\\'" eword)
+                 ;; Remove a superfluous empty line.
+                 (setq eword (substring eword 0 (match-beginning 0))))
+                ((string-match "(+\\'" eword)
+                 ;; Break the line before the open parenthesis.
+                 (setq crest (concat crest (match-string 0 eword))
+                       eword (substring eword 0 (match-beginning 0))))))
+        (rfc2047-encode-1 (length crest) string cs encoder start " " tail
+                          (concat eword "\n" crest)))
+       (t
+        (let ((index 0)
+              (limit (1- (length string)))
+              (prev "")
+              next len)
+          (while (and prev
+                      (<= index limit))
+            (setq next (concat start
+                               (funcall encoder
+                                        (if cs
+                                            (encode-coding-string
+                                             (substring string 0 (1+ index))
+                                             cs)
+                                          (substring string 0 (1+ index))))
+                               "?=")
+                  len (+ column (length next)))
+            (if (> len rfc2047-encode-max-chars)
+                (setq next prev
+                      prev nil)
+              (if (or (< index limit)
+                      (<= (+ len (or (string-match "\n" tail)
+                                     (length tail)))
+                          rfc2047-encode-max-chars))
+                  (setq prev next
+                        index (1+ index))
+                (if (string-match "\\`)+" tail)
+                    ;; Break the line after the close parenthesis.
+                    (setq tail (concat (substring tail 0 (match-end 0))
+                                       "\n "
+                                       (substring tail (match-end 0)))
+                          prev next
+                          index (1+ index))
+                  (setq next prev
+                        prev nil)))))
+          (if (> index limit)
+              (concat eword next tail)
+            (if (= 0 index)
+                (if (and eword
+                         (string-match "(+\\'" eword))
+                    (setq crest (concat crest (match-string 0 eword))
+                          eword (substring eword 0 (match-beginning 0)))
+                  (setq eword (concat eword next)))
+              (setq crest " "
+                    eword (concat eword next)))
+            (when (string-match "\n[ \t]+\\'" eword)
+              ;; Remove a superfluous empty line.
+              (setq eword (substring eword 0 (match-beginning 0))))
+            (rfc2047-encode-1 (length crest) (substring string index)
+                              cs encoder start " " tail
+                              (concat eword "\n" crest)))))))
+
+(defun rfc2047-encode (b e)
+  "Encode the word(s) in the region B to E.
+Point moves to the end of the region."
+  (let ((mime-charset (or (mm-find-mime-charset-region b e) (list 'us-ascii)))
+       cs encoding tail crest eword)
+    ;; Use utf-8 as a last resort if determining charset of text fails.
+    (if (memq nil mime-charset)
+       (setq mime-charset (list 'utf-8)))
+    (cond ((> (length mime-charset) 1)
+          (error "Can't rfc2047-encode `%s'"
+                 (buffer-substring-no-properties b e)))
+         ((= (length mime-charset) 1)
+          (setq mime-charset (car mime-charset)
+                cs (mm-charset-to-coding-system mime-charset))
+          (unless (and (mm-multibyte-p)
+                       (mm-coding-system-p cs))
+            (setq cs nil))
+          (save-restriction
+            (narrow-to-region b e)
+            (setq encoding
+                  (or (cdr (assq mime-charset
+                                 rfc2047-charset-encoding-alist))
+                      ;; For the charsets that don't have a preferred
+                      ;; encoding, choose the one that's shorter.
+                      (if (eq (rfc2047-qp-or-base64) 'base64)
+                          'B
+                        'Q)))
+            (widen)
+            (goto-char e)
+            (skip-chars-forward "^ \t\n")
+            ;; `tail' may contain a close parenthesis.
+            (setq tail (buffer-substring-no-properties e (point)))
+            (goto-char b)
+            (setq b (point-marker)
+                  e (set-marker (make-marker) e))
+            (rfc2047-fold-region (point-at-bol) b)
+            (goto-char b)
+            (skip-chars-backward "^ \t\n")
+            (unless (= 0 (skip-chars-backward " \t"))
+              ;; `crest' may contain whitespace and an open parenthesis.
+              (setq crest (buffer-substring-no-properties (point) b)))
+            (setq eword (rfc2047-encode-1
+                         (- b (point-at-bol))
+                         (replace-regexp-in-string
+                          "\n\\([ \t]?\\)" "\\1"
+                          (buffer-substring-no-properties b e))
+                         cs
+                         (or (cdr (assq encoding
+                                        rfc2047-encode-function-alist))
+                             'identity)
+                         (concat "=?" (downcase (symbol-name mime-charset))
+                                 "?" (upcase (symbol-name encoding)) "?")
+                         (or crest " ")
+                         tail))
+            (delete-region (if (eq (aref eword 0) ?\n)
+                               (if (bolp)
+                                   ;; The line was folded before encoding.
+                                   (1- (point))
+                                 (point))
+                             (goto-char b))
+                           (+ e (length tail)))
+            ;; `eword' contains `crest' and `tail'.
+            (insert eword)
+            (set-marker b nil)
+            (set-marker e nil)
+            (unless (or (/= 0 (length tail))
+                        (eobp)
+                        (looking-at "[ \t\n)]"))
+              (insert " "))))
+         (t
+          (goto-char e)))))
+
+(defun rfc2047-fold-field ()
+  "Fold the current header field."
+  (save-excursion
+    (save-restriction
+      (rfc2047-narrow-to-field)
+      (rfc2047-fold-region (point-min) (point-max)))))
+
+(defun rfc2047-fold-region (b e)
+  "Fold long lines in region B to E."
+  (save-restriction
+    (narrow-to-region b e)
+    (goto-char (point-min))
+    (let ((break nil)
+         (qword-break nil)
+         (first t)
+         (bol (save-restriction
+                (widen)
+                (point-at-bol))))
+      (while (not (eobp))
+       (when (and (or break qword-break)
+                  (> (- (point) bol) 76))
+         (goto-char (or break qword-break))
+         (setq break nil
+               qword-break nil)
+         (skip-chars-backward " \t")
+         (if (looking-at "[ \t]")
+             (insert ?\n)
+           (insert "\n "))
+         (setq bol (1- (point)))
+         ;; Don't break before the first non-LWSP characters.
+         (skip-chars-forward " \t")
+         (unless (eobp)
+           (forward-char 1)))
+       (cond
+        ((eq (char-after) ?\n)
+         (forward-char 1)
+         (setq bol (point)
+               break nil
+               qword-break nil)
+         (skip-chars-forward " \t")
+         (unless (or (eobp) (eq (char-after) ?\n))
+           (forward-char 1)))
+        ((eq (char-after) ?\r)
+         (forward-char 1))
+        ((memq (char-after) '(?  ?\t))
+         (skip-chars-forward " \t")
+         (unless first ;; Don't break just after the header name.
+           (setq break (point))))
+        ((not break)
+         (if (not (looking-at "=\\?[^=]"))
+             (if (eq (char-after) ?=)
+                 (forward-char 1)
+               (skip-chars-forward "^ \t\n\r="))
+           ;; Don't break at the start of the field.
+           (unless (= (point) b)
+             (setq qword-break (point)))
+           (skip-chars-forward "^ \t\n\r")))
+        (t
+         (skip-chars-forward "^ \t\n\r")))
+       (setq first nil))
+      (when (and (or break qword-break)
+                (> (- (point) bol) 76))
+       (goto-char (or break qword-break))
+       (setq break nil
+             qword-break nil)
+       (if (or (> 0 (skip-chars-backward " \t"))
+               (looking-at "[ \t]"))
+           (insert ?\n)
+         (insert "\n "))
+       (setq bol (1- (point)))
+       ;; Don't break before the first non-LWSP characters.
+       (skip-chars-forward " \t")
+       (unless (eobp)
+         (forward-char 1))))))
+
+(defun rfc2047-unfold-field ()
+  "Fold the current line."
+  (save-excursion
+    (save-restriction
+      (rfc2047-narrow-to-field)
+      (rfc2047-unfold-region (point-min) (point-max)))))
+
+(defun rfc2047-unfold-region (b e)
+  "Unfold lines in region B to E."
+  (save-restriction
+    (narrow-to-region b e)
+    (goto-char (point-min))
+    (let ((bol (save-restriction
+                (widen)
+                (point-at-bol)))
+         (eol (point-at-eol)))
+      (forward-line 1)
+      (while (not (eobp))
+       (if (and (looking-at "[ \t]")
+                (< (- (point-at-eol) bol) 76))
+           (delete-region eol (progn
+                                (goto-char eol)
+                                (skip-chars-forward "\r\n")
+                                (point)))
+         (setq bol (point-at-bol)))
+       (setq eol (point-at-eol))
+       (forward-line 1)))))
+
+(defun rfc2047-b-encode-string (string)
+  "Base64-encode the header contained in STRING."
+  (base64-encode-string string t))
+
+(autoload 'quoted-printable-encode-region "qp")
+
+(defun rfc2047-q-encode-string (string)
+  "Quoted-printable-encode the header in STRING."
+  (mm-with-unibyte-buffer
+    (insert string)
+    (quoted-printable-encode-region
+     (point-min) (point-max) nil
+     ;; = (\075), _ (\137), ? (\077) are used in the encoded word.
+     ;; Avoid using 8bit characters.
+     ;; This list excludes `especials' (see the RFC2047 syntax),
+     ;; meaning that some characters in non-structured fields will
+     ;; get encoded when they con't need to be.  The following is
+     ;; what it used to be.
+     ;;;  ;; Equivalent to "^\000-\007\011\013\015-\037\200-\377=_?"
+     ;;;  "\010\012\014\040-\074\076\100-\136\140-\177")
+     "-\b\n\f !#-'*+0-9A-Z\\^`-~\d")
+    (subst-char-in-region (point-min) (point-max) ?  ?_)
+    (buffer-string)))
+
+(defun rfc2047-encode-parameter (param value)
+  "Return and PARAM=VALUE string encoded in the RFC2047-like style.
+This is a substitution for the `rfc2231-encode-string' function, that
+is the standard but many mailers don't support it."
+  (let ((rfc2047-encoding-type 'mime)
+       (rfc2047-encode-max-chars nil))
+    (rfc2045-encode-string param (rfc2047-encode-string value t))))
+
+;;;
+;;; Functions for decoding RFC2047 messages
+;;;
+
+(defvar rfc2047-quote-decoded-words-containing-tspecials nil
+  "If non-nil, quote decoded words containing special characters.")
+
+(defvar rfc2047-allow-incomplete-encoded-text t
+  "*Non-nil means allow incomplete encoded-text in successive encoded-words.
+Dividing of encoded-text in the place other than character boundaries
+violates RFC2047 section 5, while we have a capability to decode it.
+If it is non-nil, the decoder will decode B- or Q-encoding in each
+encoded-word, concatenate them, and decode it by charset.  Otherwise,
+the decoder will fully decode each encoded-word before concatenating
+them.")
+
+(defun rfc2047-strip-backslashes-in-quoted-strings ()
+  "Strip backslashes in quoted strings.  `\\\"' remains."
+  (goto-char (point-min))
+  (let (beg)
+    (with-syntax-table (standard-syntax-table)
+      (while (search-forward "\"" nil t)
+       (unless (eq (char-before) ?\\)
+         (setq beg (match-end 0))
+         (goto-char (match-beginning 0))
+         (condition-case nil
+             (progn
+               (forward-sexp)
+               (save-restriction
+                 (narrow-to-region beg (1- (point)))
+                 (goto-char beg)
+                 (while (search-forward "\\" nil 'move)
+                   (unless (memq (char-after) '(?\"))
+                     (delete-char -1))
+                   (forward-char)))
+               (forward-char))
+           (error
+            (goto-char beg))))))))
+
+(defun rfc2047-charset-to-coding-system (charset &optional allow-override)
+  "Return coding-system corresponding to MIME CHARSET.
+If your Emacs implementation can't decode CHARSET, return nil.
+
+If allow-override is given, use `mm-charset-override-alist' to
+map undesired charset names to their replacement.  This should
+only be used for decoding, not for encoding."
+  (when (stringp charset)
+    (setq charset (intern (downcase charset))))
+  (when (or (not charset)
+           (eq 'gnus-all mail-parse-ignored-charsets)
+           (memq 'gnus-all mail-parse-ignored-charsets)
+           (memq charset mail-parse-ignored-charsets))
+    (setq charset mail-parse-charset))
+  (let ((cs (mm-charset-to-coding-system charset nil allow-override)))
+    (cond ((eq cs 'ascii)
+          (setq cs (or (mm-charset-to-coding-system mail-parse-charset)
+                       'raw-text)))
+         ((mm-coding-system-p cs))
+         ((and charset
+               (listp mail-parse-ignored-charsets)
+               (memq 'gnus-unknown mail-parse-ignored-charsets))
+          (setq cs (mm-charset-to-coding-system mail-parse-charset))))
+    (if (eq cs 'ascii)
+       'raw-text
+      cs)))
+
+(autoload 'quoted-printable-decode-string "qp")
+
+(defun rfc2047-decode-encoded-words (words)
+  "Decode successive encoded-words in WORDS and return a decoded string.
+Each element of WORDS looks like (CHARSET ENCODING ENCODED-TEXT
+ENCODED-WORD)."
+  (let (word charset cs encoding text rest)
+    (while words
+      (setq word (pop words))
+      (if (and (setq cs (rfc2047-charset-to-coding-system
+                        (setq charset (car word)) t))
+              (condition-case code
+                  (cond ((char-equal ?B (nth 1 word))
+                         (setq text (base64-decode-string
+                                     (rfc2047-pad-base64 (nth 2 word)))))
+                        ((char-equal ?Q (nth 1 word))
+                         (setq text (quoted-printable-decode-string
+                                     (subst-char-in-string
+                                      ?_ ?  (nth 2 word) t)))))
+                (error
+                 (message "%s" (error-message-string code))
+                 nil)))
+         (if (and rfc2047-allow-incomplete-encoded-text
+                  (eq cs (caar rest)))
+             ;; Concatenate text of which the charset is the same.
+             (setcdr (car rest) (concat (cdar rest) text))
+           (push (cons cs text) rest))
+       ;; Don't decode encoded-word.
+       (push (cons nil (nth 3 word)) rest)))
+    (while rest
+      (setq words (concat
+                  (or (and (setq cs (caar rest))
+                           (condition-case code
+                               (decode-coding-string (cdar rest) cs)
+                             (error
+                              (message "%s" (error-message-string code))
+                              nil)))
+                      (concat (when (cdr rest) " ")
+                              (cdar rest)
+                              (when (and words
+                                         (not (eq (string-to-char words) ? )))
+                                " ")))
+                  words)
+           rest (cdr rest)))
+    words))
+
+;; Fixme: This should decode in place, not cons intermediate strings.
+;; Also check whether it needs to worry about delimiting fields like
+;; encoding.
+
+;; In fact it's reported that (invalid) encoding of mailboxes in
+;; addr-specs is in use, so delimiting fields might help.  Probably
+;; not decoding a word which isn't properly delimited is good enough
+;; and worthwhile (is it more correct or not?), e.g. something like
+;; `=?iso-8859-1?q?foo?=@'.
+
+(defun rfc2047-decode-region (start end &optional address-mime)
+  "Decode MIME-encoded words in region between START and END.
+If ADDRESS-MIME is non-nil, strip backslashes which precede characters
+other than `\"' and `\\' in quoted strings."
+  (interactive "r")
+  (let ((case-fold-search t)
+       (eword-regexp
+        (if rfc2047-allow-irregular-q-encoded-words
+            (eval-when-compile
+              (concat "[\n\t ]*\\(" rfc2047-encoded-word-regexp-loose "\\)"))
+          (eval-when-compile
+            (concat "[\n\t ]*\\(" rfc2047-encoded-word-regexp "\\)"))))
+       b e match words)
+    (save-excursion
+      (save-restriction
+       (narrow-to-region start end)
+       (when address-mime
+         (rfc2047-strip-backslashes-in-quoted-strings))
+       (goto-char (setq b start))
+       ;; Look for the encoded-words.
+       (while (setq match (re-search-forward eword-regexp nil t))
+         (setq e (match-beginning 1)
+               end (match-end 0)
+               words nil)
+         (while match
+           (push (list (match-string 2) ;; charset
+                       (char-after (match-beginning 3)) ;; encoding
+                       (substring (match-string 3) 2) ;; encoded-text
+                       (match-string 1)) ;; encoded-word
+                 words)
+           ;; Look for the subsequent encoded-words.
+           (when (setq match (looking-at eword-regexp))
+             (goto-char (setq end (match-end 0)))))
+         ;; Replace the encoded-words with the decoded one.
+         (delete-region e end)
+         (insert (rfc2047-decode-encoded-words (nreverse words)))
+         (save-restriction
+           (narrow-to-region e (point))
+           (goto-char e)
+           ;; Remove newlines between decoded words, though such
+           ;; things essentially must not be there.
+           (while (re-search-forward "[\n\r]+" nil t)
+             (replace-match " "))
+           (setq end (point-max))
+           ;; Quote decoded words if there are special characters
+           ;; which might violate RFC2822.
+           (when (and rfc2047-quote-decoded-words-containing-tspecials
+                      (let ((regexp (car (rassq
+                                          'address-mime
+                                          rfc2047-header-encoding-alist))))
+                        (when regexp
+                          (save-restriction
+                            (widen)
+                            (and
+                             ;; Don't quote words if already quoted.
+                             (not (and (eq (char-before e) ?\")
+                                       (eq (char-after end) ?\")))
+                             (progn
+                               (beginning-of-line)
+                               (while (and (memq (char-after) '(?  ?\t))
+                                           (zerop (forward-line -1))))
+                               (looking-at regexp)))))))
+             (let (quoted)
+               (goto-char e)
+               (skip-chars-forward " \t")
+               (setq start (point))
+               (setq quoted (eq (char-after) ?\"))
+               (goto-char (point-max))
+               (skip-chars-backward " \t" start)
+               (if (setq quoted (and quoted
+                                     (> (point) (1+ start))
+                                     (eq (char-before) ?\")))
+                   (progn
+                     (backward-char)
+                     (setq start (1+ start)
+                           end (point-marker)))
+                 (setq end (point-marker)))
+               (goto-char start)
+               (while (search-forward "\"" end t)
+                 (when (prog2
+                           (backward-char)
+                           (zerop (% (skip-chars-backward "\\\\") 2))
+                         (goto-char (match-beginning 0)))
+                   (insert "\\"))
+                 (forward-char))
+               (when (and (not quoted)
+                          (progn
+                            (goto-char start)
+                            (re-search-forward
+                             (concat "[" ietf-drums-tspecials "]")
+                             end t)))
+                 (goto-char start)
+                 (insert "\"")
+                 (goto-char end)
+                 (insert "\""))
+               (set-marker end nil)))
+           (goto-char (point-max)))
+         (when (and (mm-multibyte-p)
+                    mail-parse-charset
+                    (not (eq mail-parse-charset 'us-ascii))
+                    (not (eq mail-parse-charset 'gnus-decoded)))
+           (decode-coding-region b e mail-parse-charset))
+         (setq b (point)))
+       (when (and (mm-multibyte-p)
+                  mail-parse-charset
+                  (not (eq mail-parse-charset 'us-ascii))
+                  (not (eq mail-parse-charset 'gnus-decoded)))
+         (decode-coding-region b (point-max) mail-parse-charset))))))
+
+(defun rfc2047-decode-address-region (start end)
+  "Decode MIME-encoded words in region between START and END.
+Backslashes which precede characters other than `\"' and `\\' in quoted
+strings are stripped."
+  (rfc2047-decode-region start end t))
+
+(defun rfc2047-decode-string (string &optional address-mime)
+  "Decode MIME-encoded STRING and return the result.
+If ADDRESS-MIME is non-nil, strip backslashes which precede characters
+other than `\"' and `\\' in quoted strings."
+  (if (string-match "=\\?" string)
+      (with-temp-buffer
+       ;; We used to only call mm-enable-multibyte if `m' is non-nil,
+       ;; but this can't be the right criterion.  Don't just revert this
+       ;; change if it encounters a bug.  Please help me fix it
+       ;; right instead.  --Stef
+       ;; The string returned should always be multibyte in a multibyte
+       ;; session, i.e. the buffer should be multibyte before
+       ;; `buffer-string' is called.
+       (mm-enable-multibyte)
+       (insert string)
+       (inline
+         (rfc2047-decode-region (point-min) (point-max) address-mime))
+       (buffer-string))
+    (when address-mime
+      (setq string
+           (with-temp-buffer
+             (when (multibyte-string-p string)
+               (mm-enable-multibyte))
+             (insert string)
+             (rfc2047-strip-backslashes-in-quoted-strings)
+             (buffer-string))))
+    ;; Fixme: As above, `m' here is inappropriate.
+    (if (and ;; m
+        mail-parse-charset
+        (not (eq mail-parse-charset 'us-ascii))
+        (not (eq mail-parse-charset 'gnus-decoded)))
+       ;; `decode-coding-string' in Emacs offers a third optional
+       ;; arg NOCOPY to avoid consing a new string if the decoding
+       ;; is "trivial".  Unfortunately it currently doesn't
+       ;; consider anything else than a nil coding system
+       ;; trivial.
+       ;; `rfc2047-decode-string' is called multiple times for each
+       ;; article during summary buffer generation, and we really
+       ;; want to avoid unnecessary consing.  So we bypass
+       ;; `decode-coding-string' if the string is purely ASCII.
+       (if (eq (detect-coding-string string t) 'undecided)
+           ;; string is purely ASCII
+           string
+         (decode-coding-string string mail-parse-charset))
+      (string-to-multibyte string))))
+
+(defun rfc2047-decode-address-string (string)
+  "Decode MIME-encoded STRING and return the result.
+Backslashes which precede characters other than `\"' and `\\' in quoted
+strings are stripped."
+  (rfc2047-decode-string string t))
+
+(defun rfc2047-pad-base64 (string)
+  "Pad STRING to quartets."
+  ;; Be more liberal to accept buggy base64 strings. If
+  ;; base64-decode-string accepts buggy strings, this function could
+  ;; be aliased to identity.
+  (if (= 0 (mod (length string) 4))
+      string
+    (when (string-match "=+$" string)
+      (setq string (substring string 0 (match-beginning 0))))
+    (case (mod (length string) 4)
+      (0 string)
+      (1 string) ;; Error, don't pad it.
+      (2 (concat string "=="))
+      (3 (concat string "=")))))
+
+(provide 'rfc2047)
+
+;;; rfc2047.el ends here
diff --git a/lisp/mail/rfc2231.el b/lisp/mail/rfc2231.el
new file mode 100644 (file)
index 0000000..128779a
--- /dev/null
@@ -0,0 +1,308 @@
+;;; rfc2231.el --- Functions for decoding rfc2231 headers
+
+;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+(require 'ietf-drums)
+(require 'rfc2047)
+(autoload 'mm-encode-body "mm-bodies")
+(autoload 'mail-header-remove-whitespace "mail-parse")
+(autoload 'mail-header-remove-comments "mail-parse")
+
+(defun rfc2231-get-value (ct attribute)
+  "Return the value of ATTRIBUTE from CT."
+  (cdr (assq attribute (cdr ct))))
+
+(defun rfc2231-parse-qp-string (string)
+  "Parse QP-encoded string using `rfc2231-parse-string'.
+N.B.  This is in violation with RFC2047, but it seem to be in common use."
+  (rfc2231-parse-string (rfc2047-decode-string string)))
+
+(defun rfc2231-parse-string (string &optional signal-error)
+  "Parse STRING and return a list.
+The list will be on the form
+ `(name (attribute . value) (attribute . value)...)'.
+
+If the optional SIGNAL-ERROR is non-nil, signal an error when this
+function fails in parsing of parameters.  Otherwise, this function
+must never cause a Lisp error."
+  (with-temp-buffer
+    (let ((ttoken (ietf-drums-token-to-list ietf-drums-text-token))
+         (stoken (ietf-drums-token-to-list ietf-drums-tspecials))
+         (ntoken (ietf-drums-token-to-list "0-9"))
+         c type attribute encoded number parameters value)
+      (ietf-drums-init
+       (condition-case nil
+          (mail-header-remove-whitespace
+           (mail-header-remove-comments string))
+        ;; The most likely cause of an error is unbalanced parentheses
+        ;; or double-quotes.  If all parentheses and double-quotes are
+        ;; quoted meaninglessly with backslashes, removing them might
+        ;; make it parsable.  Let's try...
+        (error
+         (let (mod)
+           (when (and (string-match "\\\\\"" string)
+                      (not (string-match "\\`\"\\|[^\\]\"" string)))
+             (setq string (replace-regexp-in-string "\\\\\"" "\"" string)
+                   mod t))
+           (when (and (string-match "\\\\(" string)
+                      (string-match "\\\\)" string)
+                      (not (string-match "\\`(\\|[^\\][()]" string)))
+             (setq string (replace-regexp-in-string
+                           "\\\\\\([()]\\)" "\\1" string)
+                   mod t))
+           (or (and mod
+                    (ignore-errors
+                      (mail-header-remove-whitespace
+                       (mail-header-remove-comments string))))
+               ;; Finally, attempt to extract only type.
+               (if (string-match
+                    (concat "\\`[\t\n ]*\\([^" ietf-drums-tspecials "\t\n ]+"
+                            "\\(?:/[^" ietf-drums-tspecials
+                            "\t\n ]+\\)?\\)\\(?:[\t\n ;]\\|\\'\\)")
+                    string)
+                   (match-string 1 string)
+                 ""))))))
+      (let ((table (copy-syntax-table ietf-drums-syntax-table)))
+       (modify-syntax-entry ?\' "w" table)
+       (modify-syntax-entry ?* " " table)
+       (modify-syntax-entry ?\; " " table)
+       (modify-syntax-entry ?= " " table)
+       ;; The following isn't valid, but one should be liberal
+       ;; in what one receives.
+       (modify-syntax-entry ?\: "w" table)
+       (set-syntax-table table))
+      (setq c (char-after))
+      (when (and (memq c ttoken)
+                (not (memq c stoken))
+                (setq type (ignore-errors
+                             (downcase
+                              (buffer-substring (point) (progn
+                                                          (forward-sexp 1)
+                                                          (point)))))))
+       ;; Do the params
+       (condition-case err
+           (progn
+             (while (not (eobp))
+               (setq c (char-after))
+               (unless (eq c ?\;)
+                 (error "Invalid header: %s" string))
+               (forward-char 1)
+               ;; If c in nil, then this is an invalid header, but
+               ;; since elm generates invalid headers on this form,
+               ;; we allow it.
+               (when (setq c (char-after))
+                 (if (and (memq c ttoken)
+                          (not (memq c stoken)))
+                     (setq attribute
+                           (intern
+                            (downcase
+                             (buffer-substring
+                              (point) (progn (forward-sexp 1) (point))))))
+                   (error "Invalid header: %s" string))
+                 (setq c (char-after))
+                 (if (eq c ?*)
+                     (progn
+                       (forward-char 1)
+                       (setq c (char-after))
+                       (if (not (memq c ntoken))
+                           (setq encoded t
+                                 number nil)
+                         (setq number
+                               (string-to-number
+                                (buffer-substring
+                                 (point) (progn (forward-sexp 1) (point)))))
+                         (setq c (char-after))
+                         (when (eq c ?*)
+                           (setq encoded t)
+                           (forward-char 1)
+                           (setq c (char-after)))))
+                   (setq number nil
+                         encoded nil))
+                 (unless (eq c ?=)
+                   (error "Invalid header: %s" string))
+                 (forward-char 1)
+                 (setq c (char-after))
+                 (cond
+                  ((eq c ?\")
+                   (setq value (buffer-substring (1+ (point))
+                                                 (progn
+                                                   (forward-sexp 1)
+                                                   (1- (point)))))
+                   (when encoded
+                     (setq value (mapconcat (lambda (c) (format "%%%02x" c))
+                                            value ""))))
+                  ((and (or (memq c ttoken)
+                            ;; EXTENSION: Support non-ascii chars.
+                            (> c ?\177))
+                        (not (memq c stoken)))
+                   (setq value
+                         (buffer-substring
+                          (point)
+                          (progn
+                            ;; Jump over asterisk, non-ASCII
+                            ;; and non-boundary characters.
+                            (while (and c
+                                        (or (eq c ?*)
+                                            (> c ?\177)
+                                            (not (eq (char-syntax c) ? ))))
+                              (forward-char 1)
+                              (setq c (char-after)))
+                            (point)))))
+                  (t
+                   (error "Invalid header: %s" string)))
+                 (push (list attribute value number encoded)
+                       parameters))))
+         (error
+          (setq parameters nil)
+          (when signal-error
+            (signal (car err) (cdr err)))))
+
+       ;; Now collect and concatenate continuation parameters.
+       (let ((cparams nil)
+             elem)
+         (loop for (attribute value part encoded)
+               in (sort parameters (lambda (e1 e2)
+                                     (< (or (caddr e1) 0)
+                                        (or (caddr e2) 0))))
+               do (cond
+                   ;; First part.
+                   ((or (not (setq elem (assq attribute cparams)))
+                        (and (numberp part)
+                             (zerop part)))
+                    (push (list attribute value encoded) cparams))
+                   ;; Repetition of a part; do nothing.
+                   ((and elem
+                         (null number))
+                    )
+                   ;; Concatenate continuation parts.
+                   (t
+                    (setcar (cdr elem) (concat (cadr elem) value)))))
+         ;; Finally decode encoded values.
+         (cons type (mapcar
+                     (lambda (elem)
+                       (cons (car elem)
+                             (if (nth 2 elem)
+                                 (rfc2231-decode-encoded-string (nth 1 elem))
+                               (nth 1 elem))))
+                     (nreverse cparams))))))))
+
+(defun rfc2231-decode-encoded-string (string)
+  "Decode an RFC2231-encoded string.
+These look like:
+ \"us-ascii\\='en-us\\='This%20is%20%2A%2A%2Afun%2A%2A%2A\",
+ \"us-ascii\\='\\='This%20is%20%2A%2A%2Afun%2A%2A%2A\",
+ \"\\='en-us\\='This%20is%20%2A%2A%2Afun%2A%2A%2A\",
+ \"\\='\\='This%20is%20%2A%2A%2Afun%2A%2A%2A\", or
+ \"This is ***fun***\"."
+  (string-match "\\`\\(?:\\([^']+\\)?'\\([^']+\\)?'\\)?\\(.+\\)" string)
+  (let ((coding-system (mm-charset-to-coding-system
+                       (match-string 1 string) nil t))
+       ;;(language (match-string 2 string))
+       (value (match-string 3 string)))
+    (mm-with-unibyte-buffer
+      (insert value)
+      (goto-char (point-min))
+      (while (re-search-forward "%\\([0-9A-Fa-f][0-9A-Fa-f]\\)" nil t)
+       (insert
+        (prog1
+            (string-to-number (match-string 1) 16)
+          (delete-region (match-beginning 0) (match-end 0)))))
+      ;; Decode using the charset, if any.
+      (if (memq coding-system '(nil ascii))
+         (buffer-string)
+       (decode-coding-string (buffer-string) coding-system)))))
+
+(defun rfc2231-encode-string (param value)
+  "Return and PARAM=VALUE string encoded according to RFC2231.
+Use `mml-insert-parameter' or `mml-insert-parameter-string' to insert
+the result of this function."
+  (let ((control (ietf-drums-token-to-list ietf-drums-no-ws-ctl-token))
+       (tspecial (ietf-drums-token-to-list ietf-drums-tspecials))
+       (special (ietf-drums-token-to-list "*'%\n\t"))
+       (ascii (ietf-drums-token-to-list ietf-drums-text-token))
+       (num -1)
+       ;; Don't make lines exceeding 76 column.
+       (limit (- 74 (length param)))
+       spacep encodep charsetp charset broken)
+    (mm-with-multibyte-buffer
+      (insert value)
+      (goto-char (point-min))
+      (while (not (eobp))
+       (cond
+        ((or (memq (following-char) control)
+             (memq (following-char) tspecial)
+             (memq (following-char) special))
+         (setq encodep t))
+        ((eq (following-char) ? )
+         (setq spacep t))
+        ((not (memq (following-char) ascii))
+         (setq charsetp t)))
+       (forward-char 1))
+      (when charsetp
+       (setq charset (mm-encode-body)))
+      (mm-disable-multibyte)
+      (cond
+       ((or encodep charsetp
+           (progn
+             (end-of-line)
+             (> (current-column) (if spacep (- limit 2) limit))))
+       (setq limit (- limit 6))
+       (goto-char (point-min))
+       (insert (symbol-name (or charset 'us-ascii)) "''")
+       (while (not (eobp))
+         (if (or (not (memq (following-char) ascii))
+                 (memq (following-char) control)
+                 (memq (following-char) tspecial)
+                 (memq (following-char) special)
+                 (eq (following-char) ? ))
+             (progn
+               (when (>= (current-column) (1- limit))
+                 (insert ";\n")
+                 (setq broken t))
+               (insert "%" (format "%02x" (following-char)))
+               (delete-char 1))
+           (when (> (current-column) limit)
+             (insert ";\n")
+             (setq broken t))
+           (forward-char 1)))
+       (goto-char (point-min))
+       (if (not broken)
+           (insert param "*=")
+         (while (not (eobp))
+           (insert (if (>= num 0) " " "")
+                   param "*" (format "%d" (incf num)) "*=")
+           (forward-line 1))))
+       (spacep
+       (goto-char (point-min))
+       (insert param "=\"")
+       (goto-char (point-max))
+       (insert "\""))
+       (t
+       (goto-char (point-min))
+       (insert param "=")))
+      (buffer-string))))
+
+(provide 'rfc2231)
+
+;;; rfc2231.el ends here
diff --git a/lisp/mail/yenc.el b/lisp/mail/yenc.el
new file mode 100644 (file)
index 0000000..a4ebd0d
--- /dev/null
@@ -0,0 +1,139 @@
+;;; yenc.el --- elisp native yenc decoder
+
+;; Copyright (C) 2002-2016 Free Software Foundation, Inc.
+
+;; Author: Jesper Harder <harder@ifa.au.dk>
+;; Keywords: yenc news
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Functions for decoding yenc encoded messages.
+;;
+;; Limitations:
+;;
+;; * Does not handle multipart messages.
+;; * No support for external decoders.
+;; * Doesn't check the crc32 checksum (if present).
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+
+(defconst yenc-begin-line
+  "^=ybegin.*$")
+
+(defconst yenc-decoding-vector
+  [214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
+       231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
+       248 249 250 251 252 253 254 255 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+       16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
+       39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
+       62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
+       85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
+       106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
+       123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
+       140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
+       157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173
+       174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
+       191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
+       208 209 210 211 212 213])
+
+(defun yenc-first-part-p ()
+  "Say whether the buffer contains the first part of a yEnc file."
+  (save-excursion
+    (goto-char (point-min))
+    (re-search-forward "^=ybegin part=1 " nil t)))
+
+(defun yenc-last-part-p ()
+  "Say whether the buffer contains the last part of a yEnc file."
+  (save-excursion
+    (goto-char (point-min))
+    (let (total-size end-size)
+      (when (re-search-forward "^=ybegin.*size=\\([0-9]+\\)" nil t)
+       (setq total-size (match-string 1)))
+      (when (re-search-forward "^=ypart.*end=\\([0-9]+\\)" nil t)
+       (setq end-size (match-string 1)))
+      (and total-size
+          end-size
+          (string= total-size end-size)))))
+
+;;;###autoload
+(defun yenc-decode-region (start end)
+  "Yenc decode region between START and END using an internal decoder."
+  (interactive "r")
+  (let (work-buffer)
+    (unwind-protect
+       (save-excursion
+         (goto-char start)
+         (when (re-search-forward yenc-begin-line end t)
+           (let ((first (match-end 0))
+                 (header-alist (yenc-parse-line (match-string 0)))
+                 bytes last footer-alist char)
+             (when (re-search-forward "^=ypart.*$" end t)
+               (setq first (match-end 0)))
+             (when (re-search-forward "^=yend.*$" end t)
+               (setq last (match-beginning 0))
+               (setq footer-alist (yenc-parse-line (match-string 0)))
+               (setq work-buffer (generate-new-buffer " *yenc-work*"))
+               (with-current-buffer work-buffer
+                 (set-buffer-multibyte nil))
+               (while (< first last)
+                 (setq char (char-after first))
+                 (cond ((or (eq char ?\r)
+                            (eq char ?\n)))
+                       ((eq char ?=)
+                        (setq char (char-after (incf first)))
+                        (with-current-buffer work-buffer
+                          (insert-char (mod (- char 106) 256) 1)))
+                       (t
+                        (with-current-buffer work-buffer
+                          ;;(insert-char (mod (- char 42) 256) 1)
+                          (insert-char (aref yenc-decoding-vector char) 1))))
+                 (incf first))
+               (setq bytes (buffer-size work-buffer))
+               (unless (and (= (cdr (assq 'size header-alist)) bytes)
+                            (= (cdr (assq 'size footer-alist)) bytes))
+                 (message "Warning: Size mismatch while decoding."))
+               (goto-char start)
+               (delete-region start end)
+               (insert-buffer-substring work-buffer))))
+         (and work-buffer (kill-buffer work-buffer))))))
+
+;;;###autoload
+(defun yenc-extract-filename ()
+  "Extract file name from an yenc header."
+  (save-excursion
+    (when (re-search-forward yenc-begin-line nil t)
+      (cdr (assoc 'name (yenc-parse-line (match-string 0)))))))
+
+(defun yenc-parse-line (str)
+  "Extract file name and size from STR."
+  (let (result name)
+    (when (string-match "^=y.*size=\\([0-9]+\\)" str)
+      (push (cons 'size (string-to-number (match-string 1 str))) result))
+    (when (string-match "^=y.*name=\\(.*\\)$" str)
+      (setq name (match-string 1 str))
+      ;; Remove trailing white space
+      (when (string-match " +$" name)
+       (setq name (substring name 0 (match-beginning 0))))
+      (push (cons 'name name) result))
+    result))
+
+(provide 'yenc)
+
+;;; yenc.el ends here
diff --git a/lisp/net/html2text.el b/lisp/net/html2text.el
new file mode 100644 (file)
index 0000000..2b1c205
--- /dev/null
@@ -0,0 +1,461 @@
+;;; html2text.el --- a simple html to plain text converter -*- coding: utf-8 -*-
+
+;; Copyright (C) 2002-2016 Free Software Foundation, Inc.
+
+;; Author: Joakim Hove <hove@phys.ntnu.no>
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; These functions provide a simple way to wash/clean html infected
+;; mails.  Definitely do not work in all cases, but some improvement
+;; in readability is generally obtained.  Formatting is only done in
+;; the buffer, so the next time you enter the article it will be
+;; "re-htmlized".
+;;
+;; The main function is `html2text'.
+
+;;; Code:
+
+;;
+;; <Global variables>
+;;
+
+(eval-when-compile
+  (require 'cl))
+
+(defvar html2text-format-single-element-list '(("hr" . html2text-clean-hr)))
+
+(defvar html2text-replace-list
+  '(("&acute;" . "`")
+    ("&amp;" . "&")
+    ("&apos;" . "'")
+    ("&brvbar;" . "|")
+    ("&cent;" . "c")
+    ("&circ;" . "^")
+    ("&copy;" . "(C)")
+    ("&curren;" . "(#)")
+    ("&deg;" . "degree")
+    ("&divide;" . "/")
+    ("&euro;" . "e")
+    ("&frac12;" . "1/2")
+    ("&gt;" . ">")
+    ("&iquest;" . "?")
+    ("&laquo;" . "<<")
+    ("&ldquo" . "\"")
+    ("&lsaquo;" . "(")
+    ("&lsquo;" . "`")
+    ("&lt;" . "<")
+    ("&mdash;" . "--")
+    ("&nbsp;" . " ")
+    ("&ndash;" . "-")
+    ("&permil;" . "%%")
+    ("&plusmn;" . "+-")
+    ("&pound;" . "£")
+    ("&quot;" . "\"")
+    ("&raquo;" . ">>")
+    ("&rdquo" . "\"")
+    ("&reg;" . "(R)")
+    ("&rsaquo;" . ")")
+    ("&rsquo;" . "'")
+    ("&sect;" . "§")
+    ("&sup1;" . "^1")
+    ("&sup2;" . "^2")
+    ("&sup3;" . "^3")
+    ("&tilde;" . "~"))
+  "The map of entity to text.
+
+This is an alist were each element is a dotted pair consisting of an
+old string, and a replacement string.  This replacement is done by the
+function `html2text-substitute' which basically performs a
+`replace-string' operation for every element in the list.  This is
+completely verbatim - without any use of REGEXP.")
+
+(defvar html2text-remove-tag-list
+  '("html" "body" "p" "img" "dir" "head" "div" "br" "font" "title" "meta")
+  "A list of removable tags.
+
+This is a list of tags which should be removed, without any
+formatting.  Note that tags in the list are presented *without*
+any \"<\" or \">\".  All occurrences of a tag appearing in this
+list are removed, irrespective of whether it is a closing or
+opening tag, or if the tag has additional attributes.  The
+deletion is done by the function `html2text-remove-tags'.
+
+For instance the text:
+
+\"Here comes something <font size\"+3\" face=\"Helvetica\"> big </font>.\"
+
+will be reduced to:
+
+\"Here comes something big.\"
+
+If this list contains the element \"font\".")
+
+(defvar html2text-format-tag-list
+  '(("b"         . html2text-clean-bold)
+    ("strong"     . html2text-clean-bold)
+    ("u"         . html2text-clean-underline)
+    ("i"         . html2text-clean-italic)
+    ("em"         . html2text-clean-italic)
+    ("blockquote" . html2text-clean-blockquote)
+    ("a"          . html2text-clean-anchor)
+    ("ul"         . html2text-clean-ul)
+    ("ol"         . html2text-clean-ol)
+    ("dl"         . html2text-clean-dl)
+    ("center"     . html2text-clean-center))
+  "An alist of tags and processing functions.
+
+This is an alist where each dotted pair consists of a tag, and then
+the name of a function to be called when this tag is found.  The
+function is called with the arguments p1, p2, p3 and p4. These are
+demonstrated below:
+
+\"<b> This is bold text </b>\"
+ ^   ^                 ^    ^
+ |   |                 |    |
+p1  p2                p3   p4
+
+Then the called function will typically format the text somewhat and
+remove the tags.")
+
+(defvar html2text-remove-tag-list2  '("li" "dt" "dd" "meta")
+  "Another list of removable tags.
+
+This is a list of tags which are removed similarly to the list
+`html2text-remove-tag-list' - but these tags are retained for the
+formatting, and then moved afterward.")
+
+;;
+;; </Global variables>
+;;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;
+;; <Utility functions>
+;;
+
+
+(defun html2text-replace-string (from-string to-string min max)
+  "Replace FROM-STRING with TO-STRING in region from MIN to MAX."
+  (goto-char min)
+  (let ((delta (- (string-width to-string) (string-width from-string)))
+       (change 0))
+    (while (search-forward from-string max t)
+      (replace-match to-string)
+      (setq change (+ change delta)))
+    change))
+
+;;
+;; </Utility functions>
+;;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;
+;; <Functions related to attributes> i.e. <font size=+3>
+;;
+
+(defun html2text-attr-value (list attribute)
+  "Get value of ATTRIBUTE from LIST."
+  (nth 1 (assoc attribute list)))
+
+(defun html2text-get-attr (p1 p2)
+  (goto-char p1)
+  (re-search-forward "\\s-+" p2 t)
+  (let (attr-list)
+    (while (re-search-forward "[-a-z0-9._]+" p2 t)
+      (setq attr-list
+           (cons
+            (list (match-string 0)
+                  (when (looking-at "\\s-*=")
+                    (goto-char (match-end 0))
+                    (skip-chars-forward "[:space:]")
+                    (when (or (looking-at "\"[^\"]*\"\\|'[^']*'")
+                              (looking-at "[-a-z0-9._:]+"))
+                      (goto-char (match-end 0))
+                      (match-string 0))))
+            attr-list)))
+    attr-list))
+
+;;
+;; </Functions related to attributes>
+;;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;
+;; <Functions to be called to format a tag-pair>
+;;
+(defun html2text-clean-list-items (p1 p2 list-type)
+  (goto-char p1)
+  (let ((item-nr 0)
+       (items   0))
+    (while (search-forward "<li>" p2 t)
+      (setq items (1+ items)))
+    (goto-char p1)
+    (while (< item-nr items)
+      (setq item-nr (1+ item-nr))
+      (search-forward "<li>" (point-max) t)
+      (cond
+       ((string= list-type "ul") (insert " o "))
+       ((string= list-type "ol") (insert (format " %s: " item-nr)))
+       (t (insert " x "))))))
+
+(defun html2text-clean-dtdd (p1 p2)
+  (goto-char p1)
+  (let ((items   0)
+       (item-nr 0))
+    (while (search-forward "<dt>" p2 t)
+      (setq items (1+ items)))
+    (goto-char p1)
+    (while (< item-nr items)
+      (setq item-nr (1+ item-nr))
+      (re-search-forward "<dt>\\([ ]*\\)" (point-max) t)
+      (when (match-string 1)
+       (delete-region (point) (- (point) (string-width (match-string 1)))))
+      (let ((def-p1 (point))
+           (def-p2 0))
+       (re-search-forward "\\([ ]*\\)\\(</dt>\\|<dd>\\)" (point-max) t)
+       (if (match-string 1)
+           (progn
+             (let* ((mw1 (string-width (match-string 1)))
+                    (mw2 (string-width (match-string 2)))
+                    (mw  (+ mw1 mw2)))
+               (goto-char (- (point) mw))
+               (delete-region (point) (+ (point) mw1))
+               (setq def-p2 (point))))
+         (setq def-p2 (- (point) (string-width (match-string 2)))))
+       (put-text-property def-p1 def-p2 'face 'bold)))))
+
+(defun html2text-delete-tags (p1 p2 p3 p4)
+  (delete-region p1 p2)
+  (delete-region (- p3 (- p2 p1)) (- p4 (- p2 p1))))
+
+(defun html2text-delete-single-tag (p1 p2)
+  (delete-region p1 p2))
+
+(defun html2text-clean-hr (p1 p2)
+  (html2text-delete-single-tag p1 p2)
+  (goto-char p1)
+  (newline 1)
+  (insert (make-string fill-column ?-)))
+
+(defun html2text-clean-ul (p1 p2 p3 p4)
+  (html2text-delete-tags p1 p2 p3 p4)
+  (html2text-clean-list-items p1 (- p3 (- p1 p2)) "ul"))
+
+(defun html2text-clean-ol (p1 p2 p3 p4)
+  (html2text-delete-tags p1 p2 p3 p4)
+  (html2text-clean-list-items p1 (- p3 (- p1 p2)) "ol"))
+
+(defun html2text-clean-dl (p1 p2 p3 p4)
+  (html2text-delete-tags p1 p2 p3 p4)
+  (html2text-clean-dtdd p1 (- p3 (- p1 p2))))
+
+(defun html2text-clean-center (p1 p2 p3 p4)
+  (html2text-delete-tags p1 p2 p3 p4)
+  (center-region p1 (- p3 (- p2 p1))))
+
+(defun html2text-clean-bold (p1 p2 p3 p4)
+  (put-text-property p2 p3 'face 'bold)
+  (html2text-delete-tags p1 p2 p3 p4))
+
+(defun html2text-clean-title (p1 p2 p3 p4)
+  (put-text-property p2 p3 'face 'bold)
+  (html2text-delete-tags p1 p2 p3 p4))
+
+(defun html2text-clean-underline (p1 p2 p3 p4)
+  (put-text-property p2 p3 'face 'underline)
+  (html2text-delete-tags p1 p2 p3 p4))
+
+(defun html2text-clean-italic (p1 p2 p3 p4)
+  (put-text-property p2 p3 'face 'italic)
+  (html2text-delete-tags p1 p2 p3 p4))
+
+(defun html2text-clean-font (p1 p2 p3 p4)
+  (html2text-delete-tags p1 p2 p3 p4))
+
+(defun html2text-clean-blockquote (p1 p2 p3 p4)
+  (html2text-delete-tags p1 p2 p3 p4))
+
+(defun html2text-clean-anchor (p1 p2 p3 p4)
+  ;; If someone can explain how to make the URL clickable I will surely
+  ;; improve upon this.
+  ;; Maybe `goto-addr.el' can be used here.
+  (let* ((attr-list (html2text-get-attr p1 p2))
+        (href (html2text-attr-value attr-list "href")))
+    (delete-region p1 p4)
+    (when href
+      (goto-char p1)
+      (insert (if (string-match "\\`['\"].*['\"]\\'" href)
+                 (substring href 1 -1) href))
+      (put-text-property p1 (point) 'face 'bold))))
+
+;;
+;; </Functions to be called to format a tag-pair>
+;;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;
+;; <Functions to be called to fix up paragraphs>
+;;
+
+(defun html2text-fix-paragraph (p1 p2)
+  (goto-char p1)
+  (let ((refill-start)
+       (refill-stop))
+    (when (re-search-forward "<br>$" p2 t)
+      (goto-char p1)
+      (when (re-search-forward ".+[^<][^b][^r][^>]$" p2 t)
+       (beginning-of-line)
+       (setq refill-start (point))
+       (goto-char p2)
+       (re-search-backward ".+[^<][^b][^r][^>]$" refill-start t)
+       (forward-line 1)
+       (end-of-line)
+       ;; refill-stop should ideally be adjusted to
+       ;; accommodate the "<br>" strings which are removed
+       ;; between refill-start and refill-stop.  Can simply
+       ;; be returned from my-replace-string
+       (setq refill-stop (+ (point)
+                            (html2text-replace-string
+                             "<br>" ""
+                             refill-start (point))))
+       ;; (message "Point = %s  refill-stop = %s" (point) refill-stop)
+       ;; (sleep-for 4)
+       (fill-region refill-start refill-stop))))
+  (html2text-replace-string "<br>" "" p1 p2))
+
+;;
+;; This one is interactive ...
+;;
+(defun html2text-fix-paragraphs ()
+  "This _tries_ to fix up the paragraphs - this is done in quite a ad-hook
+fashion, quite close to pure guess-work. It does work in some cases though."
+  (interactive)
+  (goto-char (point-min))
+  (while (re-search-forward "^<br>$" nil t)
+    (delete-region (match-beginning 0) (match-end 0)))
+  ;; Removing lonely <br> on a single line, if they are left intact we
+  ;; don't have any paragraphs at all.
+  (goto-char (point-min))
+  (while (not (eobp))
+    (let ((p1 (point)))
+      (forward-paragraph 1)
+      ;;(message "Kaller fix med p1=%s  p2=%s " p1 (1- (point))) (sleep-for 5)
+      (html2text-fix-paragraph p1 (1- (point)))
+      (goto-char p1)
+      (when (not (eobp))
+       (forward-paragraph 1)))))
+
+;;
+;; </Functions to be called to fix up paragraphs>
+;;
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;
+;; <Interactive functions>
+;;
+
+(defun html2text-remove-tags (tag-list)
+  "Removes the tags listed in the list `html2text-remove-tag-list'.
+See the documentation for that variable."
+  (interactive)
+  (dolist (tag tag-list)
+    (goto-char (point-min))
+    (while (re-search-forward (format "\\(</?%s[^>]*>\\)" tag) (point-max) t)
+      (delete-region (match-beginning 0) (match-end 0)))))
+
+(defun html2text-format-tags ()
+  "See the variable `html2text-format-tag-list' for documentation."
+  (interactive)
+  (dolist (tag-and-function html2text-format-tag-list)
+    (let ((tag      (car tag-and-function))
+         (function (cdr tag-and-function)))
+      (goto-char (point-min))
+      (while (re-search-forward (format "\\(<%s\\( [^>]*\\)?>\\)" tag)
+                               (point-max) t)
+       (let ((p1)
+             (p2 (point))
+             (p3) (p4))
+         (search-backward "<" (point-min) t)
+         (setq p1 (point))
+         (unless (search-forward (format "</%s>" tag) (point-max) t)
+           (goto-char p2)
+           (insert (format "</%s>" tag)))
+         (setq p4 (point))
+         (search-backward "</" (point-min) t)
+         (setq p3 (point))
+         (funcall function p1 p2 p3 p4)
+         (goto-char p1))))))
+
+(defun html2text-substitute ()
+  "See the variable `html2text-replace-list' for documentation."
+  (interactive)
+  (dolist (e html2text-replace-list)
+    (goto-char (point-min))
+    (let ((old-string (car e))
+         (new-string (cdr e)))
+      (html2text-replace-string old-string new-string (point-min) (point-max)))))
+
+(defun html2text-format-single-elements ()
+  (interactive)
+  (dolist (tag-and-function html2text-format-single-element-list)
+    (let ((tag      (car tag-and-function))
+         (function (cdr tag-and-function)))
+      (goto-char (point-min))
+      (while (re-search-forward (format "\\(<%s\\( [^>]*\\)?>\\)" tag)
+                               (point-max) t)
+       (let ((p1)
+             (p2 (point)))
+         (search-backward "<" (point-min) t)
+         (setq p1 (point))
+         (funcall function p1 p2))))))
+
+;;
+;; Main function
+;;
+
+;;;###autoload
+(defun html2text ()
+  "Convert HTML to plain text in the current buffer."
+  (interactive)
+  (save-excursion
+    (let ((case-fold-search t)
+         (buffer-read-only))
+      (html2text-remove-tags html2text-remove-tag-list)
+      (html2text-format-tags)
+      (html2text-remove-tags html2text-remove-tag-list2)
+      (html2text-substitute)
+      (html2text-format-single-elements)
+      (html2text-fix-paragraphs))))
+
+;;
+;; </Interactive functions>
+;;
+(provide 'html2text)
+
+;;; html2text.el ends here
diff --git a/lisp/net/mailcap.el b/lisp/net/mailcap.el
new file mode 100644 (file)
index 0000000..609a8f4
--- /dev/null
@@ -0,0 +1,1054 @@
+;;; mailcap.el --- MIME media types configuration
+
+;; Copyright (C) 1998-2016 Free Software Foundation, Inc.
+
+;; Author: William M. Perry <wmperry@aventail.com>
+;;     Lars Magne Ingebrigtsen <larsi@gnus.org>
+;; Keywords: news, mail, multimedia
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Provides configuration of MIME media types from directly from Lisp
+;; and via the usual mailcap mechanism (RFC 1524).  Deals with
+;; mime.types similarly.
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+(autoload 'mail-header-parse-content-type "mail-parse")
+
+(defgroup mailcap nil
+  "Definition of viewers for MIME types."
+  :version "21.1"
+  :group 'mime)
+
+(defvar mailcap-parse-args-syntax-table
+  (let ((table (copy-syntax-table emacs-lisp-mode-syntax-table)))
+    (modify-syntax-entry ?' "\"" table)
+    (modify-syntax-entry ?` "\"" table)
+    (modify-syntax-entry ?{ "(" table)
+    (modify-syntax-entry ?} ")" table)
+    table)
+  "A syntax table for parsing SGML attributes.")
+
+(defvar mailcap-print-command
+  (mapconcat 'identity
+            (cons (if (boundp 'lpr-command)
+                      lpr-command
+                    "lpr")
+                  (when (boundp 'lpr-switches)
+                    (if (stringp lpr-switches)
+                        (list lpr-switches)
+                      lpr-switches)))
+            " ")
+  "Shell command (including switches) used to print PostScript files.")
+
+;; Postpone using defcustom for this as it's so big and we essentially
+;; have to have two copies of the data around then.  Perhaps just
+;; customize the Lisp viewers and rely on the normal configuration
+;; files for the rest?  -- fx
+(defvar mailcap-mime-data
+  `(("application"
+     ("vnd\\.ms-excel"
+      (viewer . "gnumeric %s")
+      (test   . (getenv "DISPLAY"))
+      (type . "application/vnd.ms-excel"))
+     ("x-x509-ca-cert"
+      (viewer . ssl-view-site-cert)
+      (type . "application/x-x509-ca-cert"))
+     ("x-x509-user-cert"
+      (viewer . ssl-view-user-cert)
+      (type . "application/x-x509-user-cert"))
+     ("octet-stream"
+      (viewer . mailcap-save-binary-file)
+      (non-viewer . t)
+      (type . "application/octet-stream"))
+     ("dvi"
+      (viewer . "xdvi -safer %s")
+      (test   . (eq window-system 'x))
+      ("needsx11")
+      (type   . "application/dvi")
+      ("print" . "dvips -qRP %s"))
+     ("dvi"
+      (viewer . "dvitty %s")
+      (test   . (not (getenv "DISPLAY")))
+      (type   . "application/dvi")
+      ("print" . "dvips -qRP %s"))
+     ("emacs-lisp"
+      (viewer . mailcap-maybe-eval)
+      (type   . "application/emacs-lisp"))
+     ("x-emacs-lisp"
+      (viewer . mailcap-maybe-eval)
+      (type   . "application/x-emacs-lisp"))
+     ("x-tar"
+      (viewer . mailcap-save-binary-file)
+      (non-viewer . t)
+      (type   . "application/x-tar"))
+     ("x-latex"
+      (viewer . tex-mode)
+      (type   . "application/x-latex"))
+     ("x-tex"
+      (viewer . tex-mode)
+      (type   . "application/x-tex"))
+     ("latex"
+      (viewer . tex-mode)
+      (type   . "application/latex"))
+     ("tex"
+      (viewer . tex-mode)
+      (type   . "application/tex"))
+     ("texinfo"
+      (viewer . texinfo-mode)
+      (type   . "application/tex"))
+     ("zip"
+      (viewer . mailcap-save-binary-file)
+      (non-viewer . t)
+      (type   . "application/zip")
+      ("copiousoutput"))
+     ("pdf"
+      (viewer . pdf-view-mode)
+      (type . "application/pdf")
+      (test . (eq window-system 'x)))
+     ("pdf"
+      (viewer . doc-view-mode)
+      (type . "application/pdf")
+      (test . (eq window-system 'x)))
+     ("pdf"
+      (viewer . "gv -safer %s")
+      (type . "application/pdf")
+      (test . window-system)
+      ("print" . ,(concat "pdf2ps %s - | " mailcap-print-command)))
+     ("pdf"
+      (viewer . "gpdf %s")
+      (type . "application/pdf")
+      ("print" . ,(concat "pdftops %s - | " mailcap-print-command))
+      (test . (eq window-system 'x)))
+     ("pdf"
+      (viewer . "xpdf %s")
+      (type . "application/pdf")
+      ("print" . ,(concat "pdftops %s - | " mailcap-print-command))
+      (test . (eq window-system 'x)))
+     ("pdf"
+      (viewer . ,(concat "pdftotext %s -"))
+      (type   . "application/pdf")
+      ("print" . ,(concat "pdftops %s - | " mailcap-print-command))
+      ("copiousoutput"))
+     ("postscript"
+      (viewer . "gv -safer %s")
+      (type . "application/postscript")
+      (test   . window-system)
+      ("print" . ,(concat mailcap-print-command " %s"))
+      ("needsx11"))
+     ("postscript"
+      (viewer . "ghostview -dSAFER %s")
+      (type . "application/postscript")
+      (test   . (eq window-system 'x))
+      ("print" . ,(concat mailcap-print-command " %s"))
+      ("needsx11"))
+     ("postscript"
+      (viewer . "ps2ascii %s")
+      (type . "application/postscript")
+      (test . (not (getenv "DISPLAY")))
+      ("print" . ,(concat mailcap-print-command " %s"))
+      ("copiousoutput"))
+     ("sieve"
+      (viewer . sieve-mode)
+      (type   . "application/sieve"))
+     ("pgp-keys"
+      (viewer . "gpg --import --interactive --verbose")
+      (type   . "application/pgp-keys")
+      ("needsterminal")))
+    ("audio"
+     ("x-mpeg"
+      (viewer . "maplay %s")
+      (type   . "audio/x-mpeg"))
+     (".*"
+      (viewer . "showaudio")
+      (type   . "audio/*")))
+    ("message"
+     ("rfc-*822"
+      (viewer . mm-view-message)
+      (test   . (and (featurep 'gnus)
+                    (gnus-alive-p)))
+      (type   . "message/rfc822"))
+     ("rfc-*822"
+      (viewer . vm-mode)
+      (type   . "message/rfc822"))
+     ("rfc-*822"
+      (viewer . view-mode)
+      (type   . "message/rfc822")))
+    ("image"
+     ("x-xwd"
+      (viewer  . "xwud -in %s")
+      (type    . "image/x-xwd")
+      ("compose" . "xwd -frame > %s")
+      (test    . (eq window-system 'x))
+      ("needsx11"))
+     ("x11-dump"
+      (viewer . "xwud -in %s")
+      (type . "image/x-xwd")
+      ("compose" . "xwd -frame > %s")
+      (test   . (eq window-system 'x))
+      ("needsx11"))
+     ("windowdump"
+      (viewer . "xwud -in %s")
+      (type . "image/x-xwd")
+      ("compose" . "xwd -frame > %s")
+      (test   . (eq window-system 'x))
+      ("needsx11"))
+     (".*"
+      (viewer . "display %s")
+      (type . "image/*")
+      (test   . (eq window-system 'x))
+      ("needsx11"))
+     (".*"
+      (viewer . "ee %s")
+      (type . "image/*")
+      (test   . (eq window-system 'x))
+      ("needsx11")))
+    ("text"
+     ("plain"
+      (viewer  . view-mode)
+      (type    . "text/plain"))
+     ("plain"
+      (viewer  . fundamental-mode)
+      (type    . "text/plain"))
+     ("enriched"
+      (viewer . enriched-decode)
+      (type   . "text/enriched"))
+     ("dns"
+      (viewer . dns-mode)
+      (type   . "text/dns")))
+    ("video"
+     ("mpeg"
+      (viewer . "mpeg_play %s")
+      (type   . "video/mpeg")
+      (test   . (eq window-system 'x))
+      ("needsx11")))
+    ("x-world"
+     ("x-vrml"
+      (viewer  . "webspace -remote %s -URL %u")
+      (type    . "x-world/x-vrml")
+      ("description"
+       "VRML document")))
+    ("archive"
+     ("tar"
+      (viewer . tar-mode)
+      (type . "archive/tar"))))
+  "The mailcap structure is an assoc list of assoc lists.
+1st assoc list is keyed on the major content-type
+2nd assoc list is keyed on the minor content-type (which can be a regexp)
+
+Which looks like:
+-----------------
+ ((\"application\"
+   (\"postscript\" . <info>))
+  (\"text\"
+   (\"plain\" . <info>)))
+
+Where <info> is another assoc list of the various information
+related to the mailcap RFC 1524.  This is keyed on the lowercase
+attribute name (viewer, test, etc).  This looks like:
+ ((viewer . VIEWERINFO)
+  (test   . TESTINFO)
+  (xxxx   . \"STRING\")
+  FLAG)
+
+Where VIEWERINFO specifies how the content-type is viewed.  Can be
+a string, in which case it is run through a shell, with appropriate
+parameters, or a symbol, in which case the symbol is `funcall'ed if
+and only if it exists as a function, with the buffer as an argument.
+
+TESTINFO is a test for the viewer's applicability, or nil.  If nil, it
+means the viewer is always valid.  If it is a Lisp function, it is
+called with a list of items from any extra fields from the
+Content-Type header as argument to return a boolean value for the
+validity.  Otherwise, if it is a non-function Lisp symbol or list
+whose car is a symbol, it is `eval'led to yield the validity.  If it
+is a string or list of strings, it represents a shell command to run
+to return a true or false shell value for the validity.")
+(put 'mailcap-mime-data 'risky-local-variable t)
+
+(defcustom mailcap-download-directory nil
+  "*Directory to which `mailcap-save-binary-file' downloads files by default.
+nil means your home directory."
+  :type '(choice (const :tag "Home directory" nil)
+                directory)
+  :group 'mailcap)
+
+(defvar mailcap-poor-system-types
+  '(ms-dos windows-nt)
+  "Systems that don't have a Unix-like directory hierarchy.")
+
+;;;
+;;; Utility functions
+;;;
+
+(defun mailcap-save-binary-file ()
+  (goto-char (point-min))
+  (unwind-protect
+      (let ((file (read-file-name
+                  "Filename to save as: "
+                  (or mailcap-download-directory "~/")))
+           (require-final-newline nil))
+       (write-region (point-min) (point-max) file))
+    (kill-buffer (current-buffer))))
+
+(defvar mailcap-maybe-eval-warning
+  "*** WARNING ***
+
+This MIME part contains untrusted and possibly harmful content.
+If you evaluate the Emacs Lisp code contained in it, a lot of nasty
+things can happen.  Please examine the code very carefully before you
+instruct Emacs to evaluate it.  You can browse the buffer containing
+the code using \\[scroll-other-window].
+
+If you are unsure what to do, please answer \"no\"."
+  "Text of warning message displayed by `mailcap-maybe-eval'.
+Make sure that this text consists only of few text lines.  Otherwise,
+Gnus might fail to display all of it.")
+
+(defun mailcap-maybe-eval ()
+  "Maybe evaluate a buffer of Emacs Lisp code."
+  (let ((lisp-buffer (current-buffer)))
+    (goto-char (point-min))
+    (when
+       (save-window-excursion
+         (delete-other-windows)
+         (let ((buffer (get-buffer-create (generate-new-buffer-name
+                                           "*Warning*"))))
+           (unwind-protect
+               (with-current-buffer buffer
+                 (insert (substitute-command-keys
+                          mailcap-maybe-eval-warning))
+                 (goto-char (point-min))
+                 (display-buffer buffer)
+                 (yes-or-no-p "This is potentially dangerous emacs-lisp code, evaluate it? "))
+             (kill-buffer buffer))))
+      (eval-buffer (current-buffer)))
+    (when (buffer-live-p lisp-buffer)
+      (with-current-buffer lisp-buffer
+       (emacs-lisp-mode)))))
+
+
+;;;
+;;; The mailcap parser
+;;;
+
+(defun mailcap-replace-regexp (regexp to-string)
+  ;; Quiet replace-regexp.
+  (goto-char (point-min))
+  (while (re-search-forward regexp nil t)
+    (replace-match to-string t nil)))
+
+(defvar mailcap-parsed-p nil)
+
+(defun mailcap-parse-mailcaps (&optional path force)
+  "Parse out all the mailcaps specified in a path string PATH.
+Components of PATH are separated by the `path-separator' character
+appropriate for this system.  If FORCE, re-parse even if already
+parsed.  If PATH is omitted, use the value of environment variable
+MAILCAPS if set; otherwise (on Unix) use the path from RFC 1524, plus
+/usr/local/etc/mailcap."
+  (interactive (list nil t))
+  (when (or (not mailcap-parsed-p)
+           force)
+    (cond
+     (path nil)
+     ((getenv "MAILCAPS") (setq path (getenv "MAILCAPS")))
+     ((memq system-type mailcap-poor-system-types)
+      (setq path '("~/.mailcap" "~/mail.cap" "~/etc/mail.cap")))
+     (t (setq path
+             ;; This is per RFC 1524, specifically
+             ;; with /usr before /usr/local.
+             '("~/.mailcap" "/etc/mailcap" "/usr/etc/mailcap"
+               "/usr/local/etc/mailcap"))))
+    (let ((fnames (reverse
+                  (if (stringp path)
+                      (split-string path path-separator t)
+                    path)))
+         fname)
+      (while fnames
+       (setq fname (car fnames))
+       (if (and (file-readable-p fname)
+                (file-regular-p fname))
+           (mailcap-parse-mailcap fname))
+       (setq fnames (cdr fnames))))
+      (setq mailcap-parsed-p t)))
+
+(defun mailcap-parse-mailcap (fname)
+  "Parse out the mailcap file specified by FNAME."
+  (let (major                          ; The major mime type (image/audio/etc)
+       minor                           ; The minor mime type (gif, basic, etc)
+       save-pos                        ; Misc saved positions used in parsing
+       viewer                          ; How to view this mime type
+       info                            ; Misc info about this mime type
+       )
+    (with-temp-buffer
+      (insert-file-contents fname)
+      (set-syntax-table mailcap-parse-args-syntax-table)
+      (mailcap-replace-regexp "#.*" "")        ; Remove all comments
+      (mailcap-replace-regexp "\\\\[ \t]*\n" " ") ; And collapse spaces
+      (mailcap-replace-regexp "\n+" "\n") ; And blank lines
+      (goto-char (point-max))
+      (skip-chars-backward " \t\n")
+      (delete-region (point) (point-max))
+      (while (not (bobp))
+       (skip-chars-backward " \t\n")
+       (beginning-of-line)
+       (setq save-pos (point)
+             info nil)
+       (skip-chars-forward "^/; \t\n")
+       (downcase-region save-pos (point))
+       (setq major (buffer-substring save-pos (point)))
+       (skip-chars-forward " \t")
+       (setq minor "")
+       (when (eq (char-after) ?/)
+         (forward-char)
+         (skip-chars-forward " \t")
+         (setq save-pos (point))
+         (skip-chars-forward "^; \t\n")
+         (downcase-region save-pos (point))
+         (setq minor
+               (cond
+                ((eq ?* (or (char-after save-pos) 0)) ".*")
+                ((= (point) save-pos) ".*")
+                (t (regexp-quote (buffer-substring save-pos (point)))))))
+       (skip-chars-forward " \t")
+       ;;; Got the major/minor chunks, now for the viewers/etc
+       ;;; The first item _must_ be a viewer, according to the
+       ;;; RFC for mailcap files (#1524)
+       (setq viewer "")
+       (when (eq (char-after) ?\;)
+         (forward-char)
+         (skip-chars-forward " \t")
+         (setq save-pos (point))
+         (skip-chars-forward "^;\n")
+         ;; skip \;
+         (while (eq (char-before) ?\\)
+           (backward-delete-char 1)
+           (forward-char)
+           (skip-chars-forward "^;\n"))
+         (if (eq (or (char-after save-pos) 0) ?')
+             (setq viewer (progn
+                            (narrow-to-region (1+ save-pos) (point))
+                            (goto-char (point-min))
+                            (prog1
+                                (read (current-buffer))
+                              (goto-char (point-max))
+                              (widen))))
+           (setq viewer (buffer-substring save-pos (point)))))
+       (setq save-pos (point))
+       (end-of-line)
+       (unless (equal viewer "")
+         (setq info (nconc (list (cons 'viewer viewer)
+                                 (cons 'type (concat major "/"
+                                                     (if (string= minor ".*")
+                                                         "*" minor))))
+                           (mailcap-parse-mailcap-extras save-pos (point))))
+         (mailcap-mailcap-entry-passes-test info)
+         (mailcap-add-mailcap-entry major minor info))
+       (beginning-of-line)))))
+
+(defun mailcap-parse-mailcap-extras (st nd)
+  "Grab all the extra stuff from a mailcap entry."
+  (let (
+       name                            ; From name=
+       value                           ; its value
+       results                         ; Assoc list of results
+       name-pos                        ; Start of XXXX= position
+       val-pos                         ; Start of value position
+       done                            ; Found end of \'d ;s?
+       )
+    (save-restriction
+      (narrow-to-region st nd)
+      (goto-char (point-min))
+      (skip-chars-forward " \n\t;")
+      (while (not (eobp))
+       (setq done nil)
+       (setq name-pos (point))
+       (skip-chars-forward "^ \n\t=;")
+       (downcase-region name-pos (point))
+       (setq name (buffer-substring name-pos (point)))
+       (skip-chars-forward " \t\n")
+       (if (not (eq (char-after (point)) ?=)) ; There is no value
+           (setq value t)
+         (skip-chars-forward " \t\n=")
+         (setq val-pos (point))
+         (if (memq (char-after val-pos) '(?\" ?'))
+             (progn
+               (setq val-pos (1+ val-pos))
+               (condition-case nil
+                   (progn
+                     (forward-sexp 1)
+                     (backward-char 1))
+                 (error (goto-char (point-max)))))
+           (while (not done)
+             (skip-chars-forward "^;")
+             (if (eq (char-after (1- (point))) ?\\ )
+                 (progn
+                   (subst-char-in-region (1- (point)) (point) ?\\ ? )
+                   (skip-chars-forward ";"))
+               (setq done t))))
+         (setq value (buffer-substring val-pos (point))))
+       ;; `test' as symbol, others like "copiousoutput" and "needsx11" as
+       ;; strings
+       (setq results (cons (cons (if (string-equal name "test")
+                                      'test
+                                    name)
+                                  value) results))
+       (skip-chars-forward " \";\n\t"))
+      results)))
+
+(defun mailcap-mailcap-entry-passes-test (info)
+  "Replace the test clause of INFO itself with a boolean for some cases.
+This function supports only `test -n $DISPLAY' and `test -z $DISPLAY',
+replaces them with t or nil.  As for others or if INFO has a interactive
+spec (needsterm, needsterminal, or needsx11) but DISPLAY is not set,
+the test clause will be unchanged."
+  (let ((test (assq 'test info))       ; The test clause
+       status)
+    (setq status (and test (split-string (cdr test) " ")))
+    (if (and (or (assoc "needsterm" info)
+                (assoc "needsterminal" info)
+                (assoc "needsx11" info))
+            (not (getenv "DISPLAY")))
+       (setq status nil)
+      (cond
+       ((and (equal (nth 0 status) "test")
+            (equal (nth 1 status) "-n")
+            (or (equal (nth 2 status) "$DISPLAY")
+                (equal (nth 2 status) "\"$DISPLAY\"")))
+       (setq status (if (getenv "DISPLAY") t nil)))
+       ((and (equal (nth 0 status) "test")
+            (equal (nth 1 status) "-z")
+            (or (equal (nth 2 status) "$DISPLAY")
+                (equal (nth 2 status) "\"$DISPLAY\"")))
+       (setq status (if (getenv "DISPLAY") nil t)))
+       (test nil)
+       (t nil)))
+    (and test (listp test) (setcdr test status))))
+
+;;;
+;;; The action routines.
+;;;
+
+(defun mailcap-possible-viewers (major minor)
+  "Return a list of possible viewers from MAJOR for minor type MINOR."
+  (let ((exact '())
+       (wildcard '()))
+    (while major
+      (cond
+       ((equal (car (car major)) minor)
+       (setq exact (cons (cdr (car major)) exact)))
+       ((and minor (string-match (concat "^" (car (car major)) "$") minor))
+       (setq wildcard (cons (cdr (car major)) wildcard))))
+      (setq major (cdr major)))
+    (nconc exact wildcard)))
+
+(defun mailcap-unescape-mime-test (test type-info)
+  (let (save-pos save-chr subst)
+    (cond
+     ((symbolp test) test)
+     ((and (listp test) (symbolp (car test))) test)
+     ((or (stringp test)
+         (and (listp test) (stringp (car test))
+              (setq test (mapconcat 'identity test " "))))
+      (with-temp-buffer
+       (insert test)
+       (goto-char (point-min))
+       (while (not (eobp))
+         (skip-chars-forward "^%")
+         (if (/= (- (point)
+                    (progn (skip-chars-backward "\\\\")
+                           (point)))
+                 0)                    ; It is an escaped %
+             (progn
+               (delete-char 1)
+               (skip-chars-forward "%."))
+           (setq save-pos (point))
+           (skip-chars-forward "%")
+           (setq save-chr (char-after (point)))
+           ;; Escapes:
+           ;; %s: name of a file for the body data
+           ;; %t: content-type
+           ;; %{<parameter name}: value of parameter in mailcap entry
+           ;; %n: number of sub-parts for multipart content-type
+           ;; %F: a set of content-type/filename pairs for multiparts
+           (cond
+            ((null save-chr) nil)
+            ((= save-chr ?t)
+             (delete-region save-pos (progn (forward-char 1) (point)))
+             (insert (or (cdr (assq 'type type-info)) "\"\"")))
+            ((memq save-chr '(?M ?n ?F))
+             (delete-region save-pos (progn (forward-char 1) (point)))
+             (insert "\"\""))
+            ((= save-chr ?{)
+             (forward-char 1)
+             (skip-chars-forward "^}")
+             (downcase-region (+ 2 save-pos) (point))
+             (setq subst (buffer-substring (+ 2 save-pos) (point)))
+             (delete-region save-pos (1+ (point)))
+             (insert (or (cdr (assoc subst type-info)) "\"\"")))
+            (t nil))))
+       (buffer-string)))
+     (t (error "Bad value to mailcap-unescape-mime-test: %s" test)))))
+
+(defvar mailcap-viewer-test-cache nil)
+
+(defun mailcap-viewer-passes-test (viewer-info type-info)
+  "Return non-nil if viewer specified by VIEWER-INFO passes its test clause.
+Also return non-nil if it has no test clause.  TYPE-INFO is an argument
+to supply to the test."
+  (let* ((test-info (assq 'test viewer-info))
+        (test (cdr test-info))
+        (otest test)
+        (viewer (cdr (assq 'viewer viewer-info)))
+        (default-directory (expand-file-name "~/"))
+        status parsed-test cache result)
+    (cond ((not (or (stringp viewer) (fboundp viewer)))
+          nil)                         ; Non-existent Lisp function
+         ((setq cache (assoc test mailcap-viewer-test-cache))
+          (cadr cache))
+         ((not test-info) t)           ; No test clause
+         (t
+          (setq
+           result
+           (cond
+            ((not test) nil)           ; Already failed test
+            ((eq test t) t)            ; Already passed test
+            ((functionp test)          ; Lisp function as test
+             (funcall test type-info))
+            ((and (symbolp test)       ; Lisp variable as test
+                  (boundp test))
+             (symbol-value test))
+            ((and (listp test)         ; List to be eval'd
+                  (symbolp (car test)))
+             (eval test))
+            (t
+             (setq test (mailcap-unescape-mime-test test type-info)
+                   test (list shell-file-name nil nil nil
+                              shell-command-switch test)
+                   status (apply 'call-process test))
+             (eq 0 status))))
+          (push (list otest result) mailcap-viewer-test-cache)
+          result))))
+
+(defun mailcap-add-mailcap-entry (major minor info)
+  (let ((old-major (assoc major mailcap-mime-data)))
+    (if (null old-major)               ; New major area
+       (setq mailcap-mime-data
+             (cons (cons major (list (cons minor info)))
+                   mailcap-mime-data))
+      (let ((cur-minor (assoc minor old-major)))
+       (cond
+        ((or (null cur-minor)          ; New minor area, or
+             (assq 'test info))        ; Has a test, insert at beginning
+         (setcdr old-major (cons (cons minor info) (cdr old-major))))
+        ((and (not (assq 'test info))  ; No test info, replace completely
+              (not (assq 'test cur-minor))
+              (equal (assq 'viewer info)  ; Keep alternative viewer
+                     (assq 'viewer cur-minor)))
+         (setcdr cur-minor info))
+        (t
+         (setcdr old-major (cons (cons minor info) (cdr old-major))))))
+      )))
+
+(defun mailcap-add (type viewer &optional test)
+  "Add VIEWER as a handler for TYPE.
+If TEST is not given, it defaults to t."
+  (let ((tl (split-string type "/")))
+    (when (or (not (car tl))
+             (not (cadr tl)))
+      (error "%s is not a valid MIME type" type))
+    (mailcap-add-mailcap-entry
+     (car tl) (cadr tl)
+     `((viewer . ,viewer)
+       (test . ,(if test test t))
+       (type . ,type)))))
+
+;;;
+;;; The main whabbo
+;;;
+
+(defun mailcap-viewer-lessp (x y)
+  "Return t if viewer X is more desirable than viewer Y."
+  (let ((x-wild (string-match "[*?]" (or (cdr-safe (assq 'type x)) "")))
+       (y-wild (string-match "[*?]" (or (cdr-safe (assq 'type y)) "")))
+       (x-lisp (not (stringp (or (cdr-safe (assq 'viewer x)) ""))))
+       (y-lisp (not (stringp (or (cdr-safe (assq 'viewer y)) "")))))
+    (cond
+     ((and x-wild (not y-wild))
+      nil)
+     ((and (not x-wild) y-wild)
+      t)
+     ((and (not y-lisp) x-lisp)
+      t)
+     (t nil))))
+
+(defun mailcap-mime-info (string &optional request no-decode)
+  "Get the MIME viewer command for STRING, return nil if none found.
+Expects a complete content-type header line as its argument.
+
+Second argument REQUEST specifies what information to return.  If it is
+nil or the empty string, the viewer (second field of the mailcap
+entry) will be returned.  If it is a string, then the mailcap field
+corresponding to that string will be returned (print, description,
+whatever).  If a number, then all the information for this specific
+viewer is returned.  If `all', then all possible viewers for
+this type is returned.
+
+If NO-DECODE is non-nil, don't decode STRING."
+  ;; NO-DECODE avoids calling `mail-header-parse-content-type' from
+  ;; `mail-parse.el'
+  (let (
+       major                           ; Major encoding (text, etc)
+       minor                           ; Minor encoding (html, etc)
+       info                            ; Other info
+       save-pos                        ; Misc. position during parse
+       major-info                      ; (assoc major mailcap-mime-data)
+       minor-info                      ; (assoc minor major-info)
+       test                            ; current test proc.
+       viewers                         ; Possible viewers
+       passed                          ; Viewers that passed the test
+       viewer                          ; The one and only viewer
+       ctl)
+    (save-excursion
+      (setq ctl
+           (if no-decode
+               (list (or string "text/plain"))
+             (mail-header-parse-content-type (or string "text/plain"))))
+      (setq major (split-string (car ctl) "/"))
+      (setq minor (cadr major)
+           major (car major))
+      (when (setq major-info (cdr (assoc major mailcap-mime-data)))
+       (when (setq viewers (mailcap-possible-viewers major-info minor))
+         (setq info (mapcar (lambda (a) (cons (symbol-name (car a))
+                                              (cdr a)))
+                            (cdr ctl)))
+         (while viewers
+           (if (mailcap-viewer-passes-test (car viewers) info)
+               (setq passed (cons (car viewers) passed)))
+           (setq viewers (cdr viewers)))
+         (setq passed (sort passed 'mailcap-viewer-lessp))
+         (setq viewer (car passed))))
+      (when (and (stringp (cdr (assq 'viewer viewer)))
+                passed)
+       (setq viewer (car passed)))
+      (cond
+       ((and (null viewer) (not (equal major "default")) request)
+       (mailcap-mime-info "default" request no-decode))
+       ((or (null request) (equal request ""))
+       (mailcap-unescape-mime-test (cdr (assq 'viewer viewer)) info))
+       ((stringp request)
+       (mailcap-unescape-mime-test
+        (cdr-safe (assoc request viewer)) info))
+       ((eq request 'all)
+       passed)
+       (t
+       ;; MUST make a copy *sigh*, else we modify mailcap-mime-data
+       (setq viewer (copy-sequence viewer))
+       (let ((view (assq 'viewer viewer))
+             (test (assq 'test viewer)))
+         (if view (setcdr view (mailcap-unescape-mime-test (cdr view) info)))
+         (if test (setcdr test (mailcap-unescape-mime-test (cdr test) info))))
+       viewer)))))
+
+;;;
+;;; Experimental MIME-types parsing
+;;;
+
+(defvar mailcap-mime-extensions
+  '((""       . "text/plain")
+    (".1"     . "text/plain")  ;; Manual pages
+    (".3"     . "text/plain")
+    (".8"     . "text/plain")
+    (".abs"   . "audio/x-mpeg")
+    (".aif"   . "audio/aiff")
+    (".aifc"  . "audio/aiff")
+    (".aiff"  . "audio/aiff")
+    (".ano"   . "application/x-annotator")
+    (".au"    . "audio/ulaw")
+    (".avi"   . "video/x-msvideo")
+    (".bcpio" . "application/x-bcpio")
+    (".bin"   . "application/octet-stream")
+    (".cdf"   . "application/x-netcdr")
+    (".cpio"  . "application/x-cpio")
+    (".csh"   . "application/x-csh")
+    (".css"   . "text/css")
+    (".dvi"   . "application/x-dvi")
+    (".diff"  . "text/x-patch")
+    (".dpatch". "test/x-patch")
+    (".el"    . "application/emacs-lisp")
+    (".eps"   . "application/postscript")
+    (".etx"   . "text/x-setext")
+    (".exe"   . "application/octet-stream")
+    (".fax"   . "image/x-fax")
+    (".gif"   . "image/gif")
+    (".hdf"   . "application/x-hdf")
+    (".hqx"   . "application/mac-binhex40")
+    (".htm"   . "text/html")
+    (".html"  . "text/html")
+    (".icon"  . "image/x-icon")
+    (".ief"   . "image/ief")
+    (".jpg"   . "image/jpeg")
+    (".macp"  . "image/x-macpaint")
+    (".man"   . "application/x-troff-man")
+    (".me"    . "application/x-troff-me")
+    (".mif"   . "application/mif")
+    (".mov"   . "video/quicktime")
+    (".movie" . "video/x-sgi-movie")
+    (".mp2"   . "audio/x-mpeg")
+    (".mp3"   . "audio/x-mpeg")
+    (".mp2a"  . "audio/x-mpeg2")
+    (".mpa"   . "audio/x-mpeg")
+    (".mpa2"  . "audio/x-mpeg2")
+    (".mpe"   . "video/mpeg")
+    (".mpeg"  . "video/mpeg")
+    (".mpega" . "audio/x-mpeg")
+    (".mpegv" . "video/mpeg")
+    (".mpg"   . "video/mpeg")
+    (".mpv"   . "video/mpeg")
+    (".ms"    . "application/x-troff-ms")
+    (".nc"    . "application/x-netcdf")
+    (".nc"    . "application/x-netcdf")
+    (".oda"   . "application/oda")
+    (".patch" . "text/x-patch")
+    (".pbm"   . "image/x-portable-bitmap")
+    (".pdf"   . "application/pdf")
+    (".pgm"   . "image/portable-graymap")
+    (".pict"  . "image/pict")
+    (".png"   . "image/png")
+    (".pnm"   . "image/x-portable-anymap")
+    (".pod"   . "text/plain")
+    (".ppm"   . "image/portable-pixmap")
+    (".ps"    . "application/postscript")
+    (".qt"    . "video/quicktime")
+    (".ras"   . "image/x-raster")
+    (".rgb"   . "image/x-rgb")
+    (".rtf"   . "application/rtf")
+    (".rtx"   . "text/richtext")
+    (".sh"    . "application/x-sh")
+    (".sit"   . "application/x-stuffit")
+    (".siv"   . "application/sieve")
+    (".snd"   . "audio/basic")
+    (".soa"   . "text/dns")
+    (".src"   . "application/x-wais-source")
+    (".tar"   . "archive/tar")
+    (".tcl"   . "application/x-tcl")
+    (".tex"   . "application/x-tex")
+    (".texi"  . "application/texinfo")
+    (".tga"   . "image/x-targa")
+    (".tif"   . "image/tiff")
+    (".tiff"  . "image/tiff")
+    (".tr"    . "application/x-troff")
+    (".troff" . "application/x-troff")
+    (".tsv"   . "text/tab-separated-values")
+    (".txt"   . "text/plain")
+    (".vbs"   . "video/mpeg")
+    (".vox"   . "audio/basic")
+    (".vrml"  . "x-world/x-vrml")
+    (".wav"   . "audio/x-wav")
+    (".xls"   . "application/vnd.ms-excel")
+    (".wrl"   . "x-world/x-vrml")
+    (".xbm"   . "image/xbm")
+    (".xpm"   . "image/xpm")
+    (".xwd"   . "image/windowdump")
+    (".zip"   . "application/zip")
+    (".ai"    . "application/postscript")
+    (".jpe"   . "image/jpeg")
+    (".jpeg"  . "image/jpeg")
+    (".org"   . "text/x-org"))
+  "An alist of file extensions and corresponding MIME content-types.
+This exists for you to customize the information in Lisp.  It is
+merged with values from mailcap files by `mailcap-parse-mimetypes'.")
+
+(defvar mailcap-mimetypes-parsed-p nil)
+
+(defun mailcap-parse-mimetypes (&optional path force)
+  "Parse out all the mimetypes specified in a Unix-style path string PATH.
+Components of PATH are separated by the `path-separator' character
+appropriate for this system.  If PATH is omitted, use the value of
+environment variable MIMETYPES if set; otherwise use a default path.
+If FORCE, re-parse even if already parsed."
+  (interactive (list nil t))
+  (when (or (not mailcap-mimetypes-parsed-p)
+           force)
+    (cond
+     (path nil)
+     ((getenv "MIMETYPES") (setq path (getenv "MIMETYPES")))
+     ((memq system-type mailcap-poor-system-types)
+      (setq path '("~/mime.typ" "~/etc/mime.typ")))
+     (t (setq path
+             ;; mime.types seems to be the normal name, definitely so
+             ;; on current GNUish systems.  The search order follows
+             ;; that for mailcap.
+             '("~/.mime.types"
+               "/etc/mime.types"
+               "/usr/etc/mime.types"
+               "/usr/local/etc/mime.types"
+               "/usr/local/www/conf/mime.types"
+               "~/.mime-types"
+               "/etc/mime-types"
+               "/usr/etc/mime-types"
+               "/usr/local/etc/mime-types"
+               "/usr/local/www/conf/mime-types"))))
+    (let ((fnames (reverse (if (stringp path)
+                              (split-string path path-separator t)
+                            path)))
+         fname)
+      (while fnames
+       (setq fname (car fnames))
+       (if (and (file-readable-p fname))
+           (mailcap-parse-mimetype-file fname))
+       (setq fnames (cdr fnames))))
+    (setq mailcap-mimetypes-parsed-p t)))
+
+(defun mailcap-parse-mimetype-file (fname)
+  "Parse out a mime-types file FNAME."
+  (let (type                           ; The MIME type for this line
+       extns                           ; The extensions for this line
+       save-pos                        ; Misc. saved buffer positions
+       )
+    (with-temp-buffer
+      (insert-file-contents fname)
+      (mailcap-replace-regexp "#.*" "")
+      (mailcap-replace-regexp "\n+" "\n")
+      (mailcap-replace-regexp "[ \t]+$" "")
+      (goto-char (point-max))
+      (skip-chars-backward " \t\n")
+      (delete-region (point) (point-max))
+      (goto-char (point-min))
+      (while (not (eobp))
+       (skip-chars-forward " \t\n")
+       (setq save-pos (point))
+       (skip-chars-forward "^ \t\n")
+       (downcase-region save-pos (point))
+       (setq type (buffer-substring save-pos (point)))
+       (while (not (eolp))
+         (skip-chars-forward " \t")
+         (setq save-pos (point))
+         (skip-chars-forward "^ \t\n")
+         (setq extns (cons (buffer-substring save-pos (point)) extns)))
+       (while extns
+         (setq mailcap-mime-extensions
+               (cons
+                (cons (if (= (string-to-char (car extns)) ?.)
+                          (car extns)
+                        (concat "." (car extns))) type)
+                mailcap-mime-extensions)
+               extns (cdr extns)))))))
+
+(defun mailcap-extension-to-mime (extn)
+  "Return the MIME content type of the file extensions EXTN."
+  (mailcap-parse-mimetypes)
+  (if (and (stringp extn)
+          (not (eq (string-to-char extn) ?.)))
+      (setq extn (concat "." extn)))
+  (cdr (assoc (downcase extn) mailcap-mime-extensions)))
+
+;; Unused?
+(defalias 'mailcap-command-p 'executable-find)
+
+(defun mailcap-mime-types ()
+  "Return a list of MIME media types."
+  (mailcap-parse-mimetypes)
+  (delete-dups
+   (nconc
+    (mapcar 'cdr mailcap-mime-extensions)
+    (apply
+     'nconc
+     (mapcar
+      (lambda (l)
+       (delq nil
+             (mapcar
+              (lambda (m)
+                (let ((type (cdr (assq 'type (cdr m)))))
+                  (if (equal (cadr (split-string type "/"))
+                             "*")
+                      nil
+                    type)))
+              (cdr l))))
+      mailcap-mime-data)))))
+
+;;;
+;;; Useful supplementary functions
+;;;
+
+(defun mailcap-file-default-commands (files)
+  "Return a list of default commands for FILES."
+  (mailcap-parse-mailcaps)
+  (mailcap-parse-mimetypes)
+  (let* ((all-mime-type
+         ;; All unique MIME types from file extensions
+         (delete-dups
+          (mapcar (lambda (file)
+                    (mailcap-extension-to-mime
+                     (file-name-extension file t)))
+                  files)))
+        (all-mime-info
+         ;; All MIME info lists
+         (delete-dups
+          (mapcar (lambda (mime-type)
+                    (mailcap-mime-info mime-type 'all))
+                  all-mime-type)))
+        (common-mime-info
+         ;; Intersection of mime-infos from different mime-types;
+         ;; or just the first MIME info for a single MIME type
+         (if (cdr all-mime-info)
+             (delq nil (mapcar (lambda (mi1)
+                                 (unless (memq nil (mapcar
+                                                    (lambda (mi2)
+                                                      (member mi1 mi2))
+                                                    (cdr all-mime-info)))
+                                   mi1))
+                               (car all-mime-info)))
+           (car all-mime-info)))
+        (commands
+         ;; Command strings from `viewer' field of the MIME info
+         (delete-dups
+          (delq nil (mapcar
+                     (lambda (mime-info)
+                       (let ((command (cdr (assoc 'viewer mime-info))))
+                         (if (stringp command)
+                             (replace-regexp-in-string
+                              ;; Replace mailcap's `%s' placeholder
+                              ;; with dired's `?' placeholder
+                              "%s" "?"
+                              (replace-regexp-in-string
+                               ;; Remove the final filename placeholder
+                               "[ \t\n]*\\('\\)?%s\\1?[ \t\n]*\\'" ""
+                               command nil t)
+                              nil t))))
+                            common-mime-info)))))
+    commands))
+
+(defun mailcap-view-mime (type)
+  "View the data in the current buffer that has MIME type TYPE.
+`mailcap-mime-data' determines the method to use."
+  (let ((method (mailcap-mime-info type)))
+    (if (stringp method)
+       (shell-command-on-region (point-min) (point-max)
+                                ;; Use stdin as the "%s".
+                                (format method "-")
+                                (current-buffer)
+                                t)
+      (funcall method))))
+
+(provide 'mailcap)
+
+;;; mailcap.el ends here
diff --git a/lisp/net/pop3.el b/lisp/net/pop3.el
new file mode 100644 (file)
index 0000000..1695bbd
--- /dev/null
@@ -0,0 +1,914 @@
+;;; pop3.el --- Post Office Protocol (RFC 1460) interface
+
+;; Copyright (C) 1996-2016 Free Software Foundation, Inc.
+
+;; Author: Richard L. Pieri <ratinox@peorth.gweep.net>
+;; Maintainer: emacs-devel@gnu.org
+;; Keywords: mail
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; Most of the standard Post Office Protocol version 3 (RFC 1460) commands
+;; are implemented.  The LIST command has not been implemented due to lack
+;; of actual usefulness.
+;; The optional POP3 command TOP has not been implemented.
+
+;; This program was inspired by Kyle E. Jones's vm-pop program.
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+
+(require 'mail-utils)
+(defvar parse-time-months)
+
+(defgroup pop3 nil
+  "Post Office Protocol."
+  :group 'mail
+  :group 'mail-source)
+
+(defcustom pop3-maildrop (or (user-login-name)
+                            (getenv "LOGNAME")
+                            (getenv "USER"))
+  "*POP3 maildrop."
+  :version "22.1" ;; Oort Gnus
+  :type 'string
+  :group 'pop3)
+
+(defcustom pop3-mailhost (or (getenv "MAILHOST") ;; nil -> mismatch
+                            "pop3")
+  "*POP3 mailhost."
+  :version "22.1" ;; Oort Gnus
+  :type 'string
+  :group 'pop3)
+
+(defcustom pop3-port 110
+  "*POP3 port."
+  :version "22.1" ;; Oort Gnus
+  :type 'number
+  :group 'pop3)
+
+(defcustom pop3-password-required t
+  "*Non-nil if a password is required when connecting to POP server."
+  :version "22.1" ;; Oort Gnus
+  :type 'boolean
+  :group 'pop3)
+
+;; Should this be customizable?
+(defvar pop3-password nil
+  "*Password to use when connecting to POP server.")
+
+(defcustom pop3-authentication-scheme 'pass
+  "*POP3 authentication scheme.
+Defaults to `pass', for the standard USER/PASS authentication.  The other
+valid value is `apop'."
+  :type '(choice (const :tag "Normal user/password" pass)
+                (const :tag "APOP" apop))
+  :version "22.1" ;; Oort Gnus
+  :group 'pop3)
+
+(defcustom pop3-stream-length 100
+  "How many messages should be requested at one time.
+The lower the number, the more latency-sensitive the fetching
+will be.  If your pop3 server doesn't support streaming at all,
+set this to 1."
+  :type 'number
+  :version "24.1"
+  :group 'pop3)
+
+(defcustom pop3-leave-mail-on-server nil
+  "Non-nil if the mail is to be left on the POP server after fetching.
+Mails once fetched will never be fetched again by the UIDL control.
+
+If this is neither nil nor a number, all mails will be left on the
+server.  If this is a number, leave mails on the server for this many
+days since you first checked new mails.  If this is nil, mails will be
+deleted on the server right after fetching.
+
+Gnus users should use the `:leave' keyword in a mail source to direct
+the behavior per server, rather than directly modifying this value.
+
+Note that POP servers maintain no state information between sessions,
+so what the client believes is there and what is actually there may
+not match up.  If they do not, then you may get duplicate mails or
+the whole thing can fall apart and leave you with a corrupt mailbox."
+  :version "24.4"
+  :type '(choice (const :tag "Don't leave mails" nil)
+                (const :tag "Leave all mails" t)
+                (number :tag "Leave mails for this many days" :value 14))
+  :group 'pop3)
+
+(defcustom pop3-uidl-file "~/.pop3-uidl"
+  "File used to save UIDL."
+  :version "24.4"
+  :type 'file
+  :group 'pop3)
+
+(defcustom pop3-uidl-file-backup '(0 9)
+  "How to backup the UIDL file `pop3-uidl-file' when updating.
+If it is a list of numbers, the first one binds `kept-old-versions' and
+the other binds `kept-new-versions' to keep number of oldest and newest
+versions.  Otherwise, the value binds `version-control' (which see).
+
+Note: Backup will take place whenever you check new mails on a server.
+So, you may lose the backup files having been saved before a trouble
+if you set it so as to make too few backups whereas you have access to
+many servers."
+  :version "24.4"
+  :type '(choice (group :tag "Keep versions" :format "\n%v" :indent 3
+                       (number :tag "oldest")
+                       (number :tag "newest"))
+                (sexp :format "%v"
+                      :match (lambda (widget value)
+                               (condition-case nil
+                                   (not (and (numberp (car value))
+                                             (numberp (car (cdr value)))))
+                                 (error t)))))
+  :group 'pop3)
+
+(defvar pop3-timestamp nil
+  "Timestamp returned when initially connected to the POP server.
+Used for APOP authentication.")
+
+(defvar pop3-read-point nil)
+(defvar pop3-debug nil)
+
+;; Borrowed from nnheader-accept-process-output in nnheader.el.  See the
+;; comments there for explanations about the values.
+
+(eval-and-compile
+  (if (and (fboundp 'nnheader-accept-process-output)
+          (boundp 'nnheader-read-timeout))
+      (defalias 'pop3-accept-process-output 'nnheader-accept-process-output)
+    ;; Borrowed from `nnheader.el':
+    (defvar pop3-read-timeout
+      (if (string-match "windows-nt\\|os/2\\|cygwin"
+                       (symbol-name system-type))
+         1.0
+       0.01)
+      "How long pop3 should wait between checking for the end of output.
+Shorter values mean quicker response, but are more CPU intensive.")
+    (defun pop3-accept-process-output (process)
+      (accept-process-output
+       process
+       (truncate pop3-read-timeout)
+       (truncate (* (- pop3-read-timeout
+                      (truncate pop3-read-timeout))
+                   1000))))))
+
+(defvar pop3-uidl)
+;; List of UIDLs of existing messages at present in the server:
+;; ("UIDL1" "UIDL2" "UIDL3"...)
+
+(defvar pop3-uidl-saved)
+;; Locally saved UIDL data; an alist of the server, the user, and the UIDL
+;; and timestamp pairs:
+;; (("SERVER_A" ("USER_A1" "UIDL1" TIMESTAMP1 "UIDL2" TIMESTAMP2...)
+;;              ("USER_A2" "UIDL1" TIMESTAMP1 "UIDL2" TIMESTAMP2...)
+;;              ...)
+;;  ("SERVER_B" ("USER_B1" "UIDL1" TIMESTAMP1 "UIDL2" TIMESTAMP2...)
+;;              ("USER_B2" "UIDL1" TIMESTAMP1 "UIDL2" TIMESTAMP2...)
+;;              ...))
+;; Where TIMESTAMP is the most significant two digits of an Emacs time,
+;; i.e. the return value of `current-time'.
+
+;;;###autoload
+(defun pop3-movemail (file)
+  "Transfer contents of a maildrop to the specified FILE.
+Use streaming commands."
+  (let ((process (pop3-open-server pop3-mailhost pop3-port))
+       messages total-size
+       pop3-uidl
+       pop3-uidl-saved)
+    (pop3-logon process)
+    (if pop3-leave-mail-on-server
+       (setq messages (pop3-uidl-stat process)
+             total-size (cadr messages)
+             messages (car messages))
+      (let ((size (pop3-stat process)))
+       (dotimes (i (car size)) (push (1+ i) messages))
+       (setq messages (nreverse messages)
+             total-size (cadr size))))
+    (when messages
+      (with-current-buffer (process-buffer process)
+       (pop3-send-streaming-command process "RETR" messages total-size)
+       (pop3-write-to-file file messages)
+       (unless pop3-leave-mail-on-server
+         (pop3-send-streaming-command process "DELE" messages nil))))
+    (if pop3-leave-mail-on-server
+       (when (prog1 (pop3-uidl-dele process) (pop3-quit process))
+         (pop3-uidl-save))
+      (pop3-quit process)
+      ;; Remove UIDL data for the account that got not to leave mails.
+      (setq pop3-uidl-saved (pop3-uidl-load))
+      (let ((elt (assoc pop3-maildrop
+                       (cdr (assoc pop3-mailhost pop3-uidl-saved)))))
+       (when elt
+         (setcdr elt nil)
+         (pop3-uidl-save))))
+    t))
+
+(defun pop3-send-streaming-command (process command messages total-size)
+  (erase-buffer)
+  (let ((count (length messages))
+       (i 1)
+       (start-point (point-min))
+       (waited-for 0))
+    (while messages
+      (process-send-string process (format "%s %d\r\n" command (pop messages)))
+      ;; Only do 100 messages at a time to avoid pipe stalls.
+      (when (zerop (% i pop3-stream-length))
+       (setq start-point
+             (pop3-wait-for-messages process pop3-stream-length
+                                     total-size start-point))
+       (incf waited-for pop3-stream-length))
+      (incf i))
+    (pop3-wait-for-messages process (- count waited-for)
+                           total-size start-point)))
+
+(defun pop3-wait-for-messages (process count total-size start-point)
+  (while (> count 0)
+    (goto-char start-point)
+    (while (or (and (re-search-forward "^\\+OK" nil t)
+                   (or (not total-size)
+                       (re-search-forward "^\\.\r?\n" nil t)))
+              (re-search-forward "^-ERR " nil t))
+      (decf count)
+      (setq start-point (point)))
+    (unless (memq (process-status process) '(open run))
+      (error "pop3 process died"))
+    (when total-size
+      (let ((size 0))
+       (goto-char (point-min))
+       (while (re-search-forward "^\\+OK.*\n" nil t)
+         (setq size (+ size (- (point))
+                       (if (re-search-forward "^\\.\r?\n" nil 'move)
+                           (match-beginning 0)
+                         (point)))))
+       (message "pop3 retrieved %dKB (%d%%)"
+                (truncate (/ size 1000))
+                (truncate (* (/ (* size 1.0) total-size) 100)))))
+    (pop3-accept-process-output process))
+  start-point)
+
+(defun pop3-write-to-file (file messages)
+  (let ((pop-buffer (current-buffer))
+       (start (point-min))
+       beg end
+       temp-buffer)
+    (with-temp-buffer
+      (setq temp-buffer (current-buffer))
+      (with-current-buffer pop-buffer
+       (goto-char (point-min))
+       (while (re-search-forward "^\\+OK" nil t)
+         (forward-line 1)
+         (setq beg (point))
+         (when (re-search-forward "^\\.\r?\n" nil t)
+           (setq start (point))
+           (forward-line -1)
+           (setq end (point)))
+         (with-current-buffer temp-buffer
+           (goto-char (point-max))
+           (let ((hstart (point)))
+             (insert-buffer-substring pop-buffer beg end)
+             (pop3-clean-region hstart (point))
+             (goto-char (point-max))
+             (pop3-munge-message-separator hstart (point))
+             (when pop3-leave-mail-on-server
+               (pop3-uidl-add-xheader hstart (pop messages)))
+             (goto-char (point-max))))))
+      (let ((coding-system-for-write 'binary))
+       (goto-char (point-min))
+       ;; Check whether something inserted a newline at the start and
+       ;; delete it.
+       (when (eolp)
+         (delete-char 1))
+       (write-region (point-min) (point-max) file nil 'nomesg)))))
+
+(defun pop3-logon (process)
+  (let ((pop3-password pop3-password))
+    ;; for debugging only
+    (if pop3-debug (switch-to-buffer (process-buffer process)))
+    ;; query for password
+    (if (and pop3-password-required (not pop3-password))
+       (setq pop3-password
+             (read-passwd (format "Password for %s: " pop3-maildrop))))
+    (cond ((equal 'apop pop3-authentication-scheme)
+          (pop3-apop process pop3-maildrop))
+         ((equal 'pass pop3-authentication-scheme)
+          (pop3-user process pop3-maildrop)
+          (pop3-pass process))
+         (t (error "Invalid POP3 authentication scheme")))))
+
+(defun pop3-get-message-count ()
+  "Return the number of messages in the maildrop."
+  (let* ((process (pop3-open-server pop3-mailhost pop3-port))
+        message-count
+        (pop3-password pop3-password))
+    ;; for debugging only
+    (if pop3-debug (switch-to-buffer (process-buffer process)))
+    ;; query for password
+    (if (and pop3-password-required (not pop3-password))
+       (setq pop3-password
+             (read-passwd (format "Password for %s: " pop3-maildrop))))
+    (cond ((equal 'apop pop3-authentication-scheme)
+          (pop3-apop process pop3-maildrop))
+         ((equal 'pass pop3-authentication-scheme)
+          (pop3-user process pop3-maildrop)
+          (pop3-pass process))
+         (t (error "Invalid POP3 authentication scheme")))
+    (setq message-count (car (pop3-stat process)))
+    (pop3-quit process)
+    message-count))
+
+(defun pop3-uidl-stat (process)
+  "Return a list of unread message numbers and total size."
+  (pop3-send-command process "UIDL")
+  (let (err messages size)
+    (if (condition-case code
+           (progn
+             (pop3-read-response process)
+             t)
+         (error (setq err (error-message-string code))
+                nil))
+       (let ((start pop3-read-point)
+             saved list)
+         (with-current-buffer (process-buffer process)
+           (while (not (re-search-forward "^\\.\r\n" nil t))
+             (unless (memq (process-status process) '(open run))
+               (error "pop3 server closed the connection"))
+             (pop3-accept-process-output process)
+             (goto-char start))
+           (setq pop3-read-point (point-marker)
+                 pop3-uidl nil)
+           (while (progn (forward-line -1) (>= (point) start))
+             (when (looking-at "[0-9]+ \\([^\n\r ]+\\)")
+               (push (match-string 1) pop3-uidl)))
+           (when pop3-uidl
+             (setq pop3-uidl-saved (pop3-uidl-load)
+                   saved (cdr (assoc pop3-maildrop
+                                     (cdr (assoc pop3-mailhost
+                                                 pop3-uidl-saved)))))
+             (let ((i (length pop3-uidl)))
+               (while (> i 0)
+                 (unless (member (nth (1- i) pop3-uidl) saved)
+                   (push i messages))
+                 (decf i)))
+             (when messages
+               (setq list (pop3-list process)
+                     size 0)
+               (dolist (msg messages)
+                 (setq size (+ size (cdr (assq msg list)))))
+               (list messages size)))))
+      (message "%s doesn't support UIDL (%s), so we try a regressive way..."
+              pop3-mailhost err)
+      (sit-for 1)
+      (setq size (pop3-stat process))
+      (dotimes (i (car size)) (push (1+ i) messages))
+      (setcar size (nreverse messages))
+      size)))
+
+(defun pop3-uidl-dele (process)
+  "Delete messages according to `pop3-leave-mail-on-server'.
+Return non-nil if it is necessary to update the local UIDL file."
+  (let* ((ctime (current-time))
+        (srvr (assoc pop3-mailhost pop3-uidl-saved))
+        (saved (assoc pop3-maildrop (cdr srvr)))
+        i uidl mod new tstamp dele)
+    (setcdr (cdr ctime) nil)
+    ;; Add new messages to the data to be saved.
+    (cond ((and pop3-uidl saved)
+          (setq i (1- (length pop3-uidl)))
+          (while (>= i 0)
+            (unless (member (setq uidl (nth i pop3-uidl)) (cdr saved))
+              (push ctime new)
+              (push uidl new))
+            (decf i)))
+         (pop3-uidl
+          (setq new (apply 'nconc (mapcar (lambda (elt) (list elt ctime))
+                                          pop3-uidl)))))
+    (when new (setq mod t))
+    ;; List expirable messages and delete them from the data to be saved.
+    (setq ctime (when (numberp pop3-leave-mail-on-server)
+                 (/ (+ (* (car ctime) 65536.0) (cadr ctime)) 86400))
+         i (1- (length saved)))
+    (while (> i 0)
+      (if (member (setq uidl (nth (1- i) saved)) pop3-uidl)
+         (progn
+           (setq tstamp (nth i saved))
+           (if (and ctime
+                    (> (- ctime (/ (+ (* (car tstamp) 65536.0) (cadr tstamp))
+                                   86400))
+                       pop3-leave-mail-on-server))
+               ;; Mails to delete.
+               (progn
+                 (setq mod t)
+                 (push uidl dele))
+             ;; Mails to keep.
+             (push tstamp new)
+             (push uidl new)))
+       ;; Mails having been deleted in the server.
+       (setq mod t))
+      (decf i 2))
+    (cond (saved
+          (setcdr saved new))
+         (srvr
+          (setcdr (last srvr) (list (cons pop3-maildrop new))))
+         (t
+          (add-to-list 'pop3-uidl-saved
+                       (list pop3-mailhost (cons pop3-maildrop new))
+                       t)))
+    ;; Actually delete the messages in the server.
+    (when dele
+      (setq uidl nil
+           i (length pop3-uidl))
+      (while (> i 0)
+       (when (member (nth (1- i) pop3-uidl) dele)
+         (push i uidl))
+       (decf i))
+      (when uidl
+       (pop3-send-streaming-command process "DELE" uidl nil)))
+    mod))
+
+(defun pop3-uidl-load ()
+  "Load saved UIDL."
+  (when (file-exists-p pop3-uidl-file)
+    (with-temp-buffer
+      (condition-case code
+         (progn
+           (insert-file-contents pop3-uidl-file)
+           (goto-char (point-min))
+           (read (current-buffer)))
+       (error
+        (message "Error while loading %s (%s)"
+                 pop3-uidl-file (error-message-string code))
+        (sit-for 1)
+        nil)))))
+
+(defun pop3-uidl-save ()
+  "Save UIDL."
+  (with-temp-buffer
+    (if pop3-uidl-saved
+       (progn
+         (insert "(")
+         (dolist (srvr pop3-uidl-saved)
+           (when (cdr srvr)
+             (insert "(\"" (pop srvr) "\"\n  ")
+             (dolist (elt srvr)
+               (when (cdr elt)
+                 (insert "(\"" (pop elt) "\"\n   ")
+                 (while elt
+                   (insert (format "\"%s\" %s\n   " (pop elt) (pop elt))))
+                 (delete-char -4)
+                 (insert ")\n  ")))
+             (delete-char -3)
+             (if (eq (char-before) ?\))
+                 (insert ")\n ")
+               (goto-char (1+ (point-at-bol)))
+               (delete-region (point) (point-max)))))
+         (when (eq (char-before) ? )
+           (delete-char -2))
+         (insert ")\n"))
+      (insert "()\n"))
+    (let ((buffer-file-name pop3-uidl-file)
+         (delete-old-versions t)
+         (kept-new-versions kept-new-versions)
+         (kept-old-versions kept-old-versions)
+         (version-control version-control))
+      (if (consp pop3-uidl-file-backup)
+         (setq kept-new-versions (cadr pop3-uidl-file-backup)
+               kept-old-versions (car pop3-uidl-file-backup)
+               version-control t)
+       (setq version-control pop3-uidl-file-backup))
+      (save-buffer))))
+
+(defun pop3-uidl-add-xheader (start msgno)
+  "Add X-UIDL header."
+  (let ((case-fold-search t))
+    (save-restriction
+      (narrow-to-region start (progn
+                               (goto-char start)
+                               (search-forward "\n\n" nil 'move)
+                               (1- (point))))
+      (goto-char start)
+      (while (re-search-forward "^x-uidl:" nil t)
+       (while (progn
+                (forward-line 1)
+                (memq (char-after) '(?\t ? ))))
+       (delete-region (match-beginning 0) (point)))
+      (goto-char (point-max))
+      (insert "X-UIDL: " (nth (1- msgno) pop3-uidl) "\n"))))
+
+(defcustom pop3-stream-type nil
+  "*Transport security type for POP3 connections.
+This may be either nil (plain connection), `ssl' (use an
+SSL/TSL-secured stream) or `starttls' (use the starttls mechanism
+to turn on TLS security after opening the stream).  However, if
+this is nil, `ssl' is assumed for connections to port
+995 (pop3s)."
+  :version "23.1" ;; No Gnus
+  :group 'pop3
+  :type '(choice (const :tag "Plain" nil)
+                (const :tag "SSL/TLS" ssl)
+                (const starttls)))
+
+(defun pop3-open-server (mailhost port)
+  "Open TCP connection to MAILHOST on PORT.
+Returns the process associated with the connection."
+  (let ((coding-system-for-read 'binary)
+       (coding-system-for-write 'binary)
+       result)
+    (with-current-buffer
+        (get-buffer-create (concat " trace of POP session to "
+                                   mailhost))
+      (erase-buffer)
+      (setq pop3-read-point (point-min))
+      (setq result
+           (open-network-stream
+            "POP" (current-buffer) mailhost port
+            :type (cond
+                   ((or (eq pop3-stream-type 'ssl)
+                        (and (not pop3-stream-type)
+                             (member port '(995 "pop3s"))))
+                    'tls)
+                   (t
+                    (or pop3-stream-type 'network)))
+            :warn-unless-encrypted t
+            :capability-command "CAPA\r\n"
+            :end-of-command "^\\(-ERR\\|+OK\\).*\n"
+            :end-of-capability "^\\.\r?\n\\|^-ERR"
+            :success "^\\+OK.*\n"
+            :return-list t
+            :starttls-function
+            (lambda (capabilities)
+              (and (string-match "\\bSTLS\\b" capabilities)
+                   "STLS\r\n"))))
+      (when result
+       (let ((response (plist-get (cdr result) :greeting)))
+         (setq pop3-timestamp
+               (substring response (or (string-match "<" response) 0)
+                          (+ 1 (or (string-match ">" response) -1)))))
+       (set-process-query-on-exit-flag (car result) nil)
+       (erase-buffer)
+       (car result)))))
+
+;; Support functions
+
+(defun pop3-send-command (process command)
+  (set-buffer (process-buffer process))
+  (goto-char (point-max))
+  ;; (if (= (aref command 0) ?P)
+  ;;     (insert "PASS <omitted>\r\n")
+  ;;   (insert command "\r\n"))
+  (setq pop3-read-point (point))
+  (goto-char (point-max))
+  (process-send-string process (concat command "\r\n")))
+
+(defun pop3-read-response (process &optional return)
+  "Read the response from the server.
+Return the response string if optional second argument is non-nil."
+  (let ((case-fold-search nil)
+       match-end)
+    (with-current-buffer (process-buffer process)
+      (goto-char pop3-read-point)
+      (while (and (memq (process-status process) '(open run))
+                 (not (search-forward "\r\n" nil t)))
+       (pop3-accept-process-output process)
+       (goto-char pop3-read-point))
+      (setq match-end (point))
+      (goto-char pop3-read-point)
+      (if (looking-at "-ERR")
+         (error "%s" (buffer-substring (point) (- match-end 2)))
+       (if (not (looking-at "+OK"))
+           (progn (setq pop3-read-point match-end) nil)
+         (setq pop3-read-point match-end)
+         (if return
+             (buffer-substring (point) match-end)
+           t)
+         )))))
+
+(defun pop3-clean-region (start end)
+  (setq end (set-marker (make-marker) end))
+  (save-excursion
+    (goto-char start)
+    (while (and (< (point) end) (search-forward "\r\n" end t))
+      (replace-match "\n" t t))
+    (goto-char start)
+    (while (and (< (point) end) (re-search-forward "^\\." end t))
+      (replace-match "" t t)
+      (forward-char)))
+  (set-marker end nil))
+
+;; Copied from message-make-date.
+(defun pop3-make-date (&optional now)
+  "Make a valid date header.
+If NOW, use that time instead."
+  (require 'parse-time)
+  (let* ((now (or now (current-time)))
+        (zone (nth 8 (decode-time now)))
+        (sign "+"))
+    (when (< zone 0)
+      (setq sign "-")
+      (setq zone (- zone)))
+    (concat
+     (format-time-string "%d" now)
+     ;; The month name of the %b spec is locale-specific.  Pfff.
+     (format " %s "
+            (capitalize (car (rassoc (nth 4 (decode-time now))
+                                     parse-time-months))))
+     (format-time-string "%Y %H:%M:%S %z" now))))
+
+(defun pop3-munge-message-separator (start end)
+  "Check to see if a message separator exists.  If not, generate one."
+  (save-excursion
+    (save-restriction
+      (narrow-to-region start end)
+      (goto-char (point-min))
+      (if (not (or (looking-at "From .?") ; Unix mail
+                  (looking-at "\001\001\001\001\n") ; MMDF
+                  (looking-at "BABYL OPTIONS:") ; Babyl
+                  ))
+         (let* ((from (mail-strip-quoted-names (mail-fetch-field "From")))
+                (tdate (mail-fetch-field "Date"))
+                (date (split-string (or (and tdate
+                                             (not (string= "" tdate))
+                                             tdate)
+                                        (pop3-make-date))
+                                    " "))
+                (From_))
+           ;; sample date formats I have seen
+           ;; Date: Tue, 9 Jul 1996 09:04:21 -0400 (EDT)
+           ;; Date: 08 Jul 1996 23:22:24 -0400
+           ;; should be
+           ;; Tue Jul 9 09:04:21 1996
+
+           ;; Fixme: This should use timezone on the date field contents.
+           (setq date
+                 (cond ((not date)
+                        "Tue Jan 1 00:00:0 1900")
+                       ((string-match "[A-Z]" (nth 0 date))
+                        (format "%s %s %s %s %s"
+                                (nth 0 date) (nth 2 date) (nth 1 date)
+                                (nth 4 date) (nth 3 date)))
+                       (t
+                        ;; this really needs to be better but I don't feel
+                        ;; like writing a date to day converter.
+                        (format "Sun %s %s %s %s"
+                                (nth 1 date) (nth 0 date)
+                                (nth 3 date) (nth 2 date)))
+                       ))
+           (setq From_ (format "\nFrom %s  %s\n" from date))
+           (while (string-match "," From_)
+             (setq From_ (concat (substring From_ 0 (match-beginning 0))
+                                 (substring From_ (match-end 0)))))
+           (goto-char (point-min))
+           (insert From_)
+           (if (search-forward "\n\n" nil t)
+               nil
+             (goto-char (point-max))
+             (insert "\n"))
+           (let ((size (- (point-max) (point))))
+             (forward-line -1)
+             (insert (format "Content-Length: %s\n" size)))
+           )))))
+
+;; The Command Set
+
+;; AUTHORIZATION STATE
+
+(defun pop3-user (process user)
+  "Send USER information to POP3 server."
+  (pop3-send-command process (format "USER %s" user))
+  (let ((response (pop3-read-response process t)))
+    (if (not (and response (string-match "+OK" response)))
+       (error "USER %s not valid" user))))
+
+(defun pop3-pass (process)
+  "Send authentication information to the server."
+  (pop3-send-command process (format "PASS %s" pop3-password))
+  (let ((response (pop3-read-response process t)))
+    (if (not (and response (string-match "+OK" response)))
+       (pop3-quit process))))
+
+(defun pop3-apop (process user)
+  "Send alternate authentication information to the server."
+  (let ((pass pop3-password))
+    (if (and pop3-password-required (not pass))
+       (setq pass
+             (read-passwd (format "Password for %s: " pop3-maildrop))))
+    (if pass
+       (let ((hash (md5 (concat pop3-timestamp pass) nil nil 'binary)))
+         (pop3-send-command process (format "APOP %s %s" user hash))
+         (let ((response (pop3-read-response process t)))
+           (if (not (and response (string-match "+OK" response)))
+               (pop3-quit process)))))
+    ))
+
+;; TRANSACTION STATE
+
+(defun pop3-stat (process)
+  "Return the number of messages in the maildrop and the maildrop's size."
+  (pop3-send-command process "STAT")
+  (let ((response (pop3-read-response process t)))
+    (list (string-to-number (nth 1 (split-string response " ")))
+         (string-to-number (nth 2 (split-string response " "))))
+    ))
+
+(defun pop3-list (process &optional msg)
+  "If MSG is nil, return an alist of (MESSAGE-ID . SIZE) pairs.
+Otherwise, return the size of the message-id MSG"
+  (pop3-send-command process (if msg
+                                (format "LIST %d" msg)
+                              "LIST"))
+  (let ((response (pop3-read-response process t)))
+    (if msg
+       (string-to-number (nth 2 (split-string response " ")))
+      (let ((start pop3-read-point) end)
+       (with-current-buffer (process-buffer process)
+         (while (not (re-search-forward "^\\.\r\n" nil t))
+           (pop3-accept-process-output process)
+           (goto-char start))
+         (setq pop3-read-point (point-marker))
+         (goto-char (match-beginning 0))
+         (setq end (point-marker))
+         (mapcar #'(lambda (s) (let ((split (split-string s " ")))
+                                 (cons (string-to-number (nth 0 split))
+                                       (string-to-number (nth 1 split)))))
+                 (split-string (buffer-substring start end) "\r\n" t)))))))
+
+(defun pop3-retr (process msg crashbuf)
+  "Retrieve message-id MSG to buffer CRASHBUF."
+  (pop3-send-command process (format "RETR %s" msg))
+  (pop3-read-response process)
+  (let ((start pop3-read-point) end)
+    (with-current-buffer (process-buffer process)
+      (while (not (re-search-forward "^\\.\r\n" nil t))
+       (unless (memq (process-status process) '(open run))
+         (error "pop3 server closed the connection"))
+       (pop3-accept-process-output process)
+       (goto-char start))
+      (setq pop3-read-point (point-marker))
+      ;; this code does not seem to work for some POP servers...
+      ;; and I cannot figure out why not.
+      ;;      (goto-char (match-beginning 0))
+      ;;      (backward-char 2)
+      ;;      (if (not (looking-at "\r\n"))
+      ;;         (insert "\r\n"))
+      ;;      (re-search-forward "\\.\r\n")
+      (goto-char (match-beginning 0))
+      (setq end (point-marker))
+      (pop3-clean-region start end)
+      (pop3-munge-message-separator start end)
+      (with-current-buffer crashbuf
+       (erase-buffer))
+      (copy-to-buffer crashbuf start end)
+      (delete-region start end)
+      )))
+
+(defun pop3-dele (process msg)
+  "Mark message-id MSG as deleted."
+  (pop3-send-command process (format "DELE %s" msg))
+  (pop3-read-response process))
+
+(defun pop3-noop (process msg)
+  "No-operation."
+  (pop3-send-command process "NOOP")
+  (pop3-read-response process))
+
+(defun pop3-last (process)
+  "Return highest accessed message-id number for the session."
+  (pop3-send-command process "LAST")
+  (let ((response (pop3-read-response process t)))
+    (string-to-number (nth 1 (split-string response " ")))
+    ))
+
+(defun pop3-rset (process)
+  "Remove all delete marks from current maildrop."
+  (pop3-send-command process "RSET")
+  (pop3-read-response process))
+
+;; UPDATE
+
+(defun pop3-quit (process)
+  "Close connection to POP3 server.
+Tell server to remove all messages marked as deleted, unlock the maildrop,
+and close the connection."
+  (pop3-send-command process "QUIT")
+  (pop3-read-response process t)
+  (if process
+      (with-current-buffer (process-buffer process)
+       (goto-char (point-max))
+       (delete-process process))))
+\f
+;; Summary of POP3 (Post Office Protocol version 3) commands and responses
+
+;;; AUTHORIZATION STATE
+
+;; Initial TCP connection
+;; Arguments: none
+;; Restrictions: none
+;; Possible responses:
+;;  +OK [POP3 server ready]
+
+;; USER name
+;; Arguments: a server specific user-id (required)
+;; Restrictions: authorization state [after unsuccessful USER or PASS
+;; Possible responses:
+;;  +OK [valid user-id]
+;;  -ERR [invalid user-id]
+
+;; PASS string
+;; Arguments: a server/user-id specific password (required)
+;; Restrictions: authorization state, after successful USER
+;; Possible responses:
+;;  +OK [maildrop locked and ready]
+;;  -ERR [invalid password]
+;;  -ERR [unable to lock maildrop]
+
+;; STLS      (RFC 2595)
+;; Arguments: none
+;; Restrictions: Only permitted in AUTHORIZATION state.
+;; Possible responses:
+;;  +OK
+;;  -ERR
+
+;;; TRANSACTION STATE
+
+;; STAT
+;; Arguments: none
+;; Restrictions: transaction state
+;; Possible responses:
+;;  +OK nn mm [# of messages, size of maildrop]
+
+;; LIST [msg]
+;; Arguments: a message-id (optional)
+;; Restrictions: transaction state; msg must not be deleted
+;; Possible responses:
+;;  +OK [scan listing follows]
+;;  -ERR [no such message]
+
+;; RETR msg
+;; Arguments: a message-id (required)
+;; Restrictions: transaction state; msg must not be deleted
+;; Possible responses:
+;;  +OK [message contents follow]
+;;  -ERR [no such message]
+
+;; DELE msg
+;; Arguments: a message-id (required)
+;; Restrictions: transaction state; msg must not be deleted
+;; Possible responses:
+;;  +OK [message deleted]
+;;  -ERR [no such message]
+
+;; NOOP
+;; Arguments: none
+;; Restrictions: transaction state
+;; Possible responses:
+;;  +OK
+
+;; LAST
+;; Arguments: none
+;; Restrictions: transaction state
+;; Possible responses:
+;;  +OK nn [highest numbered message accessed]
+
+;; RSET
+;; Arguments: none
+;; Restrictions: transaction state
+;; Possible responses:
+;;  +OK [all delete marks removed]
+
+;; UIDL [msg]
+;; Arguments: a message-id (optional)
+;; Restrictions: transaction state; msg must not be deleted
+;; Possible responses:
+;;  +OK [uidl listing follows]
+;;  -ERR [no such message]
+
+;;; UPDATE STATE
+
+;; QUIT
+;; Arguments: none
+;; Restrictions: none
+;; Possible responses:
+;;  +OK [TCP connection closed]
+
+(provide 'pop3)
+
+;;; pop3.el ends here
diff --git a/lisp/net/sieve-manage.el b/lisp/net/sieve-manage.el
new file mode 100644 (file)
index 0000000..695bbd8
--- /dev/null
@@ -0,0 +1,575 @@
+;;; sieve-manage.el --- Implementation of the managesieve protocol in elisp
+
+;; Copyright (C) 2001-2016 Free Software Foundation, Inc.
+
+;; Author: Simon Josefsson <simon@josefsson.org>
+;;         Albert Krewinkel <tarleb@moltkeplatz.de>
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This library provides an elisp API for the managesieve network
+;; protocol.
+;;
+;; It uses the SASL library for authentication, which means it
+;; supports DIGEST-MD5, CRAM-MD5, SCRAM-MD5, NTLM, PLAIN and LOGIN
+;; methods.  STARTTLS is not well tested, but should be easy to get to
+;; work if someone wants.
+;;
+;; The API should be fairly obvious for anyone familiar with the
+;; managesieve protocol, interface functions include:
+;;
+;; `sieve-manage-open'
+;; open connection to managesieve server, returning a buffer to be
+;; used by all other API functions.
+;;
+;; `sieve-manage-opened'
+;; check if a server is open or not
+;;
+;; `sieve-manage-close'
+;; close a server connection.
+;;
+;; `sieve-manage-listscripts'
+;; `sieve-manage-deletescript'
+;; `sieve-manage-getscript'
+;; performs managesieve protocol actions
+;;
+;; and that's it.  Example of a managesieve session in *scratch*:
+;;
+;; (with-current-buffer (sieve-manage-open "mail.example.com")
+;;   (sieve-manage-authenticate)
+;;   (sieve-manage-listscripts))
+;;
+;; => ((active . "main") "vacation")
+;;
+;; References:
+;;
+;; draft-martin-managesieve-02.txt,
+;; "A Protocol for Remotely Managing Sieve Scripts",
+;; by Tim Martin.
+;;
+;; Release history:
+;;
+;; 2001-10-31 Committed to Oort Gnus.
+;; 2002-07-27 Added DELETESCRIPT.  Suggested by Ned Ludd.
+;; 2002-08-03 Use SASL library.
+;; 2013-06-05 Enabled STARTTLS support, fixed bit rot.
+
+;;; Code:
+
+(if (locate-library "password-cache")
+    (require 'password-cache)
+  (require 'password))
+
+(eval-when-compile (require 'cl))
+(require 'sasl)
+(require 'starttls)
+(autoload 'sasl-find-mechanism "sasl")
+(autoload 'auth-source-search "auth-source")
+
+;; User customizable variables:
+
+(defgroup sieve-manage nil
+  "Low-level Managesieve protocol issues."
+  :group 'mail
+  :prefix "sieve-")
+
+(defcustom sieve-manage-log "*sieve-manage-log*"
+  "Name of buffer for managesieve session trace."
+  :type 'string
+  :group 'sieve-manage)
+
+(defcustom sieve-manage-server-eol "\r\n"
+  "The EOL string sent from the server."
+  :type 'string
+  :group 'sieve-manage)
+
+(defcustom sieve-manage-client-eol "\r\n"
+  "The EOL string we send to the server."
+  :type 'string
+  :group 'sieve-manage)
+
+(defcustom sieve-manage-authenticators '(digest-md5
+                                        cram-md5
+                                        scram-md5
+                                        ntlm
+                                        plain
+                                        login)
+  "Priority of authenticators to consider when authenticating to server."
+  ;; FIXME Improve this.  It's not `set'.
+  ;; It's like (repeat (choice (const ...))), where each choice can
+  ;; only appear once.
+  :type '(repeat symbol)
+  :group 'sieve-manage)
+
+(defcustom sieve-manage-authenticator-alist
+  '((cram-md5   sieve-manage-cram-md5-p       sieve-manage-cram-md5-auth)
+    (digest-md5 sieve-manage-digest-md5-p     sieve-manage-digest-md5-auth)
+    (scram-md5  sieve-manage-scram-md5-p      sieve-manage-scram-md5-auth)
+    (ntlm       sieve-manage-ntlm-p           sieve-manage-ntlm-auth)
+    (plain      sieve-manage-plain-p          sieve-manage-plain-auth)
+    (login      sieve-manage-login-p          sieve-manage-login-auth))
+  "Definition of authenticators.
+
+\(NAME CHECK AUTHENTICATE)
+
+NAME names the authenticator.  CHECK is a function returning non-nil if
+the server support the authenticator and AUTHENTICATE is a function
+for doing the actual authentication."
+  :type '(repeat (list (symbol :tag "Name") (function :tag "Check function")
+                      (function :tag "Authentication function")))
+  :group 'sieve-manage)
+
+(defcustom sieve-manage-default-port "sieve"
+  "Default port number or service name for managesieve protocol."
+  :type '(choice integer string)
+  :version "24.4"
+  :group 'sieve-manage)
+
+(defcustom sieve-manage-default-stream 'network
+  "Default stream type to use for `sieve-manage'."
+  :version "24.1"
+  :type 'symbol
+  :group 'sieve-manage)
+
+;; Internal variables:
+
+(defconst sieve-manage-local-variables '(sieve-manage-server
+                                        sieve-manage-port
+                                        sieve-manage-auth
+                                        sieve-manage-stream
+                                        sieve-manage-process
+                                        sieve-manage-client-eol
+                                        sieve-manage-server-eol
+                                        sieve-manage-capability))
+(defconst sieve-manage-coding-system-for-read 'binary)
+(defconst sieve-manage-coding-system-for-write 'binary)
+(defvar sieve-manage-stream nil)
+(defvar sieve-manage-auth nil)
+(defvar sieve-manage-server nil)
+(defvar sieve-manage-port nil)
+(defvar sieve-manage-state 'closed
+  "Managesieve state.
+Valid states are `closed', `initial', `nonauth', and `auth'.")
+(defvar sieve-manage-process nil)
+(defvar sieve-manage-capability nil)
+
+;; Internal utility functions
+(autoload 'mm-enable-multibyte "mm-util")
+
+(defun sieve-manage-make-process-buffer ()
+  (with-current-buffer
+      (generate-new-buffer (format " *sieve %s:%s*"
+                                   sieve-manage-server
+                                   sieve-manage-port))
+    (mapc 'make-local-variable sieve-manage-local-variables)
+    (mm-enable-multibyte)
+    (buffer-disable-undo)
+    (current-buffer)))
+
+(defun sieve-manage-erase (&optional p buffer)
+  (let ((buffer (or buffer (current-buffer))))
+    (and sieve-manage-log
+        (with-current-buffer (get-buffer-create sieve-manage-log)
+          (mm-enable-multibyte)
+          (buffer-disable-undo)
+          (goto-char (point-max))
+          (insert-buffer-substring buffer (with-current-buffer buffer
+                                            (point-min))
+                                   (or p (with-current-buffer buffer
+                                           (point-max)))))))
+  (delete-region (point-min) (or p (point-max))))
+
+(defun sieve-manage-open-server (server port &optional stream buffer)
+  "Open network connection to SERVER on PORT.
+Return the buffer associated with the connection."
+  (with-current-buffer buffer
+    (sieve-manage-erase)
+    (setq sieve-manage-state 'initial)
+    (destructuring-bind (proc . props)
+        (open-network-stream
+         "SIEVE" buffer server port
+         :type stream
+         :capability-command "CAPABILITY\r\n"
+         :end-of-command "^\\(OK\\|NO\\).*\n"
+         :success "^OK.*\n"
+         :return-list t
+         :starttls-function
+         (lambda (capabilities)
+           (when (string-match "\\bSTARTTLS\\b" capabilities)
+             "STARTTLS\r\n")))
+      (setq sieve-manage-process proc)
+      (setq sieve-manage-capability
+            (sieve-manage-parse-capability (plist-get props :capabilities)))
+      ;; Ignore new capabilities issues after successful STARTTLS
+      (when (and (memq stream '(nil network starttls))
+                 (eq (plist-get props :type) 'tls))
+        (sieve-manage-drop-next-answer))
+      (current-buffer))))
+
+;; Authenticators
+(defun sieve-sasl-auth (buffer mech)
+  "Login to server using the SASL MECH method."
+  (message "sieve: Authenticating using %s..." mech)
+  (with-current-buffer buffer
+    (let* ((auth-info (auth-source-search :host sieve-manage-server
+                                          :port "sieve"
+                                          :max 1
+                                          :create t))
+           (user-name (or (plist-get (nth 0 auth-info) :user) ""))
+           (user-password (or (plist-get (nth 0 auth-info) :secret) ""))
+           (user-password (if (functionp user-password)
+                              (funcall user-password)
+                            user-password))
+           (client (sasl-make-client (sasl-find-mechanism (list mech))
+                                     user-name "sieve" sieve-manage-server))
+           (sasl-read-passphrase
+            ;; We *need* to copy the password, because sasl will modify it
+            ;; somehow.
+            `(lambda (prompt) ,(copy-sequence user-password)))
+           (step (sasl-next-step client nil))
+           (tag (sieve-manage-send
+                 (concat
+                  "AUTHENTICATE \""
+                  mech
+                  "\""
+                  (and (sasl-step-data step)
+                       (concat
+                        " \""
+                        (base64-encode-string
+                         (sasl-step-data step)
+                         'no-line-break)
+                        "\"")))))
+           data rsp)
+      (catch 'done
+        (while t
+          (setq rsp nil)
+          (goto-char (point-min))
+          (while (null (or (progn
+                             (setq rsp (sieve-manage-is-string))
+                             (if (not (and rsp (looking-at
+                                                sieve-manage-server-eol)))
+                                 (setq rsp nil)
+                               (goto-char (match-end 0))
+                               rsp))
+                           (setq rsp (sieve-manage-is-okno))))
+            (accept-process-output sieve-manage-process 1)
+            (goto-char (point-min)))
+          (sieve-manage-erase)
+          (when (sieve-manage-ok-p rsp)
+            (when (and (cadr rsp)
+                       (string-match "^SASL \"\\([^\"]+\\)\"" (cadr rsp)))
+              (sasl-step-set-data
+               step (base64-decode-string (match-string 1 (cadr rsp)))))
+            (if (and (setq step (sasl-next-step client step))
+                     (setq data (sasl-step-data step)))
+                ;; We got data for server but it's finished
+                (error "Server not ready for SASL data: %s" data)
+              ;; The authentication process is finished.
+              (throw 'done t)))
+          (unless (stringp rsp)
+            (error "Server aborted SASL authentication: %s" (caddr rsp)))
+          (sasl-step-set-data step (base64-decode-string rsp))
+          (setq step (sasl-next-step client step))
+          (sieve-manage-send
+           (if (sasl-step-data step)
+               (concat "\""
+                       (base64-encode-string (sasl-step-data step)
+                                             'no-line-break)
+                       "\"")
+             ""))))
+      (message "sieve: Login using %s...done" mech))))
+
+(defun sieve-manage-cram-md5-p (buffer)
+  (sieve-manage-capability "SASL" "CRAM-MD5" buffer))
+
+(defun sieve-manage-cram-md5-auth (buffer)
+  "Login to managesieve server using the CRAM-MD5 SASL method."
+  (sieve-sasl-auth buffer "CRAM-MD5"))
+
+(defun sieve-manage-digest-md5-p (buffer)
+  (sieve-manage-capability "SASL" "DIGEST-MD5" buffer))
+
+(defun sieve-manage-digest-md5-auth (buffer)
+  "Login to managesieve server using the DIGEST-MD5 SASL method."
+  (sieve-sasl-auth buffer "DIGEST-MD5"))
+
+(defun sieve-manage-scram-md5-p (buffer)
+  (sieve-manage-capability "SASL" "SCRAM-MD5" buffer))
+
+(defun sieve-manage-scram-md5-auth (buffer)
+  "Login to managesieve server using the SCRAM-MD5 SASL method."
+  (sieve-sasl-auth buffer "SCRAM-MD5"))
+
+(defun sieve-manage-ntlm-p (buffer)
+  (sieve-manage-capability "SASL" "NTLM" buffer))
+
+(defun sieve-manage-ntlm-auth (buffer)
+  "Login to managesieve server using the NTLM SASL method."
+  (sieve-sasl-auth buffer "NTLM"))
+
+(defun sieve-manage-plain-p (buffer)
+  (sieve-manage-capability "SASL" "PLAIN" buffer))
+
+(defun sieve-manage-plain-auth (buffer)
+  "Login to managesieve server using the PLAIN SASL method."
+  (sieve-sasl-auth buffer "PLAIN"))
+
+(defun sieve-manage-login-p (buffer)
+  (sieve-manage-capability "SASL" "LOGIN" buffer))
+
+(defun sieve-manage-login-auth (buffer)
+  "Login to managesieve server using the LOGIN SASL method."
+  (sieve-sasl-auth buffer "LOGIN"))
+
+;; Managesieve API
+
+(defun sieve-manage-open (server &optional port stream auth buffer)
+  "Open a network connection to a managesieve SERVER (string).
+Optional argument PORT is port number (integer) on remote server.
+Optional argument STREAM is any of `sieve-manage-streams' (a symbol).
+Optional argument AUTH indicates authenticator to use, see
+`sieve-manage-authenticators' for available authenticators.
+If nil, chooses the best stream the server is capable of.
+Optional argument BUFFER is buffer (buffer, or string naming buffer)
+to work in."
+  (setq sieve-manage-port (or port sieve-manage-default-port))
+  (with-current-buffer (or buffer (sieve-manage-make-process-buffer))
+    (setq sieve-manage-server (or server
+                                  sieve-manage-server)
+          sieve-manage-stream (or stream
+                                  sieve-manage-stream
+                                  sieve-manage-default-stream)
+          sieve-manage-auth   (or auth
+                                  sieve-manage-auth))
+    (message "sieve: Connecting to %s..." sieve-manage-server)
+    (sieve-manage-open-server sieve-manage-server
+                              sieve-manage-port
+                              sieve-manage-stream
+                              (current-buffer))
+    (when (sieve-manage-opened (current-buffer))
+      ;; Choose authenticator
+      (when (and (null sieve-manage-auth)
+                 (not (eq sieve-manage-state 'auth)))
+        (dolist (auth sieve-manage-authenticators)
+          (when (funcall (nth 1 (assq auth sieve-manage-authenticator-alist))
+                       buffer)
+            (setq sieve-manage-auth auth)
+            (return)))
+        (unless sieve-manage-auth
+          (error "Couldn't figure out authenticator for server")))
+      (sieve-manage-erase)
+      (current-buffer))))
+
+(defun sieve-manage-authenticate (&optional buffer)
+  "Authenticate on server in BUFFER.
+Return `sieve-manage-state' value."
+  (with-current-buffer (or buffer (current-buffer))
+    (if (eq sieve-manage-state 'nonauth)
+        (when (funcall (nth 2 (assq sieve-manage-auth
+                                    sieve-manage-authenticator-alist))
+                       (current-buffer))
+          (setq sieve-manage-state 'auth))
+      sieve-manage-state)))
+
+(defun sieve-manage-opened (&optional buffer)
+  "Return non-nil if connection to managesieve server in BUFFER is open.
+If BUFFER is nil then the current buffer is used."
+  (and (setq buffer (get-buffer (or buffer (current-buffer))))
+       (buffer-live-p buffer)
+       (with-current-buffer buffer
+        (and sieve-manage-process
+             (memq (process-status sieve-manage-process) '(open run))))))
+
+(defun sieve-manage-close (&optional buffer)
+  "Close connection to managesieve server in BUFFER.
+If BUFFER is nil, the current buffer is used."
+  (with-current-buffer (or buffer (current-buffer))
+    (when (sieve-manage-opened)
+      (sieve-manage-send "LOGOUT")
+      (sit-for 1))
+    (when (and sieve-manage-process
+              (memq (process-status sieve-manage-process) '(open run)))
+      (delete-process sieve-manage-process))
+    (setq sieve-manage-process nil)
+    (sieve-manage-erase)
+    t))
+
+(defun sieve-manage-capability (&optional name value buffer)
+  "Check if capability NAME of server BUFFER match VALUE.
+If it does, return the server value of NAME. If not returns nil.
+If VALUE is nil, do not check VALUE and return server value.
+If NAME is nil, return the full server list of capabilities."
+  (with-current-buffer (or buffer (current-buffer))
+    (if (null name)
+       sieve-manage-capability
+      (let ((server-value (cadr (assoc name sieve-manage-capability))))
+        (when (or (null value)
+                  (and server-value
+                       (string-match value server-value)))
+          server-value)))))
+
+(defun sieve-manage-listscripts (&optional buffer)
+  (with-current-buffer (or buffer (current-buffer))
+    (sieve-manage-send "LISTSCRIPTS")
+    (sieve-manage-parse-listscripts)))
+
+(defun sieve-manage-havespace (name size &optional buffer)
+  (with-current-buffer (or buffer (current-buffer))
+    (sieve-manage-send (format "HAVESPACE \"%s\" %s" name size))
+    (sieve-manage-parse-okno)))
+
+(defun sieve-manage-putscript (name content &optional buffer)
+  (with-current-buffer (or buffer (current-buffer))
+    (sieve-manage-send (format "PUTSCRIPT \"%s\" {%d+}%s%s" name
+                               ;; Here we assume that the coding-system will
+                               ;; replace each char with a single byte.
+                               ;; This is always the case if `content' is
+                               ;; a unibyte string.
+                              (length content)
+                              sieve-manage-client-eol content))
+    (sieve-manage-parse-okno)))
+
+(defun sieve-manage-deletescript (name &optional buffer)
+  (with-current-buffer (or buffer (current-buffer))
+    (sieve-manage-send (format "DELETESCRIPT \"%s\"" name))
+    (sieve-manage-parse-okno)))
+
+(defun sieve-manage-getscript (name output-buffer &optional buffer)
+  (with-current-buffer (or buffer (current-buffer))
+    (sieve-manage-send (format "GETSCRIPT \"%s\"" name))
+    (let ((script (sieve-manage-parse-string)))
+      (sieve-manage-parse-crlf)
+      (with-current-buffer output-buffer
+       (insert script))
+      (sieve-manage-parse-okno))))
+
+(defun sieve-manage-setactive (name &optional buffer)
+  (with-current-buffer (or buffer (current-buffer))
+    (sieve-manage-send (format "SETACTIVE \"%s\"" name))
+    (sieve-manage-parse-okno)))
+
+;; Protocol parsing routines
+
+(defun sieve-manage-wait-for-answer ()
+  (let ((pattern "^\\(OK\\|NO\\).*\n")
+        pos)
+    (while (not pos)
+      (setq pos (search-forward-regexp pattern nil t))
+      (goto-char (point-min))
+      (sleep-for 0 50))
+    pos))
+
+(defun sieve-manage-drop-next-answer ()
+  (sieve-manage-wait-for-answer)
+  (sieve-manage-erase))
+
+(defun sieve-manage-ok-p (rsp)
+  (string= (downcase (or (car-safe rsp) "")) "ok"))
+
+(defun sieve-manage-is-okno ()
+  (when (looking-at (concat
+                    "^\\(OK\\|NO\\)\\( (\\([^)]+\\))\\)?\\( \\(.*\\)\\)?"
+                    sieve-manage-server-eol))
+    (let ((status (match-string 1))
+         (resp-code (match-string 3))
+         (response (match-string 5)))
+      (when response
+       (goto-char (match-beginning 5))
+       (setq response (sieve-manage-is-string)))
+      (list status resp-code response))))
+
+(defun sieve-manage-parse-okno ()
+  (let (rsp)
+    (while (null rsp)
+      (accept-process-output (get-buffer-process (current-buffer)) 1)
+      (goto-char (point-min))
+      (setq rsp (sieve-manage-is-okno)))
+    (sieve-manage-erase)
+    rsp))
+
+(defun sieve-manage-parse-capability (str)
+  "Parse managesieve capability string `STR'.
+Set variable `sieve-manage-capability' to "
+  (let ((capas (delq nil
+                     (mapcar #'split-string-and-unquote
+                             (split-string str "\n")))))
+    (when (string= "OK" (caar (last capas)))
+      (setq sieve-manage-state 'nonauth))
+    capas))
+
+(defun sieve-manage-is-string ()
+  (cond ((looking-at "\"\\([^\"]+\\)\"")
+        (prog1
+            (match-string 1)
+          (goto-char (match-end 0))))
+       ((looking-at (concat "{\\([0-9]+\\+?\\)}" sieve-manage-server-eol))
+        (let ((pos (match-end 0))
+              (len (string-to-number (match-string 1))))
+          (if (< (point-max) (+ pos len))
+              nil
+            (goto-char (+ pos len))
+            (buffer-substring pos (+ pos len)))))))
+
+(defun sieve-manage-parse-string ()
+  (let (rsp)
+    (while (null rsp)
+      (accept-process-output (get-buffer-process (current-buffer)) 1)
+      (goto-char (point-min))
+      (setq rsp (sieve-manage-is-string)))
+    (sieve-manage-erase (point))
+    rsp))
+
+(defun sieve-manage-parse-crlf ()
+  (when (looking-at sieve-manage-server-eol)
+    (sieve-manage-erase (match-end 0))))
+
+(defun sieve-manage-parse-listscripts ()
+  (let (tmp rsp data)
+    (while (null rsp)
+      (while (null (or (setq rsp (sieve-manage-is-okno))
+                      (setq tmp (sieve-manage-is-string))))
+       (accept-process-output (get-buffer-process (current-buffer)) 1)
+       (goto-char (point-min)))
+      (when tmp
+       (while (not (looking-at (concat "\\( ACTIVE\\)?"
+                                       sieve-manage-server-eol)))
+         (accept-process-output (get-buffer-process (current-buffer)) 1)
+         (goto-char (point-min)))
+       (if (match-string 1)
+           (push (cons 'active tmp) data)
+         (push tmp data))
+       (goto-char (match-end 0))
+       (setq tmp nil)))
+    (sieve-manage-erase)
+    (if (sieve-manage-ok-p rsp)
+       data
+      rsp)))
+
+(defun sieve-manage-send (cmdstr)
+  (setq cmdstr (concat cmdstr sieve-manage-client-eol))
+  (and sieve-manage-log
+       (with-current-buffer (get-buffer-create sieve-manage-log)
+        (mm-enable-multibyte)
+        (buffer-disable-undo)
+        (goto-char (point-max))
+        (insert cmdstr)))
+  (process-send-string sieve-manage-process cmdstr))
+
+(provide 'sieve-manage)
+
+;; sieve-manage.el ends here
diff --git a/lisp/net/sieve-mode.el b/lisp/net/sieve-mode.el
new file mode 100644 (file)
index 0000000..7575ba6
--- /dev/null
@@ -0,0 +1,221 @@
+;;; sieve-mode.el --- Sieve code editing commands for Emacs
+
+;; Copyright (C) 2001-2016 Free Software Foundation, Inc.
+
+;; Author: Simon Josefsson <simon@josefsson.org>
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file contain editing mode functions and font-lock support for
+;; editing Sieve scripts.  It sets up C-mode with support for
+;; sieve-style #-comments and a lightly hacked syntax table.  It was
+;; strongly influenced by awk-mode.el.
+;;
+;; Put something similar to the following in your .emacs to use this file:
+;;
+;; (load "~/lisp/sieve")
+;; (setq auto-mode-alist (cons '("\\.siv\\'" . sieve-mode) auto-mode-alist))
+;;
+;; References:
+;;
+;; RFC 3028,
+;; "Sieve: A Mail Filtering Language",
+;; by Tim Showalter.
+;;
+;; Release history:
+;;
+;; 2001-03-02 version 1.0 posted to gnu.emacs.sources
+;;            version 1.1 change file extension into ".siv" (official one)
+;;                        added keymap and menubar to hook into sieve-manage
+;; 2001-10-31 version 1.2 committed to Oort Gnus
+
+;;; Code:
+
+(autoload 'sieve-manage "sieve")
+(autoload 'sieve-upload "sieve")
+(eval-when-compile
+  (require 'font-lock))
+
+(defgroup sieve nil
+  "Sieve."
+  :group 'languages)
+
+(defcustom sieve-mode-hook nil
+  "Hook run in sieve mode buffers."
+  :group 'sieve
+  :type 'hook)
+
+;; Font-lock
+
+(defvar sieve-control-commands-face 'sieve-control-commands
+  "Face name used for Sieve Control Commands.")
+
+(defface sieve-control-commands
+  '((((type tty) (class color)) (:foreground "blue" :weight light))
+    (((class grayscale) (background light)) (:foreground "LightGray" :bold t))
+    (((class grayscale) (background dark)) (:foreground "DimGray" :bold t))
+    (((class color) (background light)) (:foreground "Orchid"))
+    (((class color) (background dark)) (:foreground "LightSteelBlue"))
+    (t (:bold t)))
+  "Face used for Sieve Control Commands."
+  :group 'sieve)
+;; backward-compatibility alias
+(put 'sieve-control-commands-face 'face-alias 'sieve-control-commands)
+(put 'sieve-control-commands-face 'obsolete-face "22.1")
+
+(defvar sieve-action-commands-face 'sieve-action-commands
+  "Face name used for Sieve Action Commands.")
+
+(defface sieve-action-commands
+  '((((type tty) (class color)) (:foreground "blue" :weight bold))
+    (((class color) (background light)) (:foreground "Blue"))
+    (((class color) (background dark)) (:foreground "LightSkyBlue"))
+    (t (:inverse-video t :bold t)))
+  "Face used for Sieve Action Commands."
+  :group 'sieve)
+;; backward-compatibility alias
+(put 'sieve-action-commands-face 'face-alias 'sieve-action-commands)
+(put 'sieve-action-commands-face 'obsolete-face "22.1")
+
+(defvar sieve-test-commands-face 'sieve-test-commands
+  "Face name used for Sieve Test Commands.")
+
+(defface sieve-test-commands
+  '((((type tty) (class color)) (:foreground "magenta"))
+    (((class grayscale) (background light))
+     (:foreground "LightGray" :bold t :underline t))
+    (((class grayscale) (background dark))
+     (:foreground "Gray50" :bold t :underline t))
+    (((class color) (background light)) (:foreground "CadetBlue"))
+    (((class color) (background dark)) (:foreground "Aquamarine"))
+    (t (:bold t :underline t)))
+  "Face used for Sieve Test Commands."
+  :group 'sieve)
+;; backward-compatibility alias
+(put 'sieve-test-commands-face 'face-alias 'sieve-test-commands)
+(put 'sieve-test-commands-face 'obsolete-face "22.1")
+
+(defvar sieve-tagged-arguments-face 'sieve-tagged-arguments
+  "Face name used for Sieve Tagged Arguments.")
+
+(defface sieve-tagged-arguments
+  '((((type tty) (class color)) (:foreground "cyan" :weight bold))
+    (((class grayscale) (background light)) (:foreground "LightGray" :bold t))
+    (((class grayscale) (background dark)) (:foreground "DimGray" :bold t))
+    (((class color) (background light)) (:foreground "Purple"))
+    (((class color) (background dark)) (:foreground "Cyan"))
+    (t (:bold t)))
+  "Face used for Sieve Tagged Arguments."
+  :group 'sieve)
+;; backward-compatibility alias
+(put 'sieve-tagged-arguments-face 'face-alias 'sieve-tagged-arguments)
+(put 'sieve-tagged-arguments-face 'obsolete-face "22.1")
+
+
+(defconst sieve-font-lock-keywords
+  (eval-when-compile
+    (list
+     ;; control commands
+     (cons (regexp-opt '("require" "if" "else" "elsif" "stop")
+                       'words)
+          'sieve-control-commands-face)
+     ;; action commands
+     (cons (regexp-opt '("fileinto" "redirect" "reject" "keep" "discard")
+                       'words)
+          'sieve-action-commands-face)
+     ;; test commands
+     (cons (regexp-opt '("address" "allof" "anyof" "exists" "false"
+                        "true" "header" "not" "size" "envelope"
+                         "body")
+                       'words)
+          'sieve-test-commands-face)
+     (cons "\\Sw+:\\sw+"
+          'sieve-tagged-arguments-face))))
+
+;; Syntax table
+
+(defvar sieve-mode-syntax-table nil
+  "Syntax table in use in sieve-mode buffers.")
+
+(if sieve-mode-syntax-table
+    ()
+  (setq sieve-mode-syntax-table (make-syntax-table))
+  (modify-syntax-entry ?\\ "\\" sieve-mode-syntax-table)
+  (modify-syntax-entry ?\n ">   " sieve-mode-syntax-table)
+  (modify-syntax-entry ?\f ">   " sieve-mode-syntax-table)
+  (modify-syntax-entry ?\# "<   " sieve-mode-syntax-table)
+  (modify-syntax-entry ?/ "." sieve-mode-syntax-table)
+  (modify-syntax-entry ?* "." sieve-mode-syntax-table)
+  (modify-syntax-entry ?+ "." sieve-mode-syntax-table)
+  (modify-syntax-entry ?- "." sieve-mode-syntax-table)
+  (modify-syntax-entry ?= "." sieve-mode-syntax-table)
+  (modify-syntax-entry ?% "." sieve-mode-syntax-table)
+  (modify-syntax-entry ?< "." sieve-mode-syntax-table)
+  (modify-syntax-entry ?> "." sieve-mode-syntax-table)
+  (modify-syntax-entry ?& "." sieve-mode-syntax-table)
+  (modify-syntax-entry ?| "." sieve-mode-syntax-table)
+  (modify-syntax-entry ?_ "_" sieve-mode-syntax-table)
+  (modify-syntax-entry ?\' "\"" sieve-mode-syntax-table))
+
+;; Key map definition
+
+(defvar sieve-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map "\C-c\C-l" 'sieve-upload)
+    (define-key map "\C-c\C-c" 'sieve-upload-and-kill)
+    (define-key map "\C-c\C-m" 'sieve-manage)
+    map)
+  "Key map used in sieve mode.")
+
+;; Menu definition
+
+(defvar sieve-mode-menu nil
+  "Menubar used in sieve mode.")
+
+;; Code for Sieve editing mode.
+(autoload 'easy-menu-add-item "easymenu")
+
+;;;###autoload
+(define-derived-mode sieve-mode c-mode "Sieve"
+  "Major mode for editing Sieve code.
+This is much like C mode except for the syntax of comments.  Its keymap
+inherits from C mode's and it has the same variables for customizing
+indentation.  It has its own abbrev table and its own syntax table.
+
+Turning on Sieve mode runs `sieve-mode-hook'."
+  (set (make-local-variable 'paragraph-start) (concat "$\\|" page-delimiter))
+  (set (make-local-variable 'paragraph-separate) paragraph-start)
+  (set (make-local-variable 'comment-start) "#")
+  (set (make-local-variable 'comment-end) "")
+  ;;(set (make-local-variable 'comment-start-skip) "\\(^\\|\\s-\\);?#+ *")
+  (set (make-local-variable 'comment-start-skip) "#+ *")
+  (set (make-local-variable 'font-lock-defaults)
+       '(sieve-font-lock-keywords nil nil ((?_ . "w"))))
+  (easy-menu-add-item nil nil sieve-mode-menu))
+
+;; Menu
+
+(easy-menu-define sieve-mode-menu sieve-mode-map
+  "Sieve Menu."
+  '("Sieve"
+    ["Upload script" sieve-upload t]
+    ["Manage scripts on server" sieve-manage t]))
+
+(provide 'sieve-mode)
+
+;; sieve-mode.el ends here
diff --git a/lisp/net/sieve.el b/lisp/net/sieve.el
new file mode 100644 (file)
index 0000000..2046e53
--- /dev/null
@@ -0,0 +1,372 @@
+;;; sieve.el --- Utilities to manage sieve scripts
+
+;; Copyright (C) 2001-2016 Free Software Foundation, Inc.
+
+;; Author: Simon Josefsson <simon@josefsson.org>
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file contain utilities to facilitate upload, download and
+;; general management of sieve scripts.  Currently only the
+;; Managesieve protocol is supported (using sieve-manage.el), but when
+;; (useful) alternatives become available, they might be supported as
+;; well.
+;;
+;; The cursor navigation was inspired by biff-mode by Franklin Lee.
+;;
+;; Release history:
+;;
+;; 2001-10-31 Committed to Oort Gnus.
+;; 2002-07-27 Fix down-mouse-2 and down-mouse-3 in manage-mode.  Fix menubar
+;;            in manage-mode.  Change some messages.  Added sieve-deactivate*,
+;;            sieve-remove.  Fixed help text in manage-mode.  Suggested by
+;;            Ned Ludd.
+;;
+;; Todo:
+;;
+;; * Namespace?  This file contains `sieve-manage' and
+;;   `sieve-manage-mode', but there is a sieve-manage.el file as well.
+;;   Can't think of a good solution though, this file need a *-mode,
+;;   and naming it `sieve-mode' would collide with sieve-mode.el.  One
+;;   solution would be to come up with some better name that this file
+;;   can use that doesn't have the managesieve specific "manage" in
+;;   it.  sieve-dired?  i dunno.  we could copy all off sieve.el into
+;;   sieve-manage.el too, but I'd like to separate the interface from
+;;   the protocol implementation since the backends are likely to
+;;   change (well).
+;;
+;; * Define servers?  We could have a customize buffer to create a server,
+;;   with authentication/stream/etc parameters, much like Gnus, and then
+;;   only use names of defined servers when interacting with M-x sieve-*.
+;;   Right now you can't use STARTTLS, which sieve-manage.el provides
+
+;;; Code:
+
+(require 'sieve-manage)
+(require 'sieve-mode)
+
+;; User customizable variables:
+
+(defgroup sieve nil
+  "Manage sieve scripts."
+  :version "22.1"
+  :group 'tools)
+
+(defcustom sieve-new-script "<new script>"
+  "Name of name script indicator."
+  :type 'string
+  :group 'sieve)
+
+(defcustom sieve-buffer "*sieve*"
+  "Name of sieve management buffer."
+  :type 'string
+  :group 'sieve)
+
+(defcustom sieve-template "\
+require \"fileinto\";
+
+# Example script (remove comment character '#' to make it effective!):
+#
+# if header :contains \"from\" \"coyote\" {
+#   discard;
+# } elsif header :contains [\"subject\"] [\"$$$\"] {
+#   discard;
+# } else {
+#  fileinto \"INBOX\";
+# }
+"
+  "Template sieve script."
+  :type 'string
+  :group 'sieve)
+
+;; Internal variables:
+
+(defvar sieve-manage-buffer nil)
+(defvar sieve-buffer-header-end nil)
+(defvar sieve-buffer-script-name nil
+  "The real script name of the buffer.")
+(make-local-variable 'sieve-buffer-script-name)
+
+;; Sieve-manage mode:
+
+(defvar sieve-manage-mode-map
+  (let ((map (make-sparse-keymap)))
+    ;; various
+    (define-key map "?" 'sieve-help)
+    (define-key map "h" 'sieve-help)
+    ;; activating
+    (define-key map "m" 'sieve-activate)
+    (define-key map "u" 'sieve-deactivate)
+    (define-key map "\M-\C-?" 'sieve-deactivate-all)
+    ;; navigation keys
+    (define-key map "\C-p" 'sieve-prev-line)
+    (define-key map [up] 'sieve-prev-line)
+    (define-key map "\C-n" 'sieve-next-line)
+    (define-key map [down] 'sieve-next-line)
+    (define-key map " " 'sieve-next-line)
+    (define-key map "n" 'sieve-next-line)
+    (define-key map "p" 'sieve-prev-line)
+    (define-key map "\C-m" 'sieve-edit-script)
+    (define-key map "f" 'sieve-edit-script)
+    (define-key map "o" 'sieve-edit-script-other-window)
+    (define-key map "r" 'sieve-remove)
+    (define-key map "q" 'sieve-bury-buffer)
+    (define-key map "Q" 'sieve-manage-quit)
+    (define-key map [(down-mouse-2)] 'sieve-edit-script)
+    (define-key map [(down-mouse-3)] 'sieve-manage-mode-menu)
+    map)
+  "Keymap for `sieve-manage-mode'.")
+
+(easy-menu-define sieve-manage-mode-menu sieve-manage-mode-map
+  "Sieve Menu."
+  '("Manage Sieve"
+    ["Edit script" sieve-edit-script t]
+    ["Activate script" sieve-activate t]
+    ["Deactivate script" sieve-deactivate t]))
+
+(define-derived-mode sieve-manage-mode fundamental-mode "Sieve-manage"
+  "Mode used for sieve script management."
+  (buffer-disable-undo (current-buffer))
+  (setq truncate-lines t)
+  (easy-menu-add sieve-manage-mode-menu sieve-manage-mode-map))
+
+(put 'sieve-manage-mode 'mode-class 'special)
+
+;; Commands used in sieve-manage mode:
+
+(defun sieve-manage-quit ()
+  "Quit Manage Sieve and close the connection."
+  (interactive)
+  (sieve-manage-close sieve-manage-buffer)
+  (kill-buffer sieve-manage-buffer)
+  (kill-buffer (current-buffer)))
+
+(defun sieve-bury-buffer ()
+  "Bury the Manage Sieve buffer without closing the connection."
+  (interactive)
+  (bury-buffer))
+
+(defun sieve-activate (&optional pos)
+  (interactive "d")
+  (let ((name (sieve-script-at-point)) err)
+    (when (or (null name) (string-equal name sieve-new-script))
+      (error "No sieve script at point"))
+    (message "Activating script %s..." name)
+    (setq err (sieve-manage-setactive name sieve-manage-buffer))
+    (sieve-refresh-scriptlist)
+    (if (sieve-manage-ok-p err)
+       (message "Activating script %s...done" name)
+      (message "Activating script %s...failed: %s" name (nth 2 err)))))
+
+(defun sieve-deactivate-all (&optional pos)
+  (interactive "d")
+  (let ((name (sieve-script-at-point)) err)
+    (message "Deactivating scripts...")
+    (setq err (sieve-manage-setactive "" sieve-manage-buffer))
+    (sieve-refresh-scriptlist)
+    (if (sieve-manage-ok-p err)
+       (message "Deactivating scripts...done")
+      (message "Deactivating scripts...failed: %s" (nth 2 err)))))
+
+(defalias 'sieve-deactivate 'sieve-deactivate-all)
+
+(defun sieve-remove (&optional pos)
+  (interactive "d")
+  (let ((name (sieve-script-at-point)) err)
+    (when (or (null name) (string-equal name sieve-new-script))
+      (error "No sieve script at point"))
+    (message "Removing sieve script %s..." name)
+    (setq err (sieve-manage-deletescript name sieve-manage-buffer))
+    (unless (sieve-manage-ok-p err)
+      (error "Removing sieve script %s...failed: " err))
+    (sieve-refresh-scriptlist)
+    (message "Removing sieve script %s...done" name)))
+
+(defun sieve-edit-script (&optional pos)
+  (interactive "d")
+  (let ((name (sieve-script-at-point)))
+    (unless name
+      (error "No sieve script at point"))
+    (if (not (string-equal name sieve-new-script))
+       (let ((newbuf (generate-new-buffer name))
+             err)
+         (setq err (sieve-manage-getscript name newbuf sieve-manage-buffer))
+         (switch-to-buffer newbuf)
+         (unless (sieve-manage-ok-p err)
+           (error "Sieve download failed: %s" err)))
+      (switch-to-buffer (get-buffer-create "template.siv"))
+      (insert sieve-template))
+    (sieve-mode)
+    (setq sieve-buffer-script-name name)
+    (goto-char (point-min))
+    (message
+     (substitute-command-keys
+      "Press \\[sieve-upload] to upload script to server."))))
+
+(defmacro sieve-change-region (&rest body)
+  "Turns off sieve-region before executing BODY, then re-enables it after.
+Used to bracket operations which move point in the sieve-buffer."
+  `(progn
+     (sieve-highlight nil)
+     ,@body
+     (sieve-highlight t)))
+(put 'sieve-change-region 'lisp-indent-function 0)
+
+(defun sieve-next-line (&optional arg)
+  (interactive)
+  (unless arg
+    (setq arg 1))
+  (if (save-excursion
+       (forward-line arg)
+       (sieve-script-at-point))
+      (sieve-change-region
+       (forward-line arg))
+    (message "End of list")))
+
+(defun sieve-prev-line (&optional arg)
+  (interactive)
+  (unless arg
+    (setq arg -1))
+  (if (save-excursion
+       (forward-line arg)
+       (sieve-script-at-point))
+      (sieve-change-region
+       (forward-line arg))
+    (message "Beginning of list")))
+
+(defun sieve-help ()
+  "Display help for various sieve commands."
+  (interactive)
+  (if (eq last-command 'sieve-help)
+      ;; would need minor-mode for log-edit-mode
+      (describe-function 'sieve-mode)
+    (message "%s" (substitute-command-keys
+             "`\\[sieve-edit-script]':edit `\\[sieve-activate]':activate `\\[sieve-deactivate]':deactivate `\\[sieve-remove]':remove"))))
+
+;; Create buffer:
+
+(defun sieve-setup-buffer (server port)
+  (setq buffer-read-only nil)
+  (erase-buffer)
+  (buffer-disable-undo)
+  (let* ((port (or port sieve-manage-default-port))
+         (header (format "Server : %s:%s\n\n" server port)))
+    (insert header))
+  (set (make-local-variable 'sieve-buffer-header-end)
+       (point-max)))
+
+(defun sieve-script-at-point (&optional pos)
+  "Return name of sieve script at point POS, or nil."
+  (interactive "d")
+  (get-char-property (or pos (point)) 'script-name))
+
+(defun sieve-highlight (on)
+  "Turn ON or off highlighting on the current language overlay."
+  (overlay-put (car (overlays-at (point))) 'face (if on 'highlight 'default)))
+
+(defun sieve-insert-scripts (scripts)
+  "Format and insert LANGUAGE-LIST strings into current buffer at point."
+  (while scripts
+    (let ((p (point))
+         (ext nil)
+         (script (pop scripts)))
+      (if (consp script)
+         (insert (format " ACTIVE %s" (cdr script)))
+       (insert (format "        %s" script)))
+      (setq ext (make-overlay p (point)))
+      (overlay-put ext 'mouse-face 'highlight)
+      (overlay-put ext 'script-name (if (consp script)
+                                       (cdr script)
+                                     script))
+      (insert "\n"))))
+
+(defun sieve-open-server (server &optional port)
+  "Open SERVER (on PORT) and authenticate."
+  (with-current-buffer
+      (or ;; open server
+       (set (make-local-variable 'sieve-manage-buffer)
+           (sieve-manage-open server port))
+       (error "Error opening server %s" server))
+    (sieve-manage-authenticate)))
+
+(defun sieve-refresh-scriptlist ()
+  (interactive)
+  (with-current-buffer sieve-buffer
+    (setq buffer-read-only nil)
+    (delete-region (or sieve-buffer-header-end (point-max)) (point-max))
+    (goto-char (point-max))
+    ;; get list of script names and print them
+    (let ((scripts (sieve-manage-listscripts sieve-manage-buffer)))
+      (if (null scripts)
+         (insert
+           (substitute-command-keys
+            (format
+             "No scripts on server, press \\[sieve-edit-script] on %s to create a new script.\n"
+             sieve-new-script)))
+       (insert
+         (substitute-command-keys
+          (format (concat "%d script%s on server, press \\[sieve-edit-script] on a script "
+                          "name edits it, or\npress \\[sieve-edit-script] on %s to create "
+                          "a new script.\n") (length scripts)
+                          (if (eq (length scripts) 1) "" "s")
+                          sieve-new-script))))
+      (save-excursion
+       (sieve-insert-scripts (list sieve-new-script))
+       (sieve-insert-scripts scripts)))
+    (sieve-highlight t)
+    (setq buffer-read-only t)))
+
+;;;###autoload
+(defun sieve-manage (server &optional port)
+  (interactive "sServer: ")
+  (switch-to-buffer (get-buffer-create sieve-buffer))
+  (sieve-manage-mode)
+  (sieve-setup-buffer server port)
+  (if (sieve-open-server server port)
+      (sieve-refresh-scriptlist)
+    (message "Could not open server %s" server)))
+
+;;;###autoload
+(defun sieve-upload (&optional name)
+  (interactive)
+  (when (or (get-buffer sieve-buffer) (call-interactively 'sieve-manage))
+    (let ((script (buffer-string)) err)
+      (with-current-buffer (get-buffer sieve-buffer)
+       (setq err (sieve-manage-putscript
+                   (or name sieve-buffer-script-name (buffer-name))
+                   script sieve-manage-buffer))
+       (if (sieve-manage-ok-p err)
+           (message (substitute-command-keys
+                     "Sieve upload done.  Use \\[sieve-manage] to manage scripts."))
+         (message "Sieve upload failed: %s" (nth 2 err)))))))
+
+;;;###autoload
+(defun sieve-upload-and-bury (&optional name)
+  (interactive)
+  (sieve-upload name)
+  (bury-buffer))
+
+;;;###autoload
+(defun sieve-upload-and-kill (&optional name)
+  (interactive)
+  (sieve-upload name)
+  (kill-buffer))
+
+(provide 'sieve)
+
+;; sieve.el ends here
diff --git a/lisp/net/starttls.el b/lisp/net/starttls.el
new file mode 100644 (file)
index 0000000..096ed2a
--- /dev/null
@@ -0,0 +1,304 @@
+;;; starttls.el --- STARTTLS functions
+
+;; Copyright (C) 1999-2016 Free Software Foundation, Inc.
+
+;; Author: Daiki Ueno <ueno@unixuser.org>
+;; Author: Simon Josefsson <simon@josefsson.org>
+;; Created: 1999/11/20
+;; Keywords: TLS, SSL, OpenSSL, GnuTLS, mail, news
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This module defines some utility functions for STARTTLS profiles.
+
+;; [RFC 2595] "Using TLS with IMAP, POP3 and ACAP"
+;;     by Chris Newman <chris.newman@innosoft.com> (1999/06)
+
+;; This file now contains a combination of the two previous
+;; implementations both called "starttls.el".  The first one is Daiki
+;; Ueno's starttls.el which uses his own "starttls" command line tool,
+;; and the second one is Simon Josefsson's starttls.el which uses
+;; "gnutls-cli" from GnuTLS.
+;;
+;; If "starttls" is available, it is preferred by the code over
+;; "gnutls-cli", for backwards compatibility.  Use
+;; `starttls-use-gnutls' to toggle between implementations if you have
+;; both tools installed.  It is recommended to use GnuTLS, though, as
+;; it performs more verification of the certificates.
+
+;; The GnuTLS support requires GnuTLS 0.9.90 (released 2003-10-08) or
+;; later, from <http://www.gnu.org/software/gnutls/>, or "starttls"
+;; from <ftp://ftp.opaopa.org/pub/elisp/>.
+
+;; Usage is similar to `open-network-stream'.  For example:
+;;
+;; (when (setq tmp (starttls-open-stream
+;;                     "test" (current-buffer) "yxa.extundo.com" 25))
+;;   (accept-process-output tmp 15)
+;;   (process-send-string tmp "STARTTLS\n")
+;;   (accept-process-output tmp 15)
+;;   (message "STARTTLS output:\n%s" (starttls-negotiate tmp))
+;;   (process-send-string tmp "EHLO foo\n"))
+
+;; An example run yields the following output:
+;;
+;; 220 yxa.extundo.com ESMTP Sendmail 8.12.11/8.12.11/Debian-3; Wed, 26 May 2004 19:12:29 +0200; (No UCE/UBE) logging access from: c494102a.s-bi.bostream.se(OK)-c494102a.s-bi.bostream.se [217.215.27.65]
+;; 220 2.0.0 Ready to start TLS
+;; 250-yxa.extundo.com Hello c494102a.s-bi.bostream.se [217.215.27.65], pleased to meet you
+;; 250-ENHANCEDSTATUSCODES
+;; 250-PIPELINING
+;; 250-EXPN
+;; 250-VERB
+;; 250-8BITMIME
+;; 250-SIZE
+;; 250-DSN
+;; 250-ETRN
+;; 250-AUTH DIGEST-MD5 CRAM-MD5 PLAIN LOGIN
+;; 250-DELIVERBY
+;; 250 HELP
+;; nil
+;;
+;; With the message buffer containing:
+;;
+;; STARTTLS output:
+;; *** Starting TLS handshake
+;; - Server's trusted authorities:
+;;    [0]: C=SE,ST=Stockholm,L=Stockholm,O=YXA,OU=CA,CN=yxa.extundo.com,EMAIL=staff@yxa.extundo.com
+;; - Certificate type: X.509
+;;  - Got a certificate list of 2 certificates.
+;;
+;;  - Certificate[0] info:
+;;  # The hostname in the certificate matches 'yxa.extundo.com'.
+;;  # valid since: Wed May 26 12:16:00 CEST 2004
+;;  # expires at: Wed Jul 26 12:16:00 CEST 2023
+;;  # serial number: 04
+;;  # fingerprint: 7c 04 4b c1 fa 26 9b 5d 90 22 52 3c 65 3d 85 3a
+;;  # version: #1
+;;  # public key algorithm: RSA
+;;  #   Modulus: 1024 bits
+;;  # Subject's DN: C=SE,ST=Stockholm,L=Stockholm,O=YXA,OU=Mail server,CN=yxa.extundo.com,EMAIL=staff@yxa.extundo.com
+;;  # Issuer's DN: C=SE,ST=Stockholm,L=Stockholm,O=YXA,OU=CA,CN=yxa.extundo.com,EMAIL=staff@yxa.extundo.com
+;;
+;;  - Certificate[1] info:
+;;  # valid since: Sun May 23 11:35:00 CEST 2004
+;;  # expires at: Sun Jul 23 11:35:00 CEST 2023
+;;  # serial number: 00
+;;  # fingerprint: fc 76 d8 63 1a c9 0b 3b fa 40 fe ed 47 7a 58 ae
+;;  # version: #3
+;;  # public key algorithm: RSA
+;;  #   Modulus: 1024 bits
+;;  # Subject's DN: C=SE,ST=Stockholm,L=Stockholm,O=YXA,OU=CA,CN=yxa.extundo.com,EMAIL=staff@yxa.extundo.com
+;;  # Issuer's DN: C=SE,ST=Stockholm,L=Stockholm,O=YXA,OU=CA,CN=yxa.extundo.com,EMAIL=staff@yxa.extundo.com
+;;
+;; - Peer's certificate issuer is unknown
+;; - Peer's certificate is NOT trusted
+;; - Version: TLS 1.0
+;; - Key Exchange: RSA
+;; - Cipher: ARCFOUR 128
+;; - MAC: SHA
+;; - Compression: NULL
+
+;;; Code:
+
+(defgroup starttls nil
+  "Support for `Transport Layer Security' protocol."
+  :version "21.1"
+  :group 'mail)
+
+(defcustom starttls-gnutls-program "gnutls-cli"
+  "Name of GnuTLS command line tool.
+This program is used when GnuTLS is used, i.e. when
+`starttls-use-gnutls' is non-nil."
+  :version "22.1"
+  :type 'string
+  :group 'starttls)
+
+(defcustom starttls-program "starttls"
+  "The program to run in a subprocess to open an TLSv1 connection.
+This program is used when the `starttls' command is used,
+i.e. when `starttls-use-gnutls' is nil."
+  :type 'string
+  :group 'starttls)
+
+(defcustom starttls-use-gnutls (not (executable-find starttls-program))
+  "*Whether to use GnuTLS instead of the `starttls' command."
+  :version "22.1"
+  :type 'boolean
+  :group 'starttls)
+
+(defcustom starttls-extra-args nil
+  "Extra arguments to `starttls-program'.
+These apply when the `starttls' command is used, i.e. when
+`starttls-use-gnutls' is nil."
+  :type '(repeat string)
+  :group 'starttls)
+
+(defcustom starttls-extra-arguments nil
+  "Extra arguments to `starttls-gnutls-program'.
+These apply when GnuTLS is used, i.e. when `starttls-use-gnutls' is non-nil.
+
+For example, non-TLS compliant servers may require
+'(\"--protocols\" \"ssl3\").  Invoke \"gnutls-cli --help\" to
+find out which parameters are available."
+  :version "22.1"
+  :type '(repeat string)
+  :group 'starttls)
+
+(defcustom starttls-process-connection-type nil
+  "*Value for `process-connection-type' to use when starting STARTTLS process."
+  :version "22.1"
+  :type 'boolean
+  :group 'starttls)
+
+(defcustom starttls-connect "- Simple Client Mode:\n\n"
+  "*Regular expression indicating successful connection.
+The default is what GnuTLS's \"gnutls-cli\" outputs."
+  ;; GnuTLS cli.c:main() prints this string when it is starting to run
+  ;; in the application read/write phase.  If the logic, or the string
+  ;; itself, is modified, this must be updated.
+  :version "22.1"
+  :type 'regexp
+  :group 'starttls)
+
+(defcustom starttls-failure "\\*\\*\\* Handshake has failed"
+  "*Regular expression indicating failed TLS handshake.
+The default is what GnuTLS's \"gnutls-cli\" outputs."
+  ;; GnuTLS cli.c:do_handshake() prints this string on failure.  If the
+  ;; logic, or the string itself, is modified, this must be updated.
+  :version "22.1"
+  :type 'regexp
+  :group 'starttls)
+
+(defcustom starttls-success "- Compression: "
+  "*Regular expression indicating completed TLS handshakes.
+The default is what GnuTLS's \"gnutls-cli\" outputs."
+  ;; GnuTLS cli.c:do_handshake() calls, on success,
+  ;; common.c:print_info(), that unconditionally print this string
+  ;; last.  If that logic, or the string itself, is modified, this
+  ;; must be updated.
+  :version "22.1"
+  :type 'regexp
+  :group 'starttls)
+
+(defun starttls-negotiate-gnutls (process)
+  "Negotiate TLS on PROCESS opened by `open-starttls-stream'.
+This should typically only be done once.  It typically returns a
+multi-line informational message with information about the
+handshake, or nil on failure."
+  (let (buffer info old-max done-ok done-bad)
+    (if (null (setq buffer (process-buffer process)))
+       ;; XXX How to remove/extract the TLS negotiation junk?
+       (signal-process (process-id process) 'SIGALRM)
+      (with-current-buffer buffer
+       (save-excursion
+         (setq old-max (goto-char (point-max)))
+         (signal-process (process-id process) 'SIGALRM)
+         (while (and (processp process)
+                     (eq (process-status process) 'run)
+                     (save-excursion
+                       (goto-char old-max)
+                       (not (or (setq done-ok (re-search-forward
+                                               starttls-success nil t))
+                                (setq done-bad (re-search-forward
+                                                starttls-failure nil t))))))
+           (accept-process-output process 1 100)
+           (sit-for 0.1))
+         (setq info (buffer-substring-no-properties old-max (point-max)))
+         (delete-region old-max (point-max))
+         (if (or (and done-ok (not done-bad))
+                 ;; Prevent mitm that fake success msg after failure msg.
+                 (and done-ok done-bad (< done-ok done-bad)))
+             info
+           (message "STARTTLS negotiation failed: %s" info)
+           nil))))))
+
+(defun starttls-negotiate (process)
+  (if starttls-use-gnutls
+      (starttls-negotiate-gnutls process)
+    (signal-process (process-id process) 'SIGALRM)))
+
+(defun starttls-open-stream-gnutls (name buffer host port)
+  (message "Opening STARTTLS connection to `%s:%s'..." host port)
+  (let* (done
+        (old-max (with-current-buffer buffer (point-max)))
+        (process-connection-type starttls-process-connection-type)
+        (process (apply #'start-process name buffer
+                        starttls-gnutls-program "-s" host
+                        "-p" (if (integerp port)
+                                 (int-to-string port)
+                               port)
+                        starttls-extra-arguments)))
+    (set-process-query-on-exit-flag process nil)
+    (while (and (processp process)
+               (eq (process-status process) 'run)
+               (with-current-buffer buffer
+                 (goto-char old-max)
+                 (not (setq done (re-search-forward
+                                  starttls-connect nil t)))))
+      (accept-process-output process 0 100)
+      (sit-for 0.1))
+    (if done
+       (with-current-buffer buffer
+         (delete-region old-max done))
+      (delete-process process)
+      (setq process nil))
+    (message "Opening STARTTLS connection to `%s:%s'...%s"
+            host port (if done "done" "failed"))
+    process))
+
+;;;###autoload
+(defun starttls-open-stream (name buffer host port)
+  "Open a TLS connection for a port to a host.
+Returns a subprocess object to represent the connection.
+Input and output work as for subprocesses; `delete-process' closes it.
+Args are NAME BUFFER HOST PORT.
+NAME is name for process.  It is modified if necessary to make it unique.
+BUFFER is the buffer (or `buffer-name') to associate with the process.
+ Process output goes at end of that buffer, unless you specify
+ an output stream or filter function to handle the output.
+ BUFFER may be also nil, meaning that this process is not associated
+ with any buffer
+Third arg is name of the host to connect to, or its IP address.
+Fourth arg PORT is an integer specifying a port to connect to.
+If `starttls-use-gnutls' is nil, this may also be a service name, but
+GnuTLS requires a port number."
+  (if starttls-use-gnutls
+      (starttls-open-stream-gnutls name buffer host port)
+    (message "Opening STARTTLS connection to `%s:%s'" host (format "%s" port))
+    (let* ((process-connection-type starttls-process-connection-type)
+          (process (apply #'start-process
+                          name buffer starttls-program
+                          host (format "%s" port)
+                          starttls-extra-args)))
+      (set-process-query-on-exit-flag process nil)
+      process)))
+
+(defun starttls-available-p ()
+  "Say whether the STARTTLS programs are available."
+  (and (not (memq system-type '(windows-nt ms-dos)))
+       (executable-find (if starttls-use-gnutls
+                           starttls-gnutls-program
+                         starttls-program))))
+
+(defalias 'starttls-any-program-available 'starttls-available-p)
+(make-obsolete 'starttls-any-program-available 'starttls-available-p
+              "2011-08-02")
+
+(provide 'starttls)
+
+;;; starttls.el ends here
diff --git a/lisp/plstore.el b/lisp/plstore.el
new file mode 100644 (file)
index 0000000..62c50c0
--- /dev/null
@@ -0,0 +1,570 @@
+;;; plstore.el --- secure plist store -*- lexical-binding: t -*-
+;; Copyright (C) 2011-2016 Free Software Foundation, Inc.
+
+;; Author: Daiki Ueno <ueno@unixuser.org>
+;; Keywords: PGP, GnuPG
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary
+
+;; Plist based data store providing search and partial encryption.
+;;
+;; Creating:
+;;
+;; ;; Open a new store associated with ~/.emacs.d/auth.plist.
+;; (setq store (plstore-open (expand-file-name "~/.emacs.d/auth.plist")))
+;; ;; Both `:host' and `:port' are public property.
+;; (plstore-put store "foo" '(:host "foo.example.org" :port 80) nil)
+;; ;; No encryption will be needed.
+;; (plstore-save store)
+;;
+;; ;; `:user' is marked as secret.
+;; (plstore-put store "bar" '(:host "bar.example.org") '(:user "test"))
+;; ;; `:password' is marked as secret.
+;; (plstore-put store "baz" '(:host "baz.example.org") '(:password "test"))
+;; ;; Those secret properties are encrypted together.
+;; (plstore-save store)
+;;
+;; ;; Kill the buffer visiting ~/.emacs.d/auth.plist.
+;; (plstore-close store)
+;;
+;; Searching:
+;;
+;; (setq store (plstore-open (expand-file-name "~/.emacs.d/auth.plist")))
+;;
+;; ;; As the entry "foo" associated with "foo.example.org" has no
+;; ;; secret properties, no need to decryption.
+;; (plstore-find store '(:host ("foo.example.org")))
+;;
+;; ;; As the entry "bar" associated with "bar.example.org" has a
+;; ;; secret property `:user', Emacs tries to decrypt the secret (and
+;; ;; thus you will need to input passphrase).
+;; (plstore-find store '(:host ("bar.example.org")))
+;;
+;; ;; While the entry "baz" associated with "baz.example.org" has also
+;; ;; a secret property `:password', it is encrypted together with
+;; ;; `:user' of "bar", so no need to decrypt the secret.
+;; (plstore-find store '(:host ("bar.example.org")))
+;;
+;; (plstore-close store)
+;;
+;; Editing:
+;;
+;; This file also provides `plstore-mode', a major mode for editing
+;; the PLSTORE format file.  Visit a non-existing file and put the
+;; following line:
+;;
+;; (("foo" :host "foo.example.org" :secret-user "user"))
+;;
+;; where the prefixing `:secret-' means the property (without
+;; `:secret-' prefix) is marked as secret.  Thus, when you save the
+;; buffer, the `:secret-user' property is encrypted as `:user'.
+;;
+;; You can toggle the view between encrypted form and the decrypted
+;; form with C-c C-c.
+
+;;; Code:
+
+(require 'epg)
+
+(defgroup plstore nil
+  "Searchable, partially encrypted, persistent plist store"
+  :version "24.1"
+  :group 'files)
+
+(defcustom plstore-select-keys 'silent
+  "Control whether or not to pop up the key selection dialog.
+
+If t, always asks user to select recipients.
+If nil, query user only when a file's default recipients are not
+known (i.e. `plstore-encrypt-to' is not locally set in the buffer
+visiting a plstore file).
+If neither t nor nil, doesn't ask user."
+  :type '(choice (const :tag "Ask always" t)
+                (const :tag "Ask when recipients are not set" nil)
+                (const :tag "Don't ask" silent))
+  :group 'plstore)
+
+(defvar plstore-encrypt-to nil
+  "*Recipient(s) used for encrypting secret entries.
+May either be a string or a list of strings.  If it is nil,
+symmetric encryption will be used.")
+
+(put 'plstore-encrypt-to 'safe-local-variable
+     (lambda (val)
+       (or (stringp val)
+          (and (listp val)
+               (catch 'safe
+                 (mapc (lambda (elt)
+                         (unless (stringp elt)
+                           (throw 'safe nil)))
+                       val)
+                 t)))))
+
+(put 'plstore-encrypt-to 'permanent-local t)
+
+(defvar plstore-encoded nil)
+
+(put 'plstore-encoded 'permanent-local t)
+
+(defvar plstore-cache-passphrase-for-symmetric-encryption nil)
+(defvar plstore-passphrase-alist nil)
+
+(defun plstore-passphrase-callback-function (_context _key-id plstore)
+  (if plstore-cache-passphrase-for-symmetric-encryption
+      (let* ((file (file-truename (plstore-get-file plstore)))
+            (entry (assoc file plstore-passphrase-alist))
+            passphrase)
+       (or (copy-sequence (cdr entry))
+           (progn
+             (unless entry
+               (setq entry (list file)
+                     plstore-passphrase-alist
+                     (cons entry
+                           plstore-passphrase-alist)))
+             (setq passphrase
+                   (read-passwd (format "Passphrase for PLSTORE %s: "
+                                        (plstore--get-buffer plstore))))
+             (setcdr entry (copy-sequence passphrase))
+             passphrase)))
+    (read-passwd (format "Passphrase for PLSTORE %s: "
+                        (plstore--get-buffer plstore)))))
+
+(defun plstore-progress-callback-function (_context _what _char current total
+                                                   handback)
+  (if (= current total)
+      (message "%s...done" handback)
+    (message "%s...%d%%" handback
+            (if (> total 0) (floor (* (/ current (float total)) 100)) 0))))
+
+(defun plstore--get-buffer (arg)
+  (aref arg 0))
+
+(defun plstore--get-alist (arg)
+  (aref arg 1))
+
+(defun plstore--get-encrypted-data (arg)
+  (aref arg 2))
+
+(defun plstore--get-secret-alist (arg)
+  (aref arg 3))
+
+(defun plstore--get-merged-alist (arg)
+  (aref arg 4))
+
+(defun plstore--set-buffer (arg buffer)
+  (aset arg 0 buffer))
+
+(defun plstore--set-alist (arg plist)
+  (aset arg 1 plist))
+
+(defun plstore--set-encrypted-data (arg encrypted-data)
+  (aset arg 2 encrypted-data))
+
+(defun plstore--set-secret-alist (arg secret-alist)
+  (aset arg 3 secret-alist))
+
+(defun plstore--set-merged-alist (arg merged-alist)
+  (aset arg 4 merged-alist))
+
+(defun plstore-get-file (arg)
+  (buffer-file-name (plstore--get-buffer arg)))
+
+(defun plstore--make (&optional buffer alist encrypted-data secret-alist
+                               merged-alist)
+  (vector buffer alist encrypted-data secret-alist merged-alist))
+
+(defun plstore--init-from-buffer (plstore)
+  (goto-char (point-min))
+  (when (looking-at ";;; public entries")
+    (forward-line)
+    (plstore--set-alist plstore (read (point-marker)))
+    (forward-sexp)
+    (forward-char)
+    (when (looking-at ";;; secret entries")
+      (forward-line)
+      (plstore--set-encrypted-data plstore (read (point-marker))))
+    (plstore--merge-secret plstore)))
+
+;;;###autoload
+(defun plstore-open (file)
+  "Create a plstore instance associated with FILE."
+  (let* ((filename (file-truename file))
+        (buffer (or (find-buffer-visiting filename)
+                    (generate-new-buffer (format " plstore %s" filename))))
+        (store (plstore--make buffer)))
+    (with-current-buffer buffer
+      (erase-buffer)
+      (condition-case nil
+         (insert-file-contents-literally file)
+       (error))
+      (setq buffer-file-name (file-truename file))
+      (set-buffer-modified-p nil)
+      (plstore--init-from-buffer store)
+      store)))
+
+(defun plstore-revert (plstore)
+  "Replace current data in PLSTORE with the file on disk."
+  (with-current-buffer (plstore--get-buffer plstore)
+    (revert-buffer t t)
+    (plstore--init-from-buffer plstore)))
+
+(defun plstore-close (plstore)
+  "Destroy a plstore instance PLSTORE."
+  (kill-buffer (plstore--get-buffer plstore)))
+
+(defun plstore--merge-secret (plstore)
+  (let ((alist (plstore--get-secret-alist plstore))
+       modified-alist
+       modified-plist
+       modified-entry
+       entry
+       plist
+       placeholder)
+    (plstore--set-merged-alist
+     plstore
+     (copy-tree (plstore--get-alist plstore)))
+    (setq modified-alist (plstore--get-merged-alist plstore))
+    (while alist
+      (setq entry (car alist)
+           alist (cdr alist)
+           plist (cdr entry)
+           modified-entry (assoc (car entry) modified-alist)
+           modified-plist (cdr modified-entry))
+      (while plist
+       (setq placeholder
+             (plist-member
+              modified-plist
+              (intern (concat ":secret-"
+                              (substring (symbol-name (car plist)) 1)))))
+       (if placeholder
+           (setcar placeholder (car plist)))
+       (setq modified-plist
+             (plist-put modified-plist (car plist) (car (cdr plist))))
+       (setq plist (nthcdr 2 plist)))
+      (setcdr modified-entry modified-plist))))
+
+(defun plstore--decrypt (plstore)
+  (if (plstore--get-encrypted-data plstore)
+      (let ((context (epg-make-context 'OpenPGP))
+           plain)
+       (epg-context-set-passphrase-callback
+        context
+        (cons #'plstore-passphrase-callback-function
+              plstore))
+       (epg-context-set-progress-callback
+        context
+        (cons #'plstore-progress-callback-function
+              (format "Decrypting %s" (plstore-get-file plstore))))
+       (condition-case error
+           (setq plain
+                 (epg-decrypt-string context
+                                     (plstore--get-encrypted-data plstore)))
+         (error
+          (let ((entry (assoc (plstore-get-file plstore)
+                              plstore-passphrase-alist)))
+            (if entry
+                (setcdr entry nil)))
+          (signal (car error) (cdr error))))
+       (plstore--set-secret-alist plstore (car (read-from-string plain)))
+       (plstore--merge-secret plstore)
+       (plstore--set-encrypted-data plstore nil))))
+
+(defun plstore--match (entry keys skip-if-secret-found)
+  (let ((result t) key-name key-value prop-value secret-name)
+    (while keys
+      (setq key-name (car keys)
+           key-value (car (cdr keys))
+           prop-value (plist-get (cdr entry) key-name))
+       (unless (member prop-value key-value)
+         (if skip-if-secret-found
+             (progn
+               (setq secret-name
+                     (intern (concat ":secret-"
+                                     (substring (symbol-name key-name) 1))))
+               (if (plist-member (cdr entry) secret-name)
+                   (setq result 'secret)
+                 (setq result nil
+                       keys nil)))
+           (setq result nil
+                 keys nil)))
+       (setq keys (nthcdr 2 keys)))
+    result))
+
+(defun plstore-find (plstore keys)
+  "Perform search on PLSTORE with KEYS.
+KEYS is a plist."
+  (let (entries alist entry match decrypt plist)
+    ;; First, go through the merged plist alist and collect entries
+    ;; matched with keys.
+    (setq alist (plstore--get-merged-alist plstore))
+    (while alist
+      (setq entry (car alist)
+           alist (cdr alist)
+           match (plstore--match entry keys t))
+      (if (eq match 'secret)
+         (setq decrypt t)
+       (when match
+         (setq plist (cdr entry))
+         (while plist
+           (if (string-match "\\`:secret-" (symbol-name (car plist)))
+               (setq decrypt t
+                     plist nil))
+           (setq plist (nthcdr 2 plist)))
+         (setq entries (cons entry entries)))))
+    ;; Second, decrypt the encrypted plist and try again.
+    (when decrypt
+      (setq entries nil)
+      (plstore--decrypt plstore)
+      (setq alist (plstore--get-merged-alist plstore))
+      (while alist
+       (setq entry (car alist)
+             alist (cdr alist)
+             match (plstore--match entry keys nil))
+       (if match
+           (setq entries (cons entry entries)))))
+    (nreverse entries)))
+
+(defun plstore-get (plstore name)
+  "Get an entry with NAME in PLSTORE."
+  (let ((entry (assoc name (plstore--get-merged-alist plstore)))
+       plist)
+    (setq plist (cdr entry))
+    (while plist
+      (if (string-match "\\`:secret-" (symbol-name (car plist)))
+         (progn
+           (plstore--decrypt plstore)
+           (setq entry (assoc name (plstore--get-merged-alist plstore))
+                 plist nil))
+       (setq plist (nthcdr 2 plist))))
+    entry))
+
+(defun plstore-put (plstore name keys secret-keys)
+  "Put an entry with NAME in PLSTORE.
+KEYS is a plist containing non-secret data.
+SECRET-KEYS is a plist containing secret data."
+  (let (entry
+       plist
+       secret-plist
+       symbol)
+    (if secret-keys
+       (plstore--decrypt plstore))
+    (while secret-keys
+      (setq symbol
+           (intern (concat ":secret-"
+                           (substring (symbol-name (car secret-keys)) 1))))
+      (setq plist (plist-put plist symbol t)
+           secret-plist (plist-put secret-plist
+                                   (car secret-keys) (car (cdr secret-keys)))
+           secret-keys (nthcdr 2 secret-keys)))
+    (while keys
+      (setq symbol
+           (intern (concat ":secret-"
+                           (substring (symbol-name (car keys)) 1))))
+      (setq plist (plist-put plist (car keys) (car (cdr keys)))
+           keys (nthcdr 2 keys)))
+    (setq entry (assoc name (plstore--get-alist plstore)))
+    (if entry
+       (setcdr entry plist)
+      (plstore--set-alist
+       plstore
+       (cons (cons name plist) (plstore--get-alist plstore))))
+    (when secret-plist
+      (setq entry (assoc name (plstore--get-secret-alist plstore)))
+      (if entry
+         (setcdr entry secret-plist)
+       (plstore--set-secret-alist
+        plstore
+        (cons (cons name secret-plist) (plstore--get-secret-alist plstore)))))
+    (plstore--merge-secret plstore)))
+
+(defun plstore-delete (plstore name)
+  "Delete an entry with NAME from PLSTORE."
+  (let ((entry (assoc name (plstore--get-alist plstore))))
+    (if entry
+       (plstore--set-alist
+        plstore
+        (delq entry (plstore--get-alist plstore))))
+    (setq entry (assoc name (plstore--get-secret-alist plstore)))
+    (if entry
+       (plstore--set-secret-alist
+        plstore
+        (delq entry (plstore--get-secret-alist plstore))))
+    (setq entry (assoc name (plstore--get-merged-alist plstore)))
+    (if entry
+       (plstore--set-merged-alist
+        plstore
+        (delq entry (plstore--get-merged-alist plstore))))))
+
+(defvar pp-escape-newlines)
+(defun plstore--insert-buffer (plstore)
+  (insert ";;; public entries -*- mode: plstore -*- \n"
+         (pp-to-string (plstore--get-alist plstore)))
+  (if (plstore--get-secret-alist plstore)
+      (let ((context (epg-make-context 'OpenPGP))
+           (pp-escape-newlines nil)
+           (recipients
+            (cond
+             ((listp plstore-encrypt-to) plstore-encrypt-to)
+             ((stringp plstore-encrypt-to) (list plstore-encrypt-to))))
+           cipher)
+       (setf (epg-context-armor context) t)
+       (epg-context-set-passphrase-callback
+        context
+        (cons #'plstore-passphrase-callback-function
+              plstore))
+       (setq cipher (epg-encrypt-string
+                     context
+                     (pp-to-string
+                      (plstore--get-secret-alist plstore))
+                     (if (or (eq plstore-select-keys t)
+                             (and (null plstore-select-keys)
+                                  (not (local-variable-p 'plstore-encrypt-to
+                                                         (current-buffer)))))
+                         (epa-select-keys
+                          context
+                          "Select recipients for encryption.
+If no one is selected, symmetric encryption will be performed.  "
+                          recipients)
+                       (if plstore-encrypt-to
+                           (epg-list-keys context recipients)))))
+       (goto-char (point-max))
+       (insert ";;; secret entries\n" (pp-to-string cipher)))))
+
+(defun plstore-save (plstore)
+  "Save the contents of PLSTORE associated with a FILE."
+  (with-current-buffer (plstore--get-buffer plstore)
+    (erase-buffer)
+    (plstore--insert-buffer plstore)
+    (save-buffer)))
+
+(defun plstore--encode (plstore)
+  (plstore--decrypt plstore)
+  (let ((merged-alist (plstore--get-merged-alist plstore)))
+    (concat "("
+           (mapconcat
+            (lambda (entry)
+              (setq entry (copy-sequence entry))
+              (let ((merged-plist (cdr (assoc (car entry) merged-alist)))
+                    (plist (cdr entry)))
+                (while plist
+                  (if (string-match "\\`:secret-" (symbol-name (car plist)))
+                      (setcar (cdr plist)
+                              (plist-get
+                               merged-plist
+                               (intern (concat ":"
+                                               (substring (symbol-name
+                                                           (car plist))
+                                                          (match-end 0)))))))
+                  (setq plist (nthcdr 2 plist)))
+                (prin1-to-string entry)))
+            (plstore--get-alist plstore)
+            "\n")
+           ")")))
+
+(defun plstore--decode (string)
+  (let* ((alist (car (read-from-string string)))
+        (pointer alist)
+        secret-alist
+        plist
+        entry)
+    (while pointer
+      (unless (stringp (car (car pointer)))
+       (error "Invalid PLSTORE format %s" string))
+      (setq plist (cdr (car pointer)))
+      (while plist
+       (when (string-match "\\`:secret-" (symbol-name (car plist)))
+         (setq entry (assoc (car (car pointer)) secret-alist))
+         (unless entry
+           (setq entry (list (car (car pointer)))
+                 secret-alist (cons entry secret-alist)))
+         (setcdr entry (plist-put (cdr entry)
+                                  (intern (concat ":"
+                                               (substring (symbol-name
+                                                           (car plist))
+                                                          (match-end 0))))
+                                  (car (cdr plist))))
+         (setcar (cdr plist) t))
+       (setq plist (nthcdr 2 plist)))
+      (setq pointer (cdr pointer)))
+    (plstore--make nil alist nil secret-alist)))
+
+(defun plstore--write-contents-functions ()
+  (when plstore-encoded
+    (let ((store (plstore--decode (buffer-string)))
+         (file (buffer-file-name)))
+      (unwind-protect
+         (progn
+           (set-visited-file-name nil)
+           (with-temp-buffer
+             (plstore--insert-buffer store)
+             (write-region (buffer-string) nil file)))
+       (set-visited-file-name file)
+       (set-buffer-modified-p nil))
+      t)))
+
+(defun plstore-mode-original ()
+  "Show the original form of the this buffer."
+  (interactive)
+  (when plstore-encoded
+    (if (and (buffer-modified-p)
+            (y-or-n-p "Save buffer before reading the original form? "))
+       (save-buffer))
+    (erase-buffer)
+    (insert-file-contents-literally (buffer-file-name))
+    (set-buffer-modified-p nil)
+    (setq plstore-encoded nil)))
+
+(defun plstore-mode-decoded ()
+  "Show the decoded form of the this buffer."
+  (interactive)
+  (unless plstore-encoded
+    (if (and (buffer-modified-p)
+            (y-or-n-p "Save buffer before decoding? "))
+       (save-buffer))
+    (let ((store (plstore--make (current-buffer))))
+      (plstore--init-from-buffer store)
+      (erase-buffer)
+      (insert
+       (substitute-command-keys "\
+;;; You are looking at the decoded form of the PLSTORE file.\n\
+;;; To see the original form content, do \\[plstore-mode-toggle-display]\n\n"))
+      (insert (plstore--encode store))
+      (set-buffer-modified-p nil)
+      (setq plstore-encoded t))))
+
+(defun plstore-mode-toggle-display ()
+  "Toggle the display mode of PLSTORE between the original and decoded forms."
+  (interactive)
+  (if plstore-encoded
+      (plstore-mode-original)
+    (plstore-mode-decoded)))
+
+;;;###autoload
+(define-derived-mode plstore-mode emacs-lisp-mode "PLSTORE"
+  "Major mode for editing PLSTORE files."
+  (make-local-variable 'plstore-encoded)
+  (add-hook 'write-contents-functions #'plstore--write-contents-functions)
+  (define-key plstore-mode-map "\C-c\C-c" #'plstore-mode-toggle-display)
+  ;; to create a new file with plstore-mode, mark it as already decoded
+  (if (called-interactively-p 'any)
+      (setq plstore-encoded t)
+    (plstore-mode-decoded)))
+
+(provide 'plstore)
+
+;;; plstore.el ends here
diff --git a/lisp/registry.el b/lisp/registry.el
new file mode 100644 (file)
index 0000000..e8bc6f5
--- /dev/null
@@ -0,0 +1,379 @@
+;;; registry.el --- Track and remember data items by various fields
+
+;; Copyright (C) 2011-2016 Free Software Foundation, Inc.
+
+;; Author: Teodor Zlatanov <tzz@lifelogs.com>
+;; Keywords: data
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This library provides a general-purpose EIEIO-based registry
+;; database with persistence, initialized with these fields:
+
+;; version: a float
+
+;; max-size: an integer, default most-positive-fixnum
+
+;; prune-factor: a float between 0 and 1, default 0.1
+
+;; precious: a list of symbols
+
+;; tracked: a list of symbols
+
+;; tracker: a hashtable tuned for 100 symbols to track (you should
+;; only access this with the :lookup2-function and the
+;; :lookup2+-function)
+
+;; data: a hashtable with default size 10K and resize threshold 2.0
+;; (this reflects the expected usage so override it if you know better)
+
+;; ...plus methods to do all the work: `registry-search',
+;; `registry-lookup', `registry-lookup-secondary',
+;; `registry-lookup-secondary-value', `registry-insert',
+;; `registry-delete', `registry-prune', `registry-size' which see
+
+;; and with the following properties:
+
+;; Every piece of data has a unique ID and some general-purpose fields
+;; (F1=D1, F2=D2, F3=(a b c)...) expressed as an alist, e.g.
+
+;; ((F1 D1) (F2 D2) (F3 a b c))
+
+;; Note that whether a field has one or many pieces of data, the data
+;; is always a list of values.
+
+;; The user decides which fields are "precious", F2 for example.  When
+;; the registry is pruned, any entries without the F2 field will be
+;; removed until the size is :max-size * :prune-factor _less_ than the
+;; maximum database size. No entries with the F2 field will be removed
+;; at PRUNE TIME, which means it may not be possible to prune back all
+;; the way to the target size.
+
+;; When an entry is inserted, the registry will reject new entries if
+;; they bring it over the :max-size limit, even if they have the F2
+;; field.
+
+;; The user decides which fields are "tracked", F1 for example.  Any
+;; new entry is then indexed by all the tracked fields so it can be
+;; quickly looked up that way.  The data is always a list (see example
+;; above) and each list element is indexed.
+
+;; Precious and tracked field names must be symbols.  All other
+;; fields can be any other Emacs Lisp types.
+
+;;; Code:
+
+(eval-when-compile (require 'cl))
+
+(require 'eieio)
+(require 'eieio-base)
+
+;; The version number needs to be kept outside of the class definition
+;; itself.  The persistent-save process does *not* write to file any
+;; slot values that are equal to the default :initform value.  If a
+;; database object is at the most recent version, therefore, its
+;; version number will not be written to file.  That makes it
+;; difficult to know when a database needs to be upgraded.
+(defvar registry-db-version 0.2
+  "The current version of the registry format.")
+
+(defclass registry-db (eieio-persistent)
+  ((version :initarg :version
+            :initform nil
+            :type (or null float)
+            :documentation "The registry version.")
+   (max-size :initarg :max-size
+            ;; EIEIO's :initform is not 100% compatible with CLOS in
+            ;; that if the form is an atom, it assumes it's constant
+            ;; value rather than an expression, so in order to get the value
+            ;; of `most-positive-fixnum', we need to use an
+            ;; expression that's not just a symbol.
+             :initform (symbol-value 'most-positive-fixnum)
+             :type integer
+             :custom integer
+             :documentation "The maximum number of registry entries.")
+   (prune-factor
+    :initarg :prune-factor
+    :initform 0.1
+    :type float
+    :custom float
+    :documentation "Prune to (:max-size * :prune-factor) less
+    than the :max-size limit.  Should be a float between 0 and 1.")
+   (tracked :initarg :tracked
+            :initform nil
+            :type t
+            :documentation "The tracked (indexed) fields, a list of symbols.")
+   (precious :initarg :precious
+             :initform nil
+             :type t
+             :documentation "The precious fields, a list of symbols.")
+   (tracker :initarg :tracker
+            :type hash-table
+            :documentation "The field tracking hashtable.")
+   (data :initarg :data
+         :type hash-table
+         :documentation "The data hashtable.")))
+
+(cl-defmethod initialize-instance :before ((this registry-db) slots)
+  "Check whether a registry object needs to be upgraded."
+  ;; Hardcoded upgrade routines.  Version 0.1 to 0.2 requires the
+  ;; :max-soft slot to disappear, and the :max-hard slot to be renamed
+  ;; :max-size.
+  (let ((current-version
+        (and (plist-member slots :version)
+             (plist-get slots :version))))
+    (when (or (null current-version)
+             (eql current-version 0.1))
+      (setq slots
+           (plist-put slots :max-size (plist-get slots :max-hard)))
+      (setq slots
+           (plist-put slots :version registry-db-version))
+      (cl-remf slots :max-hard)
+      (cl-remf slots :max-soft))))
+
+(cl-defmethod initialize-instance :after ((this registry-db) slots)
+  "Set value of data slot of THIS after initialization."
+  (with-slots (data tracker) this
+    (unless (member :data slots)
+      (setq data
+           (make-hash-table :size 10000 :rehash-size 2.0 :test 'equal)))
+    (unless (member :tracker slots)
+      (setq tracker (make-hash-table :size 100 :rehash-size 2.0)))))
+
+(cl-defmethod registry-lookup ((db registry-db) keys)
+  "Search for KEYS in the registry-db THIS.
+Returns an alist of the key followed by the entry in a list, not a cons cell."
+  (let ((data (oref db data)))
+    (delq nil
+         (mapcar
+          (lambda (k)
+            (when (gethash k data)
+              (list k (gethash k data))))
+          keys))))
+
+(cl-defmethod registry-lookup-breaks-before-lexbind ((db registry-db) keys)
+  "Search for KEYS in the registry-db THIS.
+Returns an alist of the key followed by the entry in a list, not a cons cell."
+  (let ((data (oref db data)))
+    (delq nil
+         (loop for key in keys
+               when (gethash key data)
+               collect (list key (gethash key data))))))
+
+(cl-defmethod registry-lookup-secondary ((db registry-db) tracksym
+                                        &optional create)
+  "Search for TRACKSYM in the registry-db THIS.
+When CREATE is not nil, create the secondary index hashtable if needed."
+  (let ((h (gethash tracksym (oref db tracker))))
+    (if h
+       h
+      (when create
+       (puthash tracksym
+                (make-hash-table :size 800 :rehash-size 2.0 :test 'equal)
+                (oref db tracker))
+       (gethash tracksym (oref db tracker))))))
+
+(cl-defmethod registry-lookup-secondary-value ((db registry-db) tracksym val
+                                              &optional set)
+  "Search for TRACKSYM with value VAL in the registry-db THIS.
+When SET is not nil, set it for VAL (use t for an empty list)."
+  ;; either we're asked for creation or there should be an existing index
+  (when (or set (registry-lookup-secondary db tracksym))
+    ;; set the entry if requested,
+    (when set
+      (puthash val (if (eq t set) '() set)
+              (registry-lookup-secondary db tracksym t)))
+    (gethash val (registry-lookup-secondary db tracksym))))
+
+(defun registry--match (mode entry check-list)
+  ;; for all members
+  (when check-list
+    (let ((key (nth 0 (nth 0 check-list)))
+          (vals (cdr-safe (nth 0 check-list)))
+          found)
+      (while (and key vals (not found))
+        (setq found (case mode
+                      (:member
+                       (member (car-safe vals) (cdr-safe (assoc key entry))))
+                      (:regex
+                       (string-match (car vals)
+                                     (mapconcat
+                                      'prin1-to-string
+                                      (cdr-safe (assoc key entry))
+                                      "\0"))))
+              vals (cdr-safe vals)))
+      (or found
+          (registry--match mode entry (cdr-safe check-list))))))
+
+(cl-defmethod registry-search ((db registry-db) &rest spec)
+  "Search for SPEC across the registry-db THIS.
+For example calling with `:member \\='(a 1 2)' will match entry \((a 3 1)).
+Calling with `:all t' (any non-nil value) will match all.
+Calling with `:regex \\='(a \"h.llo\")' will match entry \(a \"hullo\" \"bye\").
+The test order is to check :all first, then :member, then :regex."
+  (when db
+    (let ((all (plist-get spec :all))
+         (member (plist-get spec :member))
+         (regex (plist-get spec :regex)))
+      (loop for k being the hash-keys of (oref db data)
+           using (hash-values v)
+           when (or
+                 ;; :all non-nil returns all
+                 all
+                 ;; member matching
+                 (and member (registry--match :member v member))
+                 ;; regex matching
+                 (and regex (registry--match :regex v regex)))
+           collect k))))
+
+(cl-defmethod registry-delete ((db registry-db) keys assert &rest spec)
+  "Delete KEYS from the registry-db THIS.
+If KEYS is nil, use SPEC to do a search.
+Updates the secondary ('tracked') indices as well.
+With assert non-nil, errors out if the key does not exist already."
+  (let* ((data (oref db data))
+        (keys (or keys
+                  (apply 'registry-search db spec)))
+        (tracked (oref db tracked)))
+
+    (dolist (key keys)
+      (let ((entry (gethash key data)))
+       (when assert
+         (assert entry nil
+                 "Key %s does not exist in database" key))
+       ;; clean entry from the secondary indices
+       (dolist (tr tracked)
+         ;; is this tracked symbol indexed?
+         (when (registry-lookup-secondary db tr)
+           ;; for every value in the entry under that key...
+           (dolist (val (cdr-safe (assq tr entry)))
+             (let* ((value-keys (registry-lookup-secondary-value
+                                 db tr val)))
+               (when (member key value-keys)
+                 ;; override the previous value
+                 (registry-lookup-secondary-value
+                  db tr val
+                  ;; with the indexed keys MINUS the current key
+                  ;; (we pass t when the list is empty)
+                  (or (delete key value-keys) t)))))))
+       (remhash key data)))
+    keys))
+
+(cl-defmethod registry-size ((db registry-db))
+  "Returns the size of the registry-db object THIS.
+This is the key count of the `data' slot."
+  (hash-table-count (oref db data)))
+
+(cl-defmethod registry-full ((db registry-db))
+  "Checks if registry-db THIS is full."
+  (>= (registry-size db)
+      (oref db max-size)))
+
+(cl-defmethod registry-insert ((db registry-db) key entry)
+  "Insert ENTRY under KEY into the registry-db THIS.
+Updates the secondary ('tracked') indices as well.
+Errors out if the key exists already."
+
+  (assert (not (gethash key (oref db data))) nil
+         "Key already exists in database")
+
+  (assert (not (registry-full db))
+         nil
+         "registry max-size limit reached")
+
+  ;; store the entry
+  (puthash key entry (oref db data))
+
+  ;; store the secondary indices
+  (dolist (tr (oref db tracked))
+    ;; for every value in the entry under that key...
+    (dolist (val (cdr-safe (assq tr entry)))
+      (let* ((value-keys (registry-lookup-secondary-value db tr val)))
+       (pushnew key value-keys :test 'equal)
+       (registry-lookup-secondary-value db tr val value-keys))))
+  entry)
+
+(cl-defmethod registry-reindex ((db registry-db))
+  "Rebuild the secondary indices of registry-db THIS."
+  (let ((count 0)
+       (expected (* (length (oref db tracked)) (registry-size db))))
+    (dolist (tr (oref db tracked))
+      (let (values)
+       (maphash
+        (lambda (key v)
+          (incf count)
+          (when (and (< 0 expected)
+                     (= 0 (mod count 1000)))
+            (message "reindexing: %d of %d (%.2f%%)"
+                     count expected (/ (* 100.0 count) expected)))
+          (dolist (val (cdr-safe (assq tr v)))
+            (let* ((value-keys (registry-lookup-secondary-value db tr val)))
+              (push key value-keys)
+              (registry-lookup-secondary-value db tr val value-keys))))
+        (oref db data))))))
+
+(cl-defmethod registry-prune ((db registry-db) &optional sortfunc)
+  "Prunes the registry-db object DB.
+
+Attempts to prune the number of entries down to \(*
+:max-size :prune-factor) less than the max-size limit, so
+pruning doesn't need to happen on every save. Removes only
+entries without the :precious keys, so it may not be possible to
+reach the target limit.
+
+Entries to be pruned are first sorted using SORTFUNC.  Entries
+from the front of the list are deleted first.
+
+Returns the number of deleted entries."
+  (let ((size (registry-size db))
+       (target-size
+        (floor (- (oref db max-size)
+                  (* (oref db max-size)
+                     (oref db prune-factor)))))
+       candidates)
+    (if (registry-full db)
+       (progn
+         (setq candidates
+               (registry-collect-prune-candidates
+                db (- size target-size) sortfunc))
+         (length (registry-delete db candidates nil)))
+      0)))
+
+(cl-defmethod registry-collect-prune-candidates ((db registry-db)
+                                                limit sortfunc)
+  "Collects pruning candidates from the registry-db object DB.
+
+Proposes only entries without the :precious keys, and attempts to
+return LIMIT such candidates.  If SORTFUNC is provided, sort
+entries first and return candidates from beginning of list."
+  (let* ((precious (oref db precious))
+        (precious-p (lambda (entry-key)
+                      (cdr (memq (car entry-key) precious))))
+        (data (oref db data))
+        (candidates (cl-loop for k being the hash-keys of data
+                             using (hash-values v)
+                             when (notany precious-p v)
+                             collect (cons k v))))
+    ;; We want the full entries for sorting, but should only return a
+    ;; list of entry keys.
+    (when sortfunc
+      (setq candidates (sort candidates sortfunc)))
+    (cl-subseq (mapcar #'car candidates) 0 (min limit (length candidates)))))
+
+(provide 'registry)
+;;; registry.el ends here
diff --git a/lisp/rtree.el b/lisp/rtree.el
new file mode 100644 (file)
index 0000000..662e043
--- /dev/null
@@ -0,0 +1,281 @@
+;;; rtree.el --- functions for manipulating range trees
+
+;; Copyright (C) 2010-2016 Free Software Foundation, Inc.
+
+;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; A "range tree" is a binary tree that stores ranges.  They are
+;; similar to interval trees, but do not allow overlapping intervals.
+
+;; A range is an ordered list of number intervals, like this:
+
+;; ((10 . 25) 56 78 (98 . 201))
+
+;; Common operations, like lookup, deletion and insertion are O(n) in
+;; a range, but an rtree is O(log n) in all these operations.
+;; Transformation between a range and an rtree is O(n).
+
+;; The rtrees are quite simple.  The structure of each node is
+
+;; (cons (cons low high) (cons left right))
+
+;; That is, they are three cons cells, where the car of the top cell
+;; is the actual range, and the cdr has the left and right child.  The
+;; rtrees aren't automatically balanced, but are balanced when
+;; created, and can be rebalanced when deemed necessary.
+
+;;; Code:
+
+(eval-when-compile
+  (require 'cl))
+
+(defmacro rtree-make-node ()
+  `(list (list nil) nil))
+
+(defmacro rtree-set-left (node left)
+  `(setcar (cdr ,node) ,left))
+
+(defmacro rtree-set-right (node right)
+  `(setcdr (cdr ,node) ,right))
+
+(defmacro rtree-set-range (node range)
+  `(setcar ,node ,range))
+
+(defmacro rtree-low (node)
+  `(caar ,node))
+
+(defmacro rtree-high (node)
+  `(cdar ,node))
+
+(defmacro rtree-set-low (node number)
+  `(setcar (car ,node) ,number))
+
+(defmacro rtree-set-high (node number)
+  `(setcdr (car ,node) ,number))
+
+(defmacro rtree-left (node)
+  `(cadr ,node))
+
+(defmacro rtree-right (node)
+  `(cddr ,node))
+
+(defmacro rtree-range (node)
+  `(car ,node))
+
+(defsubst rtree-normalize-range (range)
+  (when (numberp range)
+    (setq range (cons range range)))
+  range)
+
+(define-obsolete-function-alias 'rtree-normalise-range
+  'rtree-normalize-range "25.1")
+
+(defun rtree-make (range)
+  "Make an rtree from RANGE."
+  ;; Normalize the range.
+  (unless (listp (cdr-safe range))
+    (setq range (list range)))
+  (rtree-make-1 (cons nil range) (length range)))
+
+(defun rtree-make-1 (range length)
+  (let ((mid (/ length 2))
+       (node (rtree-make-node)))
+    (when (> mid 0)
+      (rtree-set-left node (rtree-make-1 range mid)))
+    (rtree-set-range node (rtree-normalize-range (cadr range)))
+    (setcdr range (cddr range))
+    (when (> (- length mid 1) 0)
+      (rtree-set-right node (rtree-make-1 range (- length mid 1))))
+    node))
+
+(defun rtree-memq (tree number)
+  "Return non-nil if NUMBER is present in TREE."
+  (while (and tree
+             (not (and (>= number (rtree-low tree))
+                       (<= number (rtree-high tree)))))
+    (setq tree
+         (if (< number (rtree-low tree))
+             (rtree-left tree)
+           (rtree-right tree))))
+  tree)
+
+(defun rtree-add (tree number)
+  "Add NUMBER to TREE."
+  (while tree
+    (cond
+     ;; It's already present, so we don't have to do anything.
+     ((and (>= number (rtree-low tree))
+          (<= number (rtree-high tree)))
+      (setq tree nil))
+     ((< number (rtree-low tree))
+      (cond
+       ;; Extend the low range.
+       ((= number (1- (rtree-low tree)))
+       (rtree-set-low tree number)
+       ;; Check whether we need to merge this node with the child.
+       (when (and (rtree-left tree)
+                  (= (rtree-high (rtree-left tree)) (1- number)))
+         ;; Extend the range to the low from the child.
+         (rtree-set-low tree (rtree-low (rtree-left tree)))
+         ;; The child can't have a right child, so just transplant the
+         ;; child's left tree to our left tree.
+         (rtree-set-left tree (rtree-left (rtree-left tree))))
+       (setq tree nil))
+       ;; Descend further to the left.
+       ((rtree-left tree)
+       (setq tree (rtree-left tree)))
+       ;; Add a new node.
+       (t
+       (let ((new-node (rtree-make-node)))
+         (rtree-set-low new-node number)
+         (rtree-set-high new-node number)
+         (rtree-set-left tree new-node)
+         (setq tree nil)))))
+     (t
+      (cond
+       ;; Extend the high range.
+       ((= number (1+ (rtree-high tree)))
+       (rtree-set-high tree number)
+       ;; Check whether we need to merge this node with the child.
+       (when (and (rtree-right tree)
+                  (= (rtree-low (rtree-right tree)) (1+ number)))
+         ;; Extend the range to the high from the child.
+         (rtree-set-high tree (rtree-high (rtree-right tree)))
+         ;; The child can't have a left child, so just transplant the
+         ;; child's left right to our right tree.
+         (rtree-set-right tree (rtree-right (rtree-right tree))))
+       (setq tree nil))
+       ;; Descend further to the right.
+       ((rtree-right tree)
+       (setq tree (rtree-right tree)))
+       ;; Add a new node.
+       (t
+       (let ((new-node (rtree-make-node)))
+         (rtree-set-low new-node number)
+         (rtree-set-high new-node number)
+         (rtree-set-right tree new-node)
+         (setq tree nil))))))))
+
+(defun rtree-delq (tree number)
+  "Remove NUMBER from TREE destructively.  Returns the new tree."
+  (let ((result tree)
+       prev)
+    (while tree
+      (cond
+       ((< number (rtree-low tree))
+       (setq prev tree
+             tree (rtree-left tree)))
+       ((> number (rtree-high tree))
+       (setq prev tree
+             tree (rtree-right tree)))
+       ;; The number is in this node.
+       (t
+       (cond
+        ;; The only entry; delete the node.
+        ((= (rtree-low tree) (rtree-high tree))
+         (cond
+          ;; Two children.  Replace with successor value.
+          ((and (rtree-left tree) (rtree-right tree))
+           (let ((parent tree)
+                 (successor (rtree-right tree)))
+             (while (rtree-left successor)
+               (setq parent successor
+                     successor (rtree-left successor)))
+             ;; We now have the leftmost child of our right child.
+             (rtree-set-range tree (rtree-range successor))
+             ;; Transplant the child (if any) to the parent.
+             (rtree-set-left parent (rtree-right successor))))
+          (t
+           (let ((rest (or (rtree-left tree)
+                           (rtree-right tree))))
+             ;; One or zero children.  Remove the node.
+             (cond
+              ((null prev)
+               (setq result rest))
+              ((eq (rtree-left prev) tree)
+               (rtree-set-left prev rest))
+              (t
+               (rtree-set-right prev rest)))))))
+        ;; The lowest in the range; just adjust.
+        ((= number (rtree-low tree))
+         (rtree-set-low tree (1+ number)))
+        ;; The highest in the range; just adjust.
+        ((= number (rtree-high tree))
+         (rtree-set-high tree (1- number)))
+        ;; We have to split this range.
+        (t
+         (let ((new-node (rtree-make-node)))
+           (rtree-set-low new-node (rtree-low tree))
+           (rtree-set-high new-node (1- number))
+           (rtree-set-low tree (1+ number))
+           (cond
+            ;; Two children; insert the new node as the predecessor
+            ;; node.
+            ((and (rtree-left tree) (rtree-right tree))
+             (let ((predecessor (rtree-left tree)))
+               (while (rtree-right predecessor)
+                 (setq predecessor (rtree-right predecessor)))
+               (rtree-set-right predecessor new-node)))
+            ((rtree-left tree)
+             (rtree-set-right new-node tree)
+             (rtree-set-left new-node (rtree-left tree))
+             (rtree-set-left tree nil)
+             (cond
+              ((null prev)
+               (setq result new-node))
+              ((eq (rtree-left prev) tree)
+               (rtree-set-left prev new-node))
+              (t
+               (rtree-set-right prev new-node))))
+            (t
+             (rtree-set-left tree new-node))))))
+       (setq tree nil))))
+    result))
+
+(defun rtree-extract (tree)
+  "Convert TREE to range form."
+  (let (stack result)
+    (while (or stack
+              tree)
+      (if tree
+         (progn
+           (push tree stack)
+           (setq tree (rtree-right tree)))
+       (setq tree (pop stack))
+       (push (if (= (rtree-low tree)
+                    (rtree-high tree))
+                 (rtree-low tree)
+               (rtree-range tree))
+             result)
+       (setq tree (rtree-left tree))))
+    result))
+
+(defun rtree-length (tree)
+  "Return the number of numbers stored in TREE."
+  (if (null tree)
+      0
+    (+ (rtree-length (rtree-left tree))
+       (1+ (- (rtree-high tree)
+             (rtree-low tree)))
+       (rtree-length (rtree-right tree)))))
+
+(provide 'rtree)
+
+;;; rtree.el ends here
diff --git a/test/lisp/auth-source-tests.el b/test/lisp/auth-source-tests.el
new file mode 100644 (file)
index 0000000..5faa1fe
--- /dev/null
@@ -0,0 +1,223 @@
+;;; auth-source-tests.el --- Tests for auth-source.el  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2015-2016 Free Software Foundation, Inc.
+
+;; Author: Damien Cassou <damien@cassou.me>,
+;;         Nicolas Petton <nicolas@petton.fr>
+
+;; This file is part of GNU Emacs.
+
+;; 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 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
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+
+(require 'ert)
+(require 'auth-source)
+
+(defvar secrets-enabled t
+  "Enable the secrets backend to test its features.")
+
+(defun auth-source-validate-backend (source validation-alist)
+  (let ((backend (auth-source-backend-parse source)))
+    (should (auth-source-backend-p backend))
+    (dolist (pair validation-alist)
+      (should (equal (eieio-oref backend (car pair)) (cdr pair))))))
+
+(ert-deftest auth-source-backend-parse-macos-keychain ()
+  (auth-source-validate-backend '(:source (:macos-keychain-generic foobar))
+                                '((:source . "foobar")
+                                  (:type . macos-keychain-generic)
+                                  (:search-function . auth-source-macos-keychain-search)
+                                  (:create-function . auth-source-macos-keychain-create))))
+
+(ert-deftest auth-source-backend-parse-macos-keychain-generic-string ()
+  (auth-source-validate-backend "macos-keychain-generic:foobar"
+                                '((:source . "foobar")
+                                  (:type . macos-keychain-generic)
+                                  (:search-function . auth-source-macos-keychain-search)
+                                  (:create-function . auth-source-macos-keychain-create))))
+
+(ert-deftest auth-source-backend-parse-macos-keychain-internet-string ()
+  (auth-source-validate-backend "macos-keychain-internet:foobar"
+                                '((:source . "foobar")
+                                  (:type . macos-keychain-internet)
+                                  (:search-function . auth-source-macos-keychain-search)
+                                  (:create-function . auth-source-macos-keychain-create))))
+
+(ert-deftest auth-source-backend-parse-macos-keychain-internet-symbol ()
+  (auth-source-validate-backend 'macos-keychain-internet
+                                '((:source . "default")
+                                  (:type . macos-keychain-internet)
+                                  (:search-function . auth-source-macos-keychain-search)
+                                  (:create-function . auth-source-macos-keychain-create))))
+
+(ert-deftest auth-source-backend-parse-macos-keychain-generic-symbol ()
+  (auth-source-validate-backend 'macos-keychain-generic
+                                '((:source . "default")
+                                  (:type . macos-keychain-generic)
+                                  (:search-function . auth-source-macos-keychain-search)
+                                  (:create-function . auth-source-macos-keychain-create))))
+
+(ert-deftest auth-source-backend-parse-macos-keychain-internet-default-string ()
+  (auth-source-validate-backend 'macos-keychain-internet
+                                '((:source . "default")
+                                  (:type . macos-keychain-internet)
+                                  (:search-function . auth-source-macos-keychain-search)
+                                  (:create-function . auth-source-macos-keychain-create))))
+
+(ert-deftest auth-source-backend-parse-plstore ()
+  (auth-source-validate-backend '(:source "foo.plist")
+                                '((:source . "foo.plist")
+                                  (:type . plstore)
+                                  (:search-function . auth-source-plstore-search)
+                                  (:create-function . auth-source-plstore-create))))
+
+(ert-deftest auth-source-backend-parse-netrc ()
+  (auth-source-validate-backend '(:source "foo")
+                                '((:source . "foo")
+                                  (:type . netrc)
+                                  (:search-function . auth-source-netrc-search)
+                                  (:create-function . auth-source-netrc-create))))
+
+(ert-deftest auth-source-backend-parse-netrc-string ()
+  (auth-source-validate-backend "foo"
+                                '((:source . "foo")
+                                  (:type . netrc)
+                                  (:search-function . auth-source-netrc-search)
+                                  (:create-function . auth-source-netrc-create))))
+
+(ert-deftest auth-source-backend-parse-secrets ()
+  (provide 'secrets) ; simulates the presence of the `secrets' package
+  (let ((secrets-enabled t))
+    (auth-source-validate-backend '(:source (:secrets "foo"))
+                                  '((:source . "foo")
+                                    (:type . secrets)
+                                    (:search-function . auth-source-secrets-search)
+                                    (:create-function . auth-source-secrets-create)))))
+
+(ert-deftest auth-source-backend-parse-secrets-strings ()
+  (provide 'secrets) ; simulates the presence of the `secrets' package
+  (let ((secrets-enabled t))
+    (auth-source-validate-backend "secrets:foo"
+                                  '((:source . "foo")
+                                    (:type . secrets)
+                                    (:search-function . auth-source-secrets-search)
+                                    (:create-function . auth-source-secrets-create)))))
+
+(ert-deftest auth-source-backend-parse-secrets-nil-source ()
+  (provide 'secrets) ; simulates the presence of the `secrets' package
+  (let ((secrets-enabled t))
+    (auth-source-validate-backend '(:source (:secrets nil))
+                                  '((:source . "session")
+                                    (:type . secrets)
+                                    (:search-function . auth-source-secrets-search)
+                                    (:create-function . auth-source-secrets-create)))))
+
+(ert-deftest auth-source-backend-parse-secrets-alias ()
+  (provide 'secrets) ; simulates the presence of the `secrets' package
+  (let ((secrets-enabled t))
+    ;; Redefine `secrets-get-alias' to map 'foo to "foo"
+    (cl-letf (((symbol-function 'secrets-get-alias) (lambda (_) "foo")))
+      (auth-source-validate-backend '(:source (:secrets foo))
+                                    '((:source . "foo")
+                                      (:type . secrets)
+                                      (:search-function . auth-source-secrets-search)
+                                      (:create-function . auth-source-secrets-create))))))
+
+(ert-deftest auth-source-backend-parse-secrets-symbol ()
+  (provide 'secrets) ; simulates the presence of the `secrets' package
+  (let ((secrets-enabled t))
+    ;; Redefine `secrets-get-alias' to map 'default to "foo"
+    (cl-letf (((symbol-function 'secrets-get-alias) (lambda (_) "foo")))
+      (auth-source-validate-backend 'default
+                                    '((:source . "foo")
+                                      (:type . secrets)
+                                      (:search-function . auth-source-secrets-search)
+                                      (:create-function . auth-source-secrets-create))))))
+
+(ert-deftest auth-source-backend-parse-secrets-no-alias ()
+  (provide 'secrets) ; simulates the presence of the `secrets' package
+  (let ((secrets-enabled t))
+    ;; Redefine `secrets-get-alias' to map 'foo to nil (so that
+    ;; "Login" is used by default
+    (cl-letf (((symbol-function 'secrets-get-alias) (lambda (_) nil)))
+      (auth-source-validate-backend '(:source (:secrets foo))
+                                    '((:source . "Login")
+                                      (:type . secrets)
+                                      (:search-function . auth-source-secrets-search)
+                                      (:create-function . auth-source-secrets-create))))))
+
+;; TODO This test shows suspicious behavior of auth-source: the
+;; "secrets" source is used even though nothing in the input indicates
+;; that is what we want
+(ert-deftest auth-source-backend-parse-secrets-no-source ()
+  (provide 'secrets) ; simulates the presence of the `secrets' package
+  (let ((secrets-enabled t))
+    (auth-source-validate-backend '(:source '(foo))
+                                  '((:source . "session")
+                                    (:type . secrets)
+                                    (:search-function . auth-source-secrets-search)
+                                    (:create-function . auth-source-secrets-create)))))
+
+(defun auth-source--test-netrc-parse-entry (entry host user port)
+  "Parse a netrc entry from buffer."
+  (auth-source-forget-all-cached)
+  (setq port (auth-source-ensure-strings port))
+  (with-temp-buffer
+    (insert entry)
+    (goto-char (point-min))
+    (let* ((check (lambda(alist)
+                   (and alist
+                        (auth-source-search-collection
+                         host
+                         (or
+                          (auth-source--aget alist "machine")
+                          (auth-source--aget alist "host")
+                          t))
+                        (auth-source-search-collection
+                         user
+                         (or
+                          (auth-source--aget alist "login")
+                          (auth-source--aget alist "account")
+                          (auth-source--aget alist "user")
+                          t))
+                        (auth-source-search-collection
+                         port
+                         (or
+                          (auth-source--aget alist "port")
+                          (auth-source--aget alist "protocol")
+                          t)))))
+          (entries (auth-source-netrc-parse-entries check 1)))
+      entries)))
+
+(ert-deftest auth-source-test-netrc-parse-entry ()
+  (should (equal (auth-source--test-netrc-parse-entry
+                  "machine mymachine1 login user1 password pass1\n" t t t)
+                 '((("password" . "pass1")
+                    ("login" . "user1")
+                    ("machine" . "mymachine1")))))
+  (should (equal (auth-source--test-netrc-parse-entry
+                  "machine mymachine1 login user1 password pass1 port 100\n"
+                  t t t)
+                 '((("port" . "100")
+                    ("password" . "pass1")
+                    ("login" . "user1")
+                    ("machine" . "mymachine1"))))))
+
+(provide 'auth-source-tests)
+;;; auth-source-tests.el ends here
diff --git a/test/lisp/gnus/auth-source-tests.el b/test/lisp/gnus/auth-source-tests.el
deleted file mode 100644 (file)
index 5faa1fe..0000000
+++ /dev/null
@@ -1,223 +0,0 @@
-;;; auth-source-tests.el --- Tests for auth-source.el  -*- lexical-binding: t; -*-
-
-;; Copyright (C) 2015-2016 Free Software Foundation, Inc.
-
-;; Author: Damien Cassou <damien@cassou.me>,
-;;         Nicolas Petton <nicolas@petton.fr>
-
-;; This file is part of GNU Emacs.
-
-;; 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 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
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
-
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
-
-;;; Commentary:
-
-;;
-
-;;; Code:
-
-(require 'ert)
-(require 'auth-source)
-
-(defvar secrets-enabled t
-  "Enable the secrets backend to test its features.")
-
-(defun auth-source-validate-backend (source validation-alist)
-  (let ((backend (auth-source-backend-parse source)))
-    (should (auth-source-backend-p backend))
-    (dolist (pair validation-alist)
-      (should (equal (eieio-oref backend (car pair)) (cdr pair))))))
-
-(ert-deftest auth-source-backend-parse-macos-keychain ()
-  (auth-source-validate-backend '(:source (:macos-keychain-generic foobar))
-                                '((:source . "foobar")
-                                  (:type . macos-keychain-generic)
-                                  (:search-function . auth-source-macos-keychain-search)
-                                  (:create-function . auth-source-macos-keychain-create))))
-
-(ert-deftest auth-source-backend-parse-macos-keychain-generic-string ()
-  (auth-source-validate-backend "macos-keychain-generic:foobar"
-                                '((:source . "foobar")
-                                  (:type . macos-keychain-generic)
-                                  (:search-function . auth-source-macos-keychain-search)
-                                  (:create-function . auth-source-macos-keychain-create))))
-
-(ert-deftest auth-source-backend-parse-macos-keychain-internet-string ()
-  (auth-source-validate-backend "macos-keychain-internet:foobar"
-                                '((:source . "foobar")
-                                  (:type . macos-keychain-internet)
-                                  (:search-function . auth-source-macos-keychain-search)
-                                  (:create-function . auth-source-macos-keychain-create))))
-
-(ert-deftest auth-source-backend-parse-macos-keychain-internet-symbol ()
-  (auth-source-validate-backend 'macos-keychain-internet
-                                '((:source . "default")
-                                  (:type . macos-keychain-internet)
-                                  (:search-function . auth-source-macos-keychain-search)
-                                  (:create-function . auth-source-macos-keychain-create))))
-
-(ert-deftest auth-source-backend-parse-macos-keychain-generic-symbol ()
-  (auth-source-validate-backend 'macos-keychain-generic
-                                '((:source . "default")
-                                  (:type . macos-keychain-generic)
-                                  (:search-function . auth-source-macos-keychain-search)
-                                  (:create-function . auth-source-macos-keychain-create))))
-
-(ert-deftest auth-source-backend-parse-macos-keychain-internet-default-string ()
-  (auth-source-validate-backend 'macos-keychain-internet
-                                '((:source . "default")
-                                  (:type . macos-keychain-internet)
-                                  (:search-function . auth-source-macos-keychain-search)
-                                  (:create-function . auth-source-macos-keychain-create))))
-
-(ert-deftest auth-source-backend-parse-plstore ()
-  (auth-source-validate-backend '(:source "foo.plist")
-                                '((:source . "foo.plist")
-                                  (:type . plstore)
-                                  (:search-function . auth-source-plstore-search)
-                                  (:create-function . auth-source-plstore-create))))
-
-(ert-deftest auth-source-backend-parse-netrc ()
-  (auth-source-validate-backend '(:source "foo")
-                                '((:source . "foo")
-                                  (:type . netrc)
-                                  (:search-function . auth-source-netrc-search)
-                                  (:create-function . auth-source-netrc-create))))
-
-(ert-deftest auth-source-backend-parse-netrc-string ()
-  (auth-source-validate-backend "foo"
-                                '((:source . "foo")
-                                  (:type . netrc)
-                                  (:search-function . auth-source-netrc-search)
-                                  (:create-function . auth-source-netrc-create))))
-
-(ert-deftest auth-source-backend-parse-secrets ()
-  (provide 'secrets) ; simulates the presence of the `secrets' package
-  (let ((secrets-enabled t))
-    (auth-source-validate-backend '(:source (:secrets "foo"))
-                                  '((:source . "foo")
-                                    (:type . secrets)
-                                    (:search-function . auth-source-secrets-search)
-                                    (:create-function . auth-source-secrets-create)))))
-
-(ert-deftest auth-source-backend-parse-secrets-strings ()
-  (provide 'secrets) ; simulates the presence of the `secrets' package
-  (let ((secrets-enabled t))
-    (auth-source-validate-backend "secrets:foo"
-                                  '((:source . "foo")
-                                    (:type . secrets)
-                                    (:search-function . auth-source-secrets-search)
-                                    (:create-function . auth-source-secrets-create)))))
-
-(ert-deftest auth-source-backend-parse-secrets-nil-source ()
-  (provide 'secrets) ; simulates the presence of the `secrets' package
-  (let ((secrets-enabled t))
-    (auth-source-validate-backend '(:source (:secrets nil))
-                                  '((:source . "session")
-                                    (:type . secrets)
-                                    (:search-function . auth-source-secrets-search)
-                                    (:create-function . auth-source-secrets-create)))))
-
-(ert-deftest auth-source-backend-parse-secrets-alias ()
-  (provide 'secrets) ; simulates the presence of the `secrets' package
-  (let ((secrets-enabled t))
-    ;; Redefine `secrets-get-alias' to map 'foo to "foo"
-    (cl-letf (((symbol-function 'secrets-get-alias) (lambda (_) "foo")))
-      (auth-source-validate-backend '(:source (:secrets foo))
-                                    '((:source . "foo")
-                                      (:type . secrets)
-                                      (:search-function . auth-source-secrets-search)
-                                      (:create-function . auth-source-secrets-create))))))
-
-(ert-deftest auth-source-backend-parse-secrets-symbol ()
-  (provide 'secrets) ; simulates the presence of the `secrets' package
-  (let ((secrets-enabled t))
-    ;; Redefine `secrets-get-alias' to map 'default to "foo"
-    (cl-letf (((symbol-function 'secrets-get-alias) (lambda (_) "foo")))
-      (auth-source-validate-backend 'default
-                                    '((:source . "foo")
-                                      (:type . secrets)
-                                      (:search-function . auth-source-secrets-search)
-                                      (:create-function . auth-source-secrets-create))))))
-
-(ert-deftest auth-source-backend-parse-secrets-no-alias ()
-  (provide 'secrets) ; simulates the presence of the `secrets' package
-  (let ((secrets-enabled t))
-    ;; Redefine `secrets-get-alias' to map 'foo to nil (so that
-    ;; "Login" is used by default
-    (cl-letf (((symbol-function 'secrets-get-alias) (lambda (_) nil)))
-      (auth-source-validate-backend '(:source (:secrets foo))
-                                    '((:source . "Login")
-                                      (:type . secrets)
-                                      (:search-function . auth-source-secrets-search)
-                                      (:create-function . auth-source-secrets-create))))))
-
-;; TODO This test shows suspicious behavior of auth-source: the
-;; "secrets" source is used even though nothing in the input indicates
-;; that is what we want
-(ert-deftest auth-source-backend-parse-secrets-no-source ()
-  (provide 'secrets) ; simulates the presence of the `secrets' package
-  (let ((secrets-enabled t))
-    (auth-source-validate-backend '(:source '(foo))
-                                  '((:source . "session")
-                                    (:type . secrets)
-                                    (:search-function . auth-source-secrets-search)
-                                    (:create-function . auth-source-secrets-create)))))
-
-(defun auth-source--test-netrc-parse-entry (entry host user port)
-  "Parse a netrc entry from buffer."
-  (auth-source-forget-all-cached)
-  (setq port (auth-source-ensure-strings port))
-  (with-temp-buffer
-    (insert entry)
-    (goto-char (point-min))
-    (let* ((check (lambda(alist)
-                   (and alist
-                        (auth-source-search-collection
-                         host
-                         (or
-                          (auth-source--aget alist "machine")
-                          (auth-source--aget alist "host")
-                          t))
-                        (auth-source-search-collection
-                         user
-                         (or
-                          (auth-source--aget alist "login")
-                          (auth-source--aget alist "account")
-                          (auth-source--aget alist "user")
-                          t))
-                        (auth-source-search-collection
-                         port
-                         (or
-                          (auth-source--aget alist "port")
-                          (auth-source--aget alist "protocol")
-                          t)))))
-          (entries (auth-source-netrc-parse-entries check 1)))
-      entries)))
-
-(ert-deftest auth-source-test-netrc-parse-entry ()
-  (should (equal (auth-source--test-netrc-parse-entry
-                  "machine mymachine1 login user1 password pass1\n" t t t)
-                 '((("password" . "pass1")
-                    ("login" . "user1")
-                    ("machine" . "mymachine1")))))
-  (should (equal (auth-source--test-netrc-parse-entry
-                  "machine mymachine1 login user1 password pass1 port 100\n"
-                  t t t)
-                 '((("port" . "100")
-                    ("password" . "pass1")
-                    ("login" . "user1")
-                    ("machine" . "mymachine1"))))))
-
-(provide 'auth-source-tests)
-;;; auth-source-tests.el ends here