From 253d03aaaafede0e218fda5fd8a349703bb24a46 Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Tue, 28 Feb 2012 22:34:22 +0200 Subject: [PATCH] * 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 --- emacs/TODO | 8 ++-- emacs/mu4e-main.el | 34 ++++++++++---- emacs/mu4e.el | 111 ++++++++++++++++++++++++--------------------- emacs/mu4e.texi | 99 ++++++++++++++++++++++++++++++++-------- 4 files changed, 170 insertions(+), 82 deletions(-) diff --git a/emacs/TODO b/emacs/TODO index 945c52ec..7a743c18 100644 --- a/emacs/TODO +++ b/emacs/TODO @@ -4,19 +4,15 @@ ** Bugs *** database locks (solved?) -*** sometimes +S-u-n does not seem to work? ** Features i -*** documentation *** mu4e-get-sub-maildirs -*** auto-mail check *** extract mailing list name *** mark thread *** bounce support *** sorting *** tool bars -*** colorize cited parts in view *** refiling-by-pattern *** window management *** inspect message (muile) @@ -62,6 +58,10 @@ ** make links clickable ** integrate with org-contacts ** 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: # mode: org; org-startup-folded: nil diff --git a/emacs/mu4e-main.el b/emacs/mu4e-main.el index 41fdd13a..203bcb5b 100644 --- a/emacs/mu4e-main.el +++ b/emacs/mu4e-main.el @@ -31,6 +31,7 @@ (defconst mu4e-main-buffer-name "*mu4e-main*" "*internal* Name of the mm main buffer.") + (defvar mu4e-main-mode-map (let ((map (make-sparse-keymap))) @@ -42,7 +43,7 @@ (define-key map "m" 'mu4e-toggle-mail-sending-mode) (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) map) @@ -124,7 +125,7 @@ clicked." (propertize " Misc\n\n" 'face 'mu4e-title-face) (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 (if (file-directory-p smtpmail-queue-dir) @@ -148,10 +149,28 @@ clicked." ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Interactive functions -(defun mu4e-retrieve-mail-update-db () - "Get new mail and update the database." +(defconst mu4e-update-buffer-name "*mu4e-update*" + "*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) - (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 () "Toggle sending mail mode, either queued or direct." @@ -160,9 +179,8 @@ clicked." (error "`smtp-queue-dir' does not exist")) (setq smtpmail-queue-mail (not smtpmail-queue-mail)) (message - (if smtpmail-queue-mail - "Outgoing mail will now be queued" - "Outgoing mail will now be sent directly")) + (concat "Outgoing mail will now be " + (if smtpmail-queue-mail "queued" "sent directly"))) (mu4e-main-view)) (provide 'mu4e-main) diff --git a/emacs/mu4e.el b/emacs/mu4e.el index 22170e5e..997637b6 100644 --- a/emacs/mu4e.el +++ b/emacs/mu4e.el @@ -69,6 +69,15 @@ PATH, you can specify the full path." :group 'mu4e :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 "~/") "Default directory for saving attachments." :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 argument (C-u) before invoking the search.") - (defvar mu4e-debug nil "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 - '( (:date . 25) - (:flags . 6) - (:from . 22) - (:subject . nil)) + '( (:date . 25) + (:flags . 6) + (:from . 22) + (:subject . nil)) "A list of header fields to show in the headers buffer, and their respective widths in characters. A width of `nil' means 'unrestricted', and this is best reserved fo the rightmost (last) - field. For the complete list of available headers, see `mu4e-header-names'" - :type (list 'symbol) - :group 'mu4e-headers) + field. For the complete list of available headers, see + `mu4e-header-names'" + :type (list 'symbol) + :group 'mu4e-headers) (defcustom mu4e-headers-date-format "%x %X" "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) (defcustom mu4e-headers-leave-behavior 'ask - "What do to when user leaves the headers view (e.g. quit or doing - a new search). Value is one of the following symbols: - - ask (ask the user whether to ignore the marks) - - apply (automatically apply the marks before doing anything else) - - ignore (automatically ignore the marks without asking)." + "What to do when user leaves the headers view (e.g. quits, + refreshes or does a new search). Value is one of the following + symbols: +- ask (ask the user whether to ignore the marks) +- apply (automatically apply the marks before doing anything else) +- ignore (automatically ignore the marks without asking)." :type 'symbol :group 'mu4e-headers) @@ -394,6 +404,9 @@ dir already existed, or has been created, nil otherwise." (unless (mu4e-create-maildir-maybe path) (error "%s (%S) does not exist" path var))))) +(defvar mu4e-update-timer nil + "*internal* The mu4e update timer.") + (defun mu4e () "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 @@ -413,10 +426,26 @@ server has the expected values." (error "mu server has version %s, but we need %s" version mu4e-mu-version)) (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" doccount (if (= doccount 1) "" "s")))) (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) "List the maildirs under PARENTDIR." ;; TODO: recursive? (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 uppercased first letters of these flags, as per [1]. Other flags than the ones listed here are ignored. - Also see `mu4e-flags-to-string'. - \[1\]: http://cr.yp.to/proto/maildir.html" (when 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. (replace-regexp-in-string "[  ]" " " body nil nil nil))) -(defconst mu4e-get-mail-name "*mu4e-get-mail*" - "*internal* Name of the process to retrieve mail") +(defconst mu4e-update-mail-name "*mu4e-update-mail*" + "*internal* Name of the process to update mail") -(defun mu4e-retrieve-mail-update-db-proc (buf) - "Try to retrieve mail (using `mu4e-get-mail-command'), with -output going to BUF if not nil, or discarded if nil. After -retrieving mail, update the database." +(defun mu4e-update-mail (&optional buf) + "Update mail (retrieve using `mu4e-get-mail-command' and update +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." (unless mu4e-get-mail-command (error "`mu4e-get-mail-command' is not defined")) - (message "Retrieving mail...") - (let* ((proc (start-process-shell-command - mu4e-get-mail-name buf mu4e-get-mail-command))) + (let* ((process-connection-type t) + (proc (start-process-shell-command + mu4e-update-mail-name buf mu4e-get-mail-command))) + (message "Retrieving mail...") (set-process-sentinel proc (lambda (proc msg) + (message nil) + (mu4e-proc-index mu4e-maildir) (let ((buf (process-buffer proc))) - (when (buffer-live-p buf) - (mu4e-proc-index mu4e-maildir) - (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) - (mu4e-retrieve-mail-update-db-proc buf)))) + (when (buffer-live-p buf) + (kill-buffer buf))))) + (set-process-query-on-exit-flag proc t))) (defun mu4e-display-manual () @@ -658,15 +674,6 @@ top level if there is none." ('mu4e-view-mode "(mu4e)Message view") (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))) - - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (provide 'mu4e) diff --git a/emacs/mu4e.texi b/emacs/mu4e.texi index 1dc0ab42..8a33140a 100644 --- a/emacs/mu4e.texi +++ b/emacs/mu4e.texi @@ -37,7 +37,7 @@ tested on Debian GNU/Linux. * Getting started:: * Running mu4e:: * Searching mail:: -* Org-mode support:: +* Interaction with other tools:: * Example configuration:: * FAQ - Frequently Anticipated Questions:: * 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 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 @section Indexing your messages @@ -405,8 +416,7 @@ Finally, there are some @emph{Misc} actions: @itemize @item @t{[U]pdate email & database} will execute whatever is in the variable @code{mu4e-get-mail-command}, and afterwards update the @t{mu} -database; @pxref{Indexing your messages}. This is a synchronous command - you -have to wait for it to finish. +database; @pxref{Indexing your messages}. See @ref{Getting mail} for details. @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 @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 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 @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 by typing @key{ma}. -@node Org-mode support -@chapter Org-mode support +@node Interaction with other tools +@chapter Interaction with other tools -Many emacs-users use @t{org-mode} for their note-taking, agenda, to-do list -and many other things, so it is useful to integrate e-mail with this as well. - -@t{mu4e} support @t{org-mode}-links, and the @t{org-mode}-address book, -@t{org-contact}. +In this chapter we discuss some ways in ways in which @t{mu4e} can cooperate +with other tools. @menu -* Org-mode links:: -* Org-contacts:: +* Creating org-mode links:: +* Maintaining an address-book with org-contacts:: +* Getting new mail notifications with Sauron:: @end menu -@node Org-mode links -@section Org-mode links +@node Creating org-mode links +@section Creating org-mode links 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 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 org-open-at-point} elsewhere - both are typically bound to @kbd{C-c C-o}. -@node Org-contacts -@section Org-contacts +@node Maintaining an address-book with org-contacts +@section Maintaining an address-book with org-contacts To manage your addresses using @t{org-mode}, there is @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 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 @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 @emph{all} selected messages @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 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