;;; Code:
-(eval-when-compile (require 'cl-lib))
+(eval-when-compile (require 'subr-x))
(require 'message)
+(require 'gmm-utils)
(require 'mm-view)
(require 'format-spec)
:group 'notmuch-send)
(defgroup notmuch-reply nil
- "Replying to messages in notmuch"
+ "Replying to messages in notmuch."
:group 'notmuch)
(defcustom notmuch-mua-cite-function 'message-cite-original
:type 'regexp
:group 'notmuch-send)
+(defcustom notmuch-mua-subject-regexp
+ "[[:blank:]]*$"
+ "Message subject indicating that something may be amiss.
+By default, this checks for empty subject lines.
+
+This is not used unless `notmuch-mua-subject-check' is added to
+`notmuch-mua-send-hook'."
+ :type 'regexp
+ :group 'notmuch-send)
+
;;; Various functions
(defun notmuch-mua-attachment-check ()
- "Signal an error if the message text indicates that an
-attachment is expected but no MML referencing an attachment is
-found.
+ "Signal an error an attachement is expected but missing.
+
+Signal an error if the message text indicates that an attachment
+is expected but no MML referencing an attachment is found.
Typically this is added to `notmuch-mua-send-hook'."
(when (and
;; ...signal an error.
(error "Missing attachment")))
+(defun notmuch-mua-subject-check ()
+ "Signal an error if the subject seems amiss.
+More precisely, if the subject conforms to
+`notmuch-mua-subject-regexp'.
+
+Typically this is added to `notmuch-mua-send-hook'."
+ (or (save-excursion
+ (message-goto-subject)
+ (message-beginning-of-header t)
+ (not (looking-at-p notmuch-mua-subject-regexp)))
+ (y-or-n-p "Subject may be erroneous – is that okay?")
+ (error "Erroneous subject")))
+
(defun notmuch-mua-get-switch-function ()
"Get a switch function according to `notmuch-mua-compose-in'."
(pcase notmuch-mua-compose-in
(defun notmuch-mua-reply-crypto (parts)
"Add mml sign-encrypt flag if any part of original message is encrypted."
(cl-loop for part in parts
- if (notmuch-match-content-type (plist-get part :content-type)
- "multipart/encrypted")
+ for type = (plist-get part :content-type)
+ if (notmuch-match-content-type type "multipart/encrypted")
do (mml-secure-message-sign-encrypt)
- else if (notmuch-match-content-type (plist-get part :content-type)
- "multipart/*")
+ else if (notmuch-match-content-type type "multipart/*")
do (notmuch-mua-reply-crypto (plist-get part :content))))
;; There is a bug in Emacs' message.el that results in a newline
;;; Mua reply
-(defun notmuch-mua-reply (query-string &optional sender reply-all)
- (let ((args '("reply" "--format=sexp" "--format-version=4"))
- (process-crypto notmuch-show-process-crypto)
- reply
- original)
+(defun notmuch-mua-reply (query-string &optional sender reply-all duplicate)
+ (let* ((duparg (and duplicate (list (format "--duplicate=%d" duplicate))))
+ (args `("reply" "--format=sexp" "--format-version=5" ,@duparg))
+ (process-crypto notmuch-show-process-crypto)
+ reply
+ original)
(when process-crypto
(setq args (append args '("--decrypt=true"))))
(if reply-all
;; text.
(notmuch-show-process-crypto process-crypto)
;; Don't indent multipart sub-parts.
- (notmuch-show-indent-multipart nil))
+ (notmuch-show-indent-multipart nil)
+ ;; Stop certain mime types from being inlined
+ (mm-inline-override-types (notmuch--inline-override-types)))
;; We don't want sigstatus buttons (an information leak and usually wrong anyway).
(cl-letf (((symbol-function 'notmuch-crypto-insert-sigstatus-button) #'ignore)
((symbol-function 'notmuch-crypto-insert-encstatus-button) #'ignore))
(defvar notmuch-message-mode-map
(let ((map (make-sparse-keymap)))
- (define-key map (kbd "C-c C-c") #'notmuch-mua-send-and-exit)
- (define-key map (kbd "C-c C-s") #'notmuch-mua-send)
+ (define-key map [remap message-send-and-exit] #'notmuch-mua-send-and-exit)
+ (define-key map [remap message-send] #'notmuch-mua-send)
(define-key map (kbd "C-c C-p") #'notmuch-draft-postpone)
(define-key map (kbd "C-x C-s") #'notmuch-draft-save)
map)
(erase-buffer)
(notmuch-message-mode)))
+(defun notmuch-mua--remove-dont-reply-to-names ()
+ (when-let* ((nr (if (functionp message-dont-reply-to-names)
+ message-dont-reply-to-names
+ (gmm-regexp-concat message-dont-reply-to-names)))
+ (nr-filter
+ (if (functionp nr)
+ (lambda (mail) (and (not (funcall nr mail)) mail))
+ (lambda (mail) (and (not (string-match-p nr mail)) mail)))))
+ (dolist (header '("To" "Cc"))
+ (when-let ((v (message-fetch-field header)))
+ (let* ((tokens (mapcar #'string-trim (message-tokenize-header v)))
+ (good-tokens (delq nil (mapcar nr-filter tokens)))
+ (addr (and good-tokens (mapconcat #'identity good-tokens ", "))))
+ (message-replace-header header addr))))))
+
+;;;###autoload
(defun notmuch-mua-mail (&optional to subject other-headers _continue
switch-function yank-action send-actions
- return-action &rest ignored)
- "Invoke the notmuch mail composition window."
+ return-action &rest _ignored)
+ "Invoke the notmuch mail composition window.
+
+The position of point when the function returns differs depending
+on the values of TO and SUBJECT. If both are non-nil, point is
+moved to the message's body. If SUBJECT is nil but TO isn't,
+point is moved to the \"Subject:\" header. Otherwise, point is
+moved to the \"To:\" header."
(interactive)
(when notmuch-mua-user-agent-function
(let ((user-agent (funcall notmuch-mua-user-agent-function)))
- (unless (string= "" user-agent)
+ (unless (string-empty-p user-agent)
(push (cons 'User-Agent user-agent) other-headers))))
- (unless (assq 'From other-headers)
- (push (cons 'From (message-make-from
- (notmuch-user-name)
- (notmuch-user-primary-email)))
- other-headers))
(notmuch-mua-pop-to-buffer (message-buffer-name "mail" to)
(or switch-function
(notmuch-mua-get-switch-function)))
;; Cause `message-setup-1' to do things relevant for mail,
;; such as observe `message-default-mail-headers'.
(message-this-is-mail t))
+ (unless (assq 'From headers)
+ (push (cons 'From (message-make-from
+ (notmuch-user-name)
+ (notmuch-user-primary-email)))
+ headers))
(message-setup-1 headers yank-action send-actions return-action))
(notmuch-fcc-header-setup)
+ (notmuch-mua--remove-dont-reply-to-names)
(message-sort-headers)
(message-hide-headers)
(set-buffer-modified-p nil)
(notmuch-mua-maybe-set-window-dedicated)
- (message-goto-to))
+ (cond
+ ((and to subject) (message-goto-body))
+ (to (message-goto-subject))
+ (t (message-goto-to))))
(defvar notmuch-mua-sender-history nil)
(with-current-buffer temp-buffer
(erase-buffer)
(let ((coding-system-for-read 'no-conversion))
- (call-process notmuch-command nil t nil
+ (notmuch--call-process notmuch-command nil t nil
"show" "--format=raw" id))
;; Because we process the messages in reverse order,
;; always generate a forwarded subject, then use the
(message-hide-headers)
(set-buffer-modified-p nil))))
-(defun notmuch-mua-new-reply (query-string &optional prompt-for-sender reply-all)
+(defun notmuch-mua-new-reply (query-string &optional prompt-for-sender reply-all duplicate)
"Compose a reply to the message identified by QUERY-STRING.
If PROMPT-FOR-SENDER is non-nil, the user will be prompted for
the From: address first. If REPLY-ALL is non-nil, the message
-will be addressed to all recipients of the source message."
+will be addressed to all recipients of the source message. If
+DUPLICATE is non-nil, based the reply on that duplicate file"
;; `select-active-regions' is t by default. The reply insertion code
;; sets the region to the quoted message to make it easy to delete
;; (kill-region or C-w). These two things combine to put the quoted
(let ((sender (and prompt-for-sender
(notmuch-mua-prompt-for-sender)))
(select-active-regions nil))
- (notmuch-mua-reply query-string sender reply-all)
+ (notmuch-mua-reply query-string sender reply-all duplicate)
(deactivate-mark)))
;;; Checks
(message-send-and-exit arg)
(message-send arg)))))
+;;;###autoload
(defun notmuch-mua-send-and-exit (&optional arg)
(interactive "P")
(notmuch-mua-send-common arg t))
+;;;###autoload
(defun notmuch-mua-send (&optional arg)
(interactive "P")
(notmuch-mua-send-common arg))
+;;;###autoload
(defun notmuch-mua-kill-buffer ()
(interactive)
(message-kill-buffer))
;;; _
+;;;###autoload
(define-mail-user-agent 'notmuch-user-agent
'notmuch-mua-mail
'notmuch-mua-send-and-exit