summaryrefslogtreecommitdiff
path: root/mu4e/mu4e-contrib.el
blob: d76306412719589e60de9961faacf68841bd5c19 (plain)
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
;;; mu4e-contrib.el --- User-contributed functions -*- lexical-binding: t -*-

;; Copyright (C) 2013-2026 Dirk-Jan C. Binnema

;; This file is not part of GNU Emacs.

;; mu4e 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.

;; mu4e 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 mu4e.  If not, see <http://www.gnu.org/licenses/>.

;;; Commentary:

;; Some user-contributed functions for mu4e

;;; Code:

(require 'mu4e-headers)
(require 'mu4e-view)
(require 'bookmark)
(require 'eshell)


;;; Various simple commands
(defun mu4e-headers-mark-all-unread-read ()
  "Put a ! \(read) mark on all visible unread messages."
  (interactive)
  (mu4e-headers-mark-for-each-if
   (cons 'read nil)
   (lambda (msg _param)
     (memq 'unread (mu4e-msg-field msg :flags)))))

(defun mu4e-headers-flag-all-read ()
  "Flag all visible messages as \"read\"."
  (interactive)
  (mu4e-headers-mark-all-unread-read)
  (mu4e-mark-execute-all t))

(defun mu4e-headers-mark-all ()
  "Mark all headers for some action.
Ask user what action to execute."
  (interactive)
  (mu4e-headers-mark-for-each-if
   (cons 'something nil)
   (lambda (_msg _param) t))
  (mu4e-mark-execute-all))



;;; Bogofilter/SpamAssassin
;;
;; Support for handling spam with Bogofilter with the possibility
;; to define it for SpamAssassin, contributed by Gour.
;;
;; To add the actions to the menu, you can use something like:
;;
;; (add-to-list 'mu4e-headers-actions
;;              '("sMark as spam" . mu4e-register-msg-as-spam) t)
;; (add-to-list 'mu4e-headers-actions
;;              '("hMark as ham" . mu4e-register-msg-as-ham) t)

(defvar mu4e-register-as-spam-cmd nil
  "Command for invoking spam processor to register message as spam.
For example for bogofilter, use \"/usr/bin/bogofilter -Ns < %s\"")

(defvar mu4e-register-as-ham-cmd nil
  "Command for invoking spam processor to register message as ham.
For example for bogofile, use \"/usr/bin/bogofilter -Sn < %s\"")

(defun mu4e-register-msg-as-spam (msg)
  "Register MSG  as spam."
  (interactive)
  (let* ((path (shell-quote-argument (mu4e-message-field msg :path)))
         (command (format mu4e-register-as-spam-cmd path)))
    (shell-command command))
  (mu4e-mark-at-point 'delete nil))

(defun mu4e-register-msg-as-ham (msg)
  "Register MSG as ham."
  (interactive)
  (let* ((path (shell-quote-argument(mu4e-message-field msg :path)))
         (command (format mu4e-register-as-ham-cmd path)))
    (shell-command command))
  (mu4e-mark-at-point 'something nil))

;; (add-to-list 'mu4e-view-actions
;;              '("sMark as spam" . mu4e-view-register-msg-as-spam) t)
;; (add-to-list 'mu4e-view-actions
;;              '("hMark as ham" . mu4e-view-register-msg-as-ham) t)

(defun mu4e-view-register-msg-as-spam (msg)
  "Register MSG as spam (view mode)."
  (interactive)
  (let* ((path (shell-quote-argument (mu4e-message-field msg :path)))
         (command (format mu4e-register-as-spam-cmd path)))
    (shell-command command))
  (mu4e-view-mark-for-delete))

(defun mu4e-view-register-msg-as-ham (msg)
  "Mark MSG as ham (view mode)."
  (interactive)
  (let* ((path (shell-quote-argument(mu4e-message-field msg :path)))
         (command (format mu4e-register-as-ham-cmd path)))
    (shell-command command))
  (mu4e-view-mark-for-something))


;;; Eshell functions
;;
;; Code for `gnus-dired-attached' modified to run from eshell,
;; allowing files to be attached to an email via mu4e using the
;; eshell.  Does not depend on gnus.


(defun mu4e--active-composition-buffers ()
  "Return all active mu4e composition buffers."
  (let (buffers)
    (save-excursion
      (dolist (buffer (buffer-list t))
        (set-buffer buffer)
        (when (eq major-mode 'mu4e-compose-mode)
          (push (buffer-name buffer) buffers))))
    (nreverse buffers)))



;; backward compat until 27.1 is univeral.
(defalias 'mu4e--flatten-list
  (if (fboundp 'flatten-list)
      #'flatten-list
    (with-no-warnings
      #'eshell-flatten-list)))

;; backward compat ntil 28.1 is universal.
(defalias 'mu4e--mm-default-file-type
  (if (fboundp 'mm-default-file-type)
      #'mm-default-file-type
    (with-no-warnings
      #'mm-default-file-encoding)))

(defun eshell/mu4e-attach (&rest args)
  "Attach files to a mu4e message using eshell with ARGS.
If no mu4e buffers found, compose a new message and then attach
the file."
  (let ((destination nil)
        (files-str nil)
        (bufs nil)
        ;; Remove directories from the list
        (files-to-attach
         (seq-keep
          (lambda (f) (when (and (file-exists-p f)
                                (not (file-directory-p f)))
                        (expand-file-name f)))
          (mu4e--flatten-list (reverse args)))))
    ;; warn if user tries to attach without any files marked
    (if (null files-to-attach)
        (error "No files to attach")
      (setq files-str
            (mapconcat
             (lambda (f) (file-name-nondirectory f))
             files-to-attach ", "))
      (setq bufs (mu4e--active-composition-buffers))
      ;; set up destination mail composition buffer
      (if (and bufs
               (y-or-n-p "Attach files to existing mail composition buffer? "))
          (setq destination
                (if (length= bufs 1)
                    (get-buffer (car bufs))
                  (let ((prompt (mu4e-format "%s" "Attach to buffer")))
                    (substring-no-properties
                     (funcall mu4e-completing-read-function prompt
                              bufs)))))
        ;; setup a new mail composition buffer
        (if (y-or-n-p "Compose new mail and attach this file? ")
            (progn (mu4e-compose-new)
                   (setq destination (current-buffer)))))
      ;; if buffer was found, set buffer to destination buffer, and attach files
      (if (not (eq destination 'nil))
          (progn (set-buffer destination)
                 (goto-char (point-max)) ; attach at end of buffer
                 (while files-to-attach
                   (mml-attach-file (car files-to-attach)
                                    (or (mu4e--mm-default-file-type
                                         (car files-to-attach))
                                        "application/octet-stream") nil)
                   (setq files-to-attach (cdr files-to-attach)))
                 (message "Attached file(s) %s" files-str))
        (message "No buffer to attach file to.")))))

;;; _
(provide 'mu4e-contrib)
;;; mu4e-contrib.el ends here