From c3f30ee2046f4a2caeb009565ea20e977af00990 Mon Sep 17 00:00:00 2001 From: Paul Nelson Date: Mon, 7 Jul 2025 22:18:39 +0200 Subject: [PATCH] Add post-save actions for Rmail attachments * lisp/mail/rmailmm.el (rmail-mime-save-action): New user option. (rmail-mime-save): Use it. * doc/emacs/rmail.texi (Rmail Display): Document it (bug#78971). --- doc/emacs/rmail.texi | 21 +++++++++++++++++++++ etc/NEWS | 7 +++++++ lisp/mail/rmailmm.el | 38 +++++++++++++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/doc/emacs/rmail.texi b/doc/emacs/rmail.texi index 08add98b733..70fabace693 100644 --- a/doc/emacs/rmail.texi +++ b/doc/emacs/rmail.texi @@ -1229,6 +1229,27 @@ summarizes the part's index, size, and content type. Depending on the content type, it may also contain one or more buttons; these perform actions such as saving the part into a file. +@vindex rmail-mime-save-action + When you save a MIME attachment to a file, Rmail can perform follow-up +actions according to the value of @code{rmail-mime-save-action}. By +default, this value is @code{nil}, which means Rmail does nothing. +Other predefined values are: + +@table @code +@item visit-file +Visit the saved file in Emacs. + +@item visit-directory +Visit the file's directory in Dired and move point to the file. + +@item open-external +Open the file with an external program appropriate for its type. +@end table + +You can also set @code{rmail-mime-save-action} to a custom function. +This function will be called with the absolute filename of the saved +attachment as its only argument. + @table @kbd @findex rmail-mime-toggle-hidden @item @key{RET} diff --git a/etc/NEWS b/etc/NEWS index 5ca0642b615..8d41efdaf64 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1344,6 +1344,13 @@ replies. 'rmail-re-abbrevs'. 'rmail-re-abbrevs' is still honored if it was already set. ++++ +*** New user option 'rmail-mime-save-action'. +This option specifies an action to take after saving a MIME attachment. +Predefined values include visiting the file in Emacs, jumping to the +file in Dired, or opening the file with an external program. You can +also provide a custom function. + ** Message +++ diff --git a/lisp/mail/rmailmm.el b/lisp/mail/rmailmm.el index aa38cbf14d5..173f95fd325 100644 --- a/lisp/mail/rmailmm.el +++ b/lisp/mail/rmailmm.el @@ -242,6 +242,27 @@ TRUNCATED is non-nil if the text of this entity was truncated.")) ;;; Buttons +(defcustom rmail-mime-save-action nil + "Action to perform after saving a MIME attachment in Rmail. +The value can be one of the following: + +- nil: Do nothing (default). +- `visit-file': Visit the saved file in Emacs. +- `visit-directory': Visit the file's directory in Dired. +- `open-external': Open the file with an external program. +- A function: Call the function with the absolute filename as argument. + +Email attachments can be dangerous. When this variable is set to one of +the predefined actions, the user will be prompted to confirm the action +before it is performed. If you set this variable to a function, it will +be called without confirmation. Please exercise caution." + :type '(choice (const :tag "Do nothing" nil) + (const :tag "Visit file in Emacs" visit-file) + (const :tag "Visit directory in Dired" visit-directory) + (const :tag "Open with external program" open-external) + (function :tag "Custom function")) + :version "31.1") + (defun rmail-mime-save (button) "Save the attachment using info in the BUTTON." (let* ((rmail-mime-mbox-buffer rmail-view-buffer) @@ -282,7 +303,22 @@ TRUNCATED is non-nil if the text of this entity was truncated.")) (ignore-errors (base64-decode-region (point-min) (point-max)))) ((string= transfer-encoding "quoted-printable") (quoted-printable-decode-region (point-min) (point-max)))))) - (write-region nil nil filename nil nil nil t)))) + (write-region nil nil filename nil nil nil t)) + (pcase rmail-mime-save-action + ('nil nil) + ('visit-file + (when (yes-or-no-p (format "Visit attachment `%s' in Emacs? " + (file-name-nondirectory filename))) + (find-file filename))) + ('visit-directory + (when (yes-or-no-p (format "Visit attachment `%s' in Dired? " + (file-name-nondirectory filename))) + (dired-jump nil filename))) + ('open-external + (when (yes-or-no-p (format "Open attachment `%s' with external program? " + (file-name-nondirectory filename))) + (shell-command-do-open (list filename)))) + ((pred functionp) (funcall rmail-mime-save-action filename))))) (define-button-type 'rmail-mime-save 'action 'rmail-mime-save)