* automatic mail retrieval (WIP-2):

- mu4e-main.el: mu4e-update-mail-show-window retrieves mail / updates
    database, show output in split window (actual work is done by...:)
  - mu4e.el: mu4e-update-mail retrieves mail, updates database,
    asynchronously. Optionally, show output in a buffer
    when `mu4e-update-interval' is non-nil and some integer, it calls
    mu4e-update-mail every so many seconds.
  - mu4e.texi: document this
  - TODO: updated
This commit is contained in:
Dirk-Jan C. Binnema
2012-02-28 22:34:22 +02:00
parent d6a53ebee2
commit 253d03aaaa
4 changed files with 170 additions and 82 deletions

View File

@ -4,19 +4,15 @@
** Bugs ** Bugs
*** database locks (solved?) *** database locks (solved?)
*** sometimes +S-u-n does not seem to work?
** Features i ** Features i
*** documentation
*** mu4e-get-sub-maildirs *** mu4e-get-sub-maildirs
*** auto-mail check
*** extract mailing list name *** extract mailing list name
*** mark thread *** mark thread
*** bounce support *** bounce support
*** sorting *** sorting
*** tool bars *** tool bars
*** colorize cited parts in view
*** refiling-by-pattern *** refiling-by-pattern
*** window management *** window management
*** inspect message (muile) *** inspect message (muile)
@ -62,6 +58,10 @@
** make links clickable ** make links clickable
** integrate with org-contacts ** integrate with org-contacts
** forward should take the attachments from the original ** forward should take the attachments from the original
** auto-mail check
** colorize cited parts in view
** documentation
** sometimes +S-u-n does not seem to work?
# Local Variables: # Local Variables:
# mode: org; org-startup-folded: nil # mode: org; org-startup-folded: nil

View File

@ -31,6 +31,7 @@
(defconst mu4e-main-buffer-name "*mu4e-main*" (defconst mu4e-main-buffer-name "*mu4e-main*"
"*internal* Name of the mm main buffer.") "*internal* Name of the mm main buffer.")
(defvar mu4e-main-mode-map (defvar mu4e-main-mode-map
(let ((map (make-sparse-keymap))) (let ((map (make-sparse-keymap)))
@ -42,7 +43,7 @@
(define-key map "m" 'mu4e-toggle-mail-sending-mode) (define-key map "m" 'mu4e-toggle-mail-sending-mode)
(define-key map "f" 'smtpmail-send-queued-mail) (define-key map "f" 'smtpmail-send-queued-mail)
(define-key map "U" 'mu4e-retrieve-mail-update-db) (define-key map "U" 'mu4e-update-mail-show-window)
(define-key map "H" 'mu4e-display-manual) (define-key map "H" 'mu4e-display-manual)
map) map)
@ -124,7 +125,7 @@ clicked."
(propertize " Misc\n\n" 'face 'mu4e-title-face) (propertize " Misc\n\n" 'face 'mu4e-title-face)
(mu4e-action-str "\t* [U]pdate email & database\n" (mu4e-action-str "\t* [U]pdate email & database\n"
'mu4e-retrieve-mail-update-db) 'mu4e-update-mail-show-window)
;; show the queue functions if `smtpmail-queue-dir' is defined ;; show the queue functions if `smtpmail-queue-dir' is defined
(if (file-directory-p smtpmail-queue-dir) (if (file-directory-p smtpmail-queue-dir)
@ -148,10 +149,28 @@ clicked."
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interactive functions ;; Interactive functions
(defun mu4e-retrieve-mail-update-db () (defconst mu4e-update-buffer-name "*mu4e-update*"
"Get new mail and update the database." "*internal* Name of the buffer for message retrieval / database
updating.")
(defun mu4e-update-mail-show-window ()
"Try to retrieve mail (using the user-provided shell command),
and update the database afterwards, and show the progress in a
split-window."
(interactive) (interactive)
(mu4e-proc-retrieve-mail-update-db)) (unless mu4e-get-mail-command
(error "`mu4e-get-mail-command' is not defined"))
(let ((buf (get-buffer-create mu4e-update-buffer-name))
(win
(split-window (selected-window)
(- (window-height (selected-window)) 8))))
(with-selected-window win
(switch-to-buffer buf)
(set-window-dedicated-p win t)
(erase-buffer)
(insert "\n") ;; FIXME -- needed so output starts
(mu4e-update-mail buf))))
(defun mu4e-toggle-mail-sending-mode () (defun mu4e-toggle-mail-sending-mode ()
"Toggle sending mail mode, either queued or direct." "Toggle sending mail mode, either queued or direct."
@ -160,9 +179,8 @@ clicked."
(error "`smtp-queue-dir' does not exist")) (error "`smtp-queue-dir' does not exist"))
(setq smtpmail-queue-mail (not smtpmail-queue-mail)) (setq smtpmail-queue-mail (not smtpmail-queue-mail))
(message (message
(if smtpmail-queue-mail (concat "Outgoing mail will now be "
"Outgoing mail will now be queued" (if smtpmail-queue-mail "queued" "sent directly")))
"Outgoing mail will now be sent directly"))
(mu4e-main-view)) (mu4e-main-view))
(provide 'mu4e-main) (provide 'mu4e-main)

View File

@ -69,6 +69,15 @@ PATH, you can specify the full path."
:group 'mu4e :group 'mu4e
:safe 'stringp) :safe 'stringp)
(defcustom mu4e-update-interval nil
"Number of seconds between automatic calls to retrieve mail and
update the database. If nil, don't update automatically. Note,
changes in `mu4e-update-interval' only take effect after restarting
mu4d."
:type 'integer
:group 'mu4e
:safe 'integerp)
(defcustom mu4e-attachment-dir (expand-file-name "~/") (defcustom mu4e-attachment-dir (expand-file-name "~/")
"Default directory for saving attachments." "Default directory for saving attachments."
:type 'string :type 'string
@ -90,7 +99,6 @@ limiting search results speeds up searches significantly, it's
useful to limit this. Note, to ignore the limit, use a prefix useful to limit this. Note, to ignore the limit, use a prefix
argument (C-u) before invoking the search.") argument (C-u) before invoking the search.")
(defvar mu4e-debug nil (defvar mu4e-debug nil
"When set to non-nil, log debug information to the *mu4e-log* buffer.") "When set to non-nil, log debug information to the *mu4e-log* buffer.")
@ -152,16 +160,17 @@ designated shortcut character for the maildir.")
(defcustom mu4e-headers-fields (defcustom mu4e-headers-fields
'( (:date . 25) '( (:date . 25)
(:flags . 6) (:flags . 6)
(:from . 22) (:from . 22)
(:subject . nil)) (:subject . nil))
"A list of header fields to show in the headers buffer, and their "A list of header fields to show in the headers buffer, and their
respective widths in characters. A width of `nil' means respective widths in characters. A width of `nil' means
'unrestricted', and this is best reserved fo the rightmost (last) 'unrestricted', and this is best reserved fo the rightmost (last)
field. For the complete list of available headers, see `mu4e-header-names'" field. For the complete list of available headers, see
:type (list 'symbol) `mu4e-header-names'"
:group 'mu4e-headers) :type (list 'symbol)
:group 'mu4e-headers)
(defcustom mu4e-headers-date-format "%x %X" (defcustom mu4e-headers-date-format "%x %X"
"Date format to use in the headers view, in the format of "Date format to use in the headers view, in the format of
@ -170,11 +179,12 @@ designated shortcut character for the maildir.")
:group 'mu4e-headers) :group 'mu4e-headers)
(defcustom mu4e-headers-leave-behavior 'ask (defcustom mu4e-headers-leave-behavior 'ask
"What do to when user leaves the headers view (e.g. quit or doing "What to do when user leaves the headers view (e.g. quits,
a new search). Value is one of the following symbols: refreshes or does a new search). Value is one of the following
- ask (ask the user whether to ignore the marks) symbols:
- apply (automatically apply the marks before doing anything else) - ask (ask the user whether to ignore the marks)
- ignore (automatically ignore the marks without asking)." - apply (automatically apply the marks before doing anything else)
- ignore (automatically ignore the marks without asking)."
:type 'symbol :type 'symbol
:group 'mu4e-headers) :group 'mu4e-headers)
@ -394,6 +404,9 @@ dir already existed, or has been created, nil otherwise."
(unless (mu4e-create-maildir-maybe path) (unless (mu4e-create-maildir-maybe path)
(error "%s (%S) does not exist" path var))))) (error "%s (%S) does not exist" path var)))))
(defvar mu4e-update-timer nil
"*internal* The mu4e update timer.")
(defun mu4e () (defun mu4e ()
"Start mm. We do this by sending a 'ping' to the mu server "Start mm. We do this by sending a 'ping' to the mu server
process, and start the main view if the 'pong' we receive from the process, and start the main view if the 'pong' we receive from the
@ -413,10 +426,26 @@ server has the expected values."
(error "mu server has version %s, but we need %s" (error "mu server has version %s, but we need %s"
version mu4e-mu-version)) version mu4e-mu-version))
(mu4e-main-view) (mu4e-main-view)
(when mu4e-update-interval
(setq mu4e-update-timer
(run-at-time
0 mu4e-update-interval
'mu4e-update-mail)))
(message "Started mu4e with %d message%s in store" (message "Started mu4e with %d message%s in store"
doccount (if (= doccount 1) "" "s")))) doccount (if (= doccount 1) "" "s"))))
(mu4e-proc-ping))))) (mu4e-proc-ping)))))
(defun mu4e-quit()
"Quit the mm session."
(interactive)
(when (y-or-n-p "Are you sure you want to quit? ")
(message nil)
(when mu4e-update-timer
(cancel-timer mu4e-update-timer))
(mu4e-kill-proc)
(kill-buffer)))
(defun mu4e-get-maildirs (parentdir) (defun mu4e-get-maildirs (parentdir)
"List the maildirs under PARENTDIR." ;; TODO: recursive? "List the maildirs under PARENTDIR." ;; TODO: recursive?
(let* ((files (directory-files parentdir)) (let* ((files (directory-files parentdir))
@ -521,9 +550,7 @@ message files; flags are symbols draft, flagged, new, passed,
replied, seen, trashed and the string is the concatenation of the replied, seen, trashed and the string is the concatenation of the
uppercased first letters of these flags, as per [1]. Other flags uppercased first letters of these flags, as per [1]. Other flags
than the ones listed here are ignored. than the ones listed here are ignored.
Also see `mu4e-flags-to-string'. Also see `mu4e-flags-to-string'.
\[1\]: http://cr.yp.to/proto/maildir.html" \[1\]: http://cr.yp.to/proto/maildir.html"
(when flags (when flags
(let ((kar (case (car flags) (let ((kar (case (car flags)
@ -612,40 +639,29 @@ function prefers the text part, but this can be changed by setting
;; and finally, remove some crap from the remaining string. ;; and finally, remove some crap from the remaining string.
(replace-regexp-in-string "[ (replace-regexp-in-string "[
 ]" " " body nil nil nil)))  ]" " " body nil nil nil)))
(defconst mu4e-get-mail-name "*mu4e-get-mail*" (defconst mu4e-update-mail-name "*mu4e-update-mail*"
"*internal* Name of the process to update mail") "*internal* Name of the process to update mail")
(defun mu4e-retrieve-mail-update-db-proc (buf) (defun mu4e-update-mail (&optional buf)
"Try to retrieve mail (using `mu4e-get-mail-command'), with "Update mail (retrieve using `mu4e-get-mail-command' and update
output going to BUF if not nil, or discarded if nil. After the database afterwards), with output going to BUF if not nil, or
discarded if nil. After retrieving mail, update the database. Note,
function is asynchronous, returns (almost) immediately, and all the
processing takes part in the background, unless buf is non-nil." processing takes part in the background, unless buf is non-nil."
(unless mu4e-get-mail-command (unless mu4e-get-mail-command
(error "`mu4e-get-mail-command' is not defined")) (error "`mu4e-get-mail-command' is not defined"))
(message "Retrieving mail...") (let* ((process-connection-type t)
(let* ((proc (start-process-shell-command (proc (start-process-shell-command
mu4e-update-mail-name buf mu4e-get-mail-command)))
(message "Retrieving mail...") (message "Retrieving mail...")
(set-process-sentinel proc (set-process-sentinel proc
(lambda (proc msg)
(message nil)
(mu4e-proc-index mu4e-maildir) (mu4e-proc-index mu4e-maildir)
(let ((buf (process-buffer proc))) (let ((buf (process-buffer proc)))
(when (buffer-live-p buf) (when (buffer-live-p buf)
(mu4e-proc-index mu4e-maildir) (kill-buffer buf)))))
(kill-buffer buf)))))))
(defun mu4e-retrieve-mail-update-db ()
"Try to retrieve mail (using the user-provided shell command),
and update the database afterwards"
(interactive)
(unless mu4e-get-mail-command
(error "`mu4e-get-mail-command' is not defined"))
(let ((buf (get-buffer-create mu4e-update-buffer-name))
(win
(split-window (selected-window)
(- (window-height (selected-window)) 8))))
(with-selected-window win
(switch-to-buffer buf)
(set-window-dedicated-p win t)
(erase-buffer)
(set-process-query-on-exit-flag proc t))) (set-process-query-on-exit-flag proc t)))
@ -658,15 +674,6 @@ top level if there is none."
('mu4e-hdrs-mode "(mu4e)Headers view") ('mu4e-hdrs-mode "(mu4e)Headers view")
('mu4e-view-mode "(mu4e)Message view") ('mu4e-view-mode "(mu4e)Message view")
(t "mu4e")))) (t "mu4e"))))
(defun mu4e-quit()
"Quit the mm session."
(interactive)
(when (y-or-n-p "Are you sure you want to quit? ")
(message nil)
(mu4e-kill-proc)
(kill-buffer)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -37,7 +37,7 @@ tested on Debian GNU/Linux.
* Getting started:: * Getting started::
* Running mu4e:: * Running mu4e::
* Searching mail:: * Searching mail::
* Org-mode support:: * Interaction with other tools::
* Example configuration:: * Example configuration::
* FAQ - Frequently Anticipated Questions:: * FAQ - Frequently Anticipated Questions::
* Known issues / missing features:: * Known issues / missing features::
@ -180,6 +180,17 @@ of googling should be able to provide you with the details; also there is full
example of setting @t{mu4e} up with @t{offlineimap} and Gmail; @pxref{Gmail example of setting @t{mu4e} up with @t{offlineimap} and Gmail; @pxref{Gmail
configuration}. configuration}.
You can do all of the mail retrieval @emph{outside} of @t{emacs}/@t{mu4e}, but
you can also do it from the @t{mu4e}. For that, set the variable
@code{mu4e-get-mail-command} to the command you want to use for retrieving
mail, which you can then access from the @ref{Main view}.
You can also have this command run periodically (in the background), by
setting the variable @code{mu4e-update-interval} to the number of seconds
between such updates. If set to @code{nil}, it will not update at all. Note
that if you make changes to @code{mu4e-update-interval}, @code{mu4e} must be
restarted before the change will take effect.
@node Indexing your messages @node Indexing your messages
@section Indexing your messages @section Indexing your messages
@ -405,8 +416,7 @@ Finally, there are some @emph{Misc} actions:
@itemize @itemize
@item @t{[U]pdate email & database} will execute whatever is in @item @t{[U]pdate email & database} will execute whatever is in
the variable @code{mu4e-get-mail-command}, and afterwards update the @t{mu} the variable @code{mu4e-get-mail-command}, and afterwards update the @t{mu}
database; @pxref{Indexing your messages}. This is a synchronous command - you database; @pxref{Indexing your messages}. See @ref{Getting mail} for details.
have to wait for it to finish.
@item @t{toggle [m]ail sending mode (direct)} will toggle between sending @item @t{toggle [m]ail sending mode (direct)} will toggle between sending
mail directly, and queuing it first (for example, when you are offline), and mail directly, and queuing it first (for example, when you are offline), and
@t{[f]lush queued mail} will flush any queued mail. This item is visible only @t{[f]lush queued mail} will flush any queued mail. This item is visible only
@ -487,6 +497,12 @@ The two-step mark-execute sequence is similar to what for example @t{dired}
does; @inforef{(emacs) Dired} - it tries to be as fast as possible while still does; @inforef{(emacs) Dired} - it tries to be as fast as possible while still
trying to protect the user from accidents. trying to protect the user from accidents.
When you try to do a new search, or refresh the headers buffer while you still
have marked messages, by default you will be asked what to do with those marks
-- whether to @emph{apply} them before leaving, @emph{ignore} them or to
@emph{cancel} the operation. This behavior can be influenced with the variable
@code{mu4e-headers-leave-behavior} -- see its documentation.
@node Message view @node Message view
@section Message view @section Message view
@ -794,22 +810,20 @@ The same shortcuts are used by the function @code{mu4e-mark-for-move}; so for
example, if you want to move a message the @t{/archive} folder, you can do so example, if you want to move a message the @t{/archive} folder, you can do so
by typing @key{ma}. by typing @key{ma}.
@node Org-mode support @node Interaction with other tools
@chapter Org-mode support @chapter Interaction with other tools
Many emacs-users use @t{org-mode} for their note-taking, agenda, to-do list In this chapter we discuss some ways in ways in which @t{mu4e} can cooperate
and many other things, so it is useful to integrate e-mail with this as well. with other tools.
@t{mu4e} support @t{org-mode}-links, and the @t{org-mode}-address book,
@t{org-contact}.
@menu @menu
* Org-mode links:: * Creating org-mode links::
* Org-contacts:: * Maintaining an address-book with org-contacts::
* Getting new mail notifications with Sauron::
@end menu @end menu
@node Org-mode links @node Creating org-mode links
@section Org-mode links @section Creating org-mode links
It can be useful to include links to e-mail messages or even search queries in It can be useful to include links to e-mail messages or even search queries in
your org-mode files. @t{mu4e} supports this with the @t{org-mu4e} module; you your org-mode files. @t{mu4e} supports this with the @t{org-mu4e} module; you
can set it up by adding it to your configuration: can set it up by adding it to your configuration:
@ -826,8 +840,8 @@ org-insert-link}. Then, you can go to the query or message the link points to
with either @t{M-x org-agenda-open-link} in agenda buffers, or @t{M-x with either @t{M-x org-agenda-open-link} in agenda buffers, or @t{M-x
org-open-at-point} elsewhere - both are typically bound to @kbd{C-c C-o}. org-open-at-point} elsewhere - both are typically bound to @kbd{C-c C-o}.
@node Org-contacts @node Maintaining an address-book with org-contacts
@section Org-contacts @section Maintaining an address-book with org-contacts
To manage your addresses using @t{org-mode}, there is To manage your addresses using @t{org-mode}, there is
@t{org-contacts}@footnote{@url{http://julien.danjou.info/software/org-contacts.el}}. @t{org-contacts}@footnote{@url{http://julien.danjou.info/software/org-contacts.el}}.
@ -844,7 +858,55 @@ You can use it with a @t{capture}-template:
@end verbatim @end verbatim
After setting this up, you can use @t{M-x org-capture RET c} to get a template After setting this up, you can use @t{M-x org-capture RET c} to get a template
for a new contact based on the 'From:' address. for a new contact based on` the 'From:' address.
@node Getting new mail notifications with Sauron
@section Getting new mail notifications with Sauron
The emacs-package Sauron@footnote{Sauron can be found at
@url{https://github.com/djcb/sauron}, or in the Marmalade package-repository
at @url{http://http://marmalade-repo.org/}.} (by the same author) can be used
to get notifications about new mails.
If you put something like the below script in your @t{crontab} (or have some
other way of having it execute every @emph{n} minutes, you will receive
notifications in the sauron-buffer when new messages arrive.
@verbatim
#!/bin/sh
# put the path to your Inbox folder here
CHECKDIR="/home/$LOGNAME/Maildir/Inbox"
sauron-msg () {
DBUS_COOKIE="/home/$LOGNAME/.sauron-dbus"
if test "x$DBUS_SESSION_BUS_ADDRESS" = "x"; then
if test -e $DBUS_COOKIE; then
export DBUS_SESSION_BUS_ADDRESS="`cat $DBUS_COOKIE`"
fi
fi
if test -n "x$DBUS_SESSION_BUS_ADDRESS"; then
dbus-send --session \
--dest="org.gnu.Emacs" \
"/org/gnu/Emacs/Sauron" \
"org.gnu.Emacs.Sauron.AddMsgEvent" \
string:shell uint32:3 string:"$1"
fi
}
for f in `find $CHECKDIR -mmin -2 -a -type f`; do
subject=`$MU view $f | grep '^Subject:' | sed 's/^Subject://'`
sauron-msg "mail: $subject"
done
@end verbatim
Note, you should put something like:
@lisp
(setq sauron-dbus-cookie t)
@end lisp
in your setup, which allows the script to find the D-Bus session bus.
@node Example configuration @node Example configuration
@chapter Example configuration @chapter Example configuration
@ -1105,7 +1167,8 @@ select ('mark' in emacs-speak) the messages; the actions you then take (e.g.,
@key{DEL} for delete, @key{m} for move and @key{t} for trash) will apply to @key{DEL} for delete, @key{m} for move and @key{t} for trash) will apply to
@emph{all} selected messages @emph{all} selected messages
@item @emph{How can I use @t{BBDB}?} Currently, there is no built-in for @item @emph{How can I use @t{BBDB}?} Currently, there is no built-in for
address management with @t{BBDB}; we recommend using @ref{Org-contacts} for now. address management with @t{BBDB}; instead, we recommend @ref{Maintaining an
address-book with org-contacts} for now.
@item @emph{mu4e only seems to return a subset of all matches - how can I get @item @emph{mu4e only seems to return a subset of all matches - how can I get
all?}. Yes, for speed reasons (and because, if you are like the author, you all?}. Yes, for speed reasons (and because, if you are like the author, you
usually don't need thousands of matches), @t{mu4e} returns only up to the usually don't need thousands of matches), @t{mu4e} returns only up to the