325 lines
12 KiB
EmacsLisp
325 lines
12 KiB
EmacsLisp
;;; mh-identity.el --- multiple identify support for MH-E -*- lexical-binding: t; -*-
|
|
|
|
;; Copyright (C) 2002-2024 Free Software Foundation, Inc.
|
|
|
|
;; Author: Peter S. Galbraith <psg@debian.org>
|
|
;; Maintainer: Bill Wohler <wohler@newt.com>
|
|
;; Keywords: mail
|
|
;; See: mh-e.el
|
|
|
|
;; 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 <https://www.gnu.org/licenses/>.
|
|
|
|
;;; Commentary:
|
|
|
|
;; Multiple identity support for MH-E.
|
|
|
|
;; Used to easily set different fields such as From and Organization,
|
|
;; as well as different signature files.
|
|
|
|
;; Customize the variable `mh-identity-list' and see the Identity menu
|
|
;; in MH-Letter mode. The command `mh-insert-identity' can be used
|
|
;; to manually insert an identity.
|
|
|
|
;;; Code:
|
|
|
|
(require 'mh-e)
|
|
|
|
(autoload 'mml-insert-tag "mml")
|
|
|
|
(define-obsolete-variable-alias 'mh-identity-pgg-default-user-id
|
|
'mh-identity-gpg-default-user-id "29.1")
|
|
(defvar-local mh-identity-gpg-default-user-id nil
|
|
"Holds the GPG key ID.
|
|
This is normally set as part of an Identity in
|
|
`mh-identity-list'.")
|
|
|
|
(defvar mh-identity-menu nil
|
|
"The Identity menu.")
|
|
|
|
(defalias 'mh-identity-make-menu-no-autoload #'mh-identity-make-menu)
|
|
|
|
;;;###mh-autoload
|
|
(defun mh-identity-make-menu ()
|
|
"Build the Identity menu.
|
|
This should be called any time `mh-identity-list' or
|
|
`mh-auto-fields-list' change."
|
|
(easy-menu-define mh-identity-menu mh-letter-mode-map
|
|
"MH-E identity menu"
|
|
(append
|
|
'("Identity")
|
|
;; Dynamically render :type corresponding to `mh-identity-list'
|
|
;; e.g.:
|
|
;; ["Home" (mh-insert-identity "Home")
|
|
;; :style radio :active (not (equal mh-identity-local "Home"))
|
|
;; :selected (equal mh-identity-local "Home")]
|
|
'(["Insert Auto Fields"
|
|
(mh-insert-auto-fields) mh-auto-fields-list]
|
|
"--")
|
|
|
|
(mapcar (lambda (arg)
|
|
`[,arg (mh-insert-identity ,arg) :style radio
|
|
:selected (equal mh-identity-local ,arg)])
|
|
(mapcar #'car mh-identity-list))
|
|
'(["None"
|
|
(mh-insert-identity "None") :style radio
|
|
:selected (not mh-identity-local)]
|
|
"--"
|
|
["Set Default for Session"
|
|
(setq mh-identity-default mh-identity-local) t]
|
|
["Save as Default"
|
|
(customize-save-variable 'mh-identity-default mh-identity-local) t]
|
|
["Customize Identities" (customize-variable 'mh-identity-list) t]
|
|
))))
|
|
|
|
;;;###mh-autoload
|
|
(defun mh-identity-add-menu ()
|
|
"Add the current Identity menu.
|
|
See `mh-identity-make-menu'."
|
|
(declare (obsolete nil "29.1"))
|
|
nil)
|
|
|
|
(defvar-local mh-identity-local nil
|
|
"Buffer-local variable that holds the identity currently in use.")
|
|
|
|
(defun mh-header-field-delete (field value-only)
|
|
"Delete header FIELD, or only its value if VALUE-ONLY is t.
|
|
Return t if anything is deleted."
|
|
(let ((field-colon (if (string-match "^.*:$" field)
|
|
field
|
|
(concat field ":"))))
|
|
(when (mh-goto-header-field field-colon)
|
|
(if (not value-only)
|
|
(beginning-of-line)
|
|
(forward-char))
|
|
(delete-region (point)
|
|
(progn (mh-header-field-end)
|
|
(if (not value-only) (forward-char 1))
|
|
(point)))
|
|
t)))
|
|
|
|
(defvar mh-identity-signature-start nil
|
|
"Marker for the beginning of a signature inserted by `mh-insert-identity'.")
|
|
(defvar mh-identity-signature-end nil
|
|
"Marker for the end of a signature inserted by `mh-insert-identity'.")
|
|
|
|
(defun mh-identity-field-handler (field)
|
|
"Return the handler for header FIELD or nil if none set.
|
|
The field name is downcased. If the FIELD begins with the
|
|
character \":\", then it must have a special handler defined in
|
|
`mh-identity-handlers', else return an error since it is not a
|
|
valid header field."
|
|
(or (cdr (assoc-string field mh-identity-handlers t))
|
|
(and (eq (aref field 0) ?:)
|
|
(error "Field %s not found in `mh-identity-handlers'" field))
|
|
(cdr (assoc ":default" mh-identity-handlers))
|
|
'mh-identity-handler-default))
|
|
|
|
;;;###mh-autoload
|
|
(defun mh-select-identity (default)
|
|
"Prompt for and return an identity.
|
|
If DEFAULT is non-nil, it will be used if the user doesn't enter a
|
|
different identity.
|
|
|
|
See `mh-identity-list'."
|
|
(let (identity)
|
|
(setq identity
|
|
(completing-read
|
|
"Identity: "
|
|
(cons '("None")
|
|
(mapcar #'list (mapcar #'car mh-identity-list)))
|
|
nil t default nil default))
|
|
(if (equal identity "None")
|
|
nil
|
|
identity)))
|
|
|
|
;;;###mh-autoload
|
|
(defun mh-identity-field (identity field)
|
|
"Return the specified FIELD of the given IDENTITY.
|
|
|
|
See `mh-identity-list'."
|
|
(let* ((pers-list (cadr (assoc identity mh-identity-list)))
|
|
(value (cdr (assoc field pers-list))))
|
|
value))
|
|
|
|
;;;###mh-autoload
|
|
(defun mh-insert-identity (identity &optional maybe-insert)
|
|
"Insert fields specified by given IDENTITY.
|
|
|
|
In a program, do not insert fields if MAYBE-INSERT is non-nil,
|
|
`mh-identity-default' is non-nil, and fields have already been
|
|
inserted.
|
|
|
|
See `mh-identity-list'."
|
|
(interactive
|
|
(list (completing-read
|
|
"Identity: "
|
|
(if mh-identity-local
|
|
(cons '("None")
|
|
(mapcar #'list (mapcar #'car mh-identity-list)))
|
|
(mapcar #'list (mapcar #'car mh-identity-list)))
|
|
nil t)
|
|
nil))
|
|
|
|
(when (or (not maybe-insert)
|
|
(and (boundp 'mh-identity-default)
|
|
mh-identity-default
|
|
(not mh-identity-local)))
|
|
(save-excursion
|
|
;;First remove old settings, if any.
|
|
(when mh-identity-local
|
|
(let ((pers-list (cadr (assoc mh-identity-local mh-identity-list))))
|
|
(while pers-list
|
|
(let* ((field (caar pers-list))
|
|
(handler (mh-identity-field-handler field)))
|
|
(funcall handler field 'remove))
|
|
(setq pers-list (cdr pers-list)))))
|
|
;; Then insert the replacement
|
|
(when (not (equal "None" identity))
|
|
(let ((pers-list (cadr (assoc identity mh-identity-list))))
|
|
(while pers-list
|
|
(let* ((field (caar pers-list))
|
|
(value (cdar pers-list))
|
|
(handler (mh-identity-field-handler field)))
|
|
(funcall handler field 'add value))
|
|
(setq pers-list (cdr pers-list))))))
|
|
;; Remember what is in use in this buffer
|
|
(if (equal "None" identity)
|
|
(setq mh-identity-local nil)
|
|
(setq mh-identity-local identity))))
|
|
|
|
;;;###mh-autoload
|
|
(defun mh-identity-handler-gpg-identity (_field action &optional value)
|
|
"Process header FIELD \":pgg-default-user-id\".
|
|
The ACTION is one of `remove' or `add'. If `add', the VALUE is added.
|
|
The buffer-local variable `mh-identity-gpg-default-user-id' is set to
|
|
VALUE when action `add' is selected."
|
|
(cond
|
|
((or (equal action 'remove)
|
|
(not value)
|
|
(string= value ""))
|
|
(setq mh-identity-gpg-default-user-id nil))
|
|
((equal action 'add)
|
|
(setq mh-identity-gpg-default-user-id value))))
|
|
|
|
;;;###mh-autoload
|
|
(defun mh-identity-handler-signature (_field action &optional value)
|
|
"Process header FIELD \":signature\".
|
|
The ACTION is one of `remove' or `add'. If `add', the VALUE is
|
|
added."
|
|
(cond
|
|
((equal action 'remove)
|
|
(when (and (markerp mh-identity-signature-start)
|
|
(markerp mh-identity-signature-end))
|
|
(delete-region mh-identity-signature-start
|
|
mh-identity-signature-end)))
|
|
(t
|
|
;; Insert "signature". Nil value means to use `mh-signature-file-name'.
|
|
(when (not (mh-signature-separator-p)) ;...unless already present
|
|
(goto-char (point-max))
|
|
(save-restriction
|
|
(narrow-to-region (point) (point))
|
|
(if (null value)
|
|
(mh-insert-signature)
|
|
(mh-insert-signature value))
|
|
(setq-local mh-identity-signature-start (point-min-marker))
|
|
(set-marker-insertion-type mh-identity-signature-start t)
|
|
(setq-local mh-identity-signature-end (point-max-marker)))))))
|
|
|
|
(defvar mh-identity-attribution-verb-start nil
|
|
"Marker for the beginning of the attribution verb.")
|
|
(defvar mh-identity-attribution-verb-end nil
|
|
"Marker for the end of the attribution verb.")
|
|
|
|
;;;###mh-autoload
|
|
(defun mh-identity-handler-attribution-verb (_field action &optional value)
|
|
"Process header FIELD \":attribution-verb\".
|
|
The ACTION is one of `remove' or `add'. If `add', the VALUE is
|
|
added."
|
|
(when (and (markerp mh-identity-attribution-verb-start)
|
|
(markerp mh-identity-attribution-verb-end))
|
|
(delete-region mh-identity-attribution-verb-start
|
|
mh-identity-attribution-verb-end)
|
|
(goto-char mh-identity-attribution-verb-start)
|
|
(cond
|
|
((equal action 'remove) ; Replace with default
|
|
(mh-identity-insert-attribution-verb nil))
|
|
(t ; Insert attribution verb.
|
|
(mh-identity-insert-attribution-verb value)))))
|
|
|
|
;;;###mh-autoload
|
|
(defun mh-identity-insert-attribution-verb (value)
|
|
"Insert VALUE as attribution verb, setting up delimiting markers.
|
|
If VALUE is nil, use `mh-extract-from-attribution-verb'."
|
|
(save-restriction
|
|
(narrow-to-region (point) (point))
|
|
(if (null value)
|
|
(insert mh-extract-from-attribution-verb)
|
|
(insert value))
|
|
(setq-local mh-identity-attribution-verb-start (point-min-marker))
|
|
(set-marker-insertion-type mh-identity-attribution-verb-start t)
|
|
(setq-local mh-identity-attribution-verb-end (point-max-marker))))
|
|
|
|
(defun mh-identity-handler-default (field action top &optional value)
|
|
"Process header FIELD.
|
|
The ACTION is one of `remove' or `add'. If TOP is non-nil, add the
|
|
field and its VALUE at the top of the header, else add it at the
|
|
bottom of the header. If action is `add', the VALUE is added."
|
|
(let ((field-colon (if (string-match "^.*:$" field)
|
|
field
|
|
(concat field ":"))))
|
|
(cond
|
|
((equal action 'remove)
|
|
(mh-header-field-delete field-colon nil))
|
|
(t
|
|
(cond
|
|
;; No value, remove field
|
|
((or (not value)
|
|
(string= value ""))
|
|
(mh-header-field-delete field-colon nil))
|
|
;; Existing field, replace
|
|
((mh-header-field-delete field-colon t)
|
|
(insert value))
|
|
;; Other field, add at end or top
|
|
(t
|
|
(goto-char (point-min))
|
|
(if (not top)
|
|
(mh-goto-header-end 0))
|
|
(insert field-colon " " value "\n")))))))
|
|
|
|
;;;###mh-autoload
|
|
(defun mh-identity-handler-top (field action &optional value)
|
|
"Process header FIELD.
|
|
The ACTION is one of `remove' or `add'. If `add', the VALUE is
|
|
added. If the field wasn't present, it is added to the top of the
|
|
header."
|
|
(mh-identity-handler-default field action t value))
|
|
|
|
;;;###mh-autoload
|
|
(defun mh-identity-handler-bottom (field action &optional value)
|
|
"Process header FIELD.
|
|
The ACTION is one of `remove' or `add'. If `add', the VALUE is
|
|
added. If the field wasn't present, it is added to the bottom of
|
|
the header."
|
|
(mh-identity-handler-default field action nil value))
|
|
|
|
(provide 'mh-identity)
|
|
|
|
;; Local Variables:
|
|
;; sentence-end-double-space: nil
|
|
;; End:
|
|
|
|
;;; mh-identity.el ends here
|