;;; planner-mutt.el --- mutt E-Mail client support for Planner ;; Copyright (C) 2004 Frederik Fouvry ;; 2005 Jürgen Doser ;; Author: Jürgen Doser ;; Keywords: ;; This file is not part of GNU Emacs. ;; This file 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 2, or (at your option) ;; any later version. ;; This file 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 GNU Emacs; see the file COPYING. If not, write to ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330, ;; Boston, MA 02111-1307, USA. ;;; Commentary: ;; based on planner-unix-mail.el ;; from Frederik Fouvry ;; ;;;_ + Usage ;; ;; Place planner-mutt.el in your load path and add this to your .emacs: ;; ;; (require 'planner-mutt) ;; ;; mutt mailbox URLs are of the form ;; ;; mutt://PATH/TO/MBOX/message-id ;; ;; Annotations will be of the form ;; [[mutt://PATH/TO/MBOX/E1AyTpt-0000JR-LU%40sacha.ateneo.edu][E-mail from Sacha Chua]] ;; ;; CAVEAT: I only tried this with mbox-style E-Mail folders. I have no ;; idea whether the following also works with maildir, imap, or other ;; types of E-Mail folder that mutt supports. ;; ;; You will need some glue code for mutt to work with this: ;; ;; 1. you will need a script script that, given a folder and a message-id, ;; tells mutt to open the corresponding message. ;; This can be done as follows: ;; ;; ------------------------------------------------------------------- ;; #!/bin/sh ;; exec rxvt -e mutt -f $2 -e "push \" ~i $1\"" ;; ------------------------------------------------------------------- ;; ;; This script then can be used as "planner-mutt-command" ;; (use customize to set this variable) ;; ;; 2. you will need a way to create tasks from within mutt. ;; ;; Basically, this is done by another script, that uses emacsclient to ;; evaluate appropriate elisp commands. ;; The script can be: ;; ;; ------------------------------------------------------------------- ;; #/bin/bash ;; ;; PLAN=$1 ;; shift ;; TASK="$*" ;; FROM=`cat /tmp/planner-mutt-from | tr -d "\""` ;; TO=`cat /tmp/planner-mutt-to | tr -d "\""` ;; MID=`cat /tmp/planner-mutt-mid | tr -d " "` ;; FOLDER=`cat /tmp/planner-mutt-folder` ;; ;; emacsclient -e "(planner-mutt-create-task-with-plan-from-mutt \"$PLAN\" \"$TASK\" \"$FROM\" \"$TO\" \"$MID\" \"$FOLDER\")" ;; ;; ------------------------------------------------------------------- ;; ;; This script will generate tasks on the plan page given by the first ;; arguement; the task description is taken from the remaining ;; arguments. ;; ;; Alternatively, you can use the following ;; ;; ------------------------------------------------------------------- ;; #/bin/bash ;; ;; TASK="$*" ;; FROM=`cat /tmp/planner-mutt-from | tr -d "\""` ;; TO=`cat /tmp/planner-mutt-to | tr -d "\""` ;; MID=`cat /tmp/planner-mutt-mid | tr -d " "` ;; FOLDER=`cat /tmp/planner-mutt-folder` ;; ;; emacsclient -e "(planner-mutt-create-task-from-mutt \"$TASK\" \"$FROM\" \"$TO\" \"$MID\" \"$FOLDER\")" ;; ------------------------------------------------------------------ ;; ;; This script will generate tasks on a default (customizable) plan ;; page. ;; ;; To make these scripts work, you need a way to put the appropriate ;; information into the /tmp/planner-mutt-* files (like From:, To:, ;; etc.). ;; ;; FROM, TO, and MID can be done using formail just before you start ;; the script above. So you can configure a macro in your .muttrc like ;; the following: ;; ;; ------------------------------------------------------------------- ;; macro index Y "formail -x Message-ID>/tmp/planner-mutt-midformail -x From:>/tmp/planner-mutt-fromformail -x To:>/tmp/planner-mutt-toplanner-create-task-from-mutt " ;; ------------------------------------------------------------------- ;; ;; and ;; ;;------------------------------------------------------------------- ;; macro pager Y "formail -x Message-ID>/tmp/planner-mutt-midformail -x From:>/tmp/planner-mutt-fromformail -x To:>/tmp/planner-mutt-toplanner-create-task-from-mutt " ;; ------------------------------------------------------------------ ;; ;; This way, pressing the 'Y'-key in the index or the pager will write ;; the necessary information (From, To, and Message-ID) into the ;; /tmp/planner-mutt-* files and prompt you to enter the arguments to ;; 'planner-create-task-from mutt', which should be the name of one of ;; the shell scripts above. ;; ;; Now to the really ugly part :): Finding out the filename of the ;; E-Mail folder: ;; ;; I didn't find a better solution than creating folder-hooks in mutt ;; that write the filename of the folder into the ;; /tmp/planner-mutt-folder file each time a folder is visited. So for ;; each E-Mail folder, I have a folder-hook like the following: ;; ;; -------------------------------------------------------------------- ;; folder-hook /PATH/TO/MAIL/FOLDER$ 'push "echo /PATH/TO/MAIL/FOLDER >/tmp/planner-mutt-folder"' ;; -------------------------------------------------------------------- ;; ;; ;; ;;; Code: (require 'planner) ;;; Code: ;; This is (kind of) a combination of the "mid" and "file" (RFC 1738) ;; protocol. (defgroup planner-mutt nil "Options for integration of the mutt E-Mail client with planner.el." :prefix "planner-mutt-" :group 'planner) (defcustom planner-mutt-protocol-name "mutt" "protocol name used for planner-mutt links." :type 'string :group 'planner-mutt) (defcustom planner-mutt-default-plan-page "TaskPool" "Default plan page for tasks where none is given at time of creation." :type 'string :group 'planner-mutt) (defcustom planner-mutt-command "mutt-message-rxvt" "Command used to start mutt and jump to the appropriate message. The command gets MESSAGE-ID and FOLDER of the E-Mail as first and second argument. For example, mutt-message-rxvt could be a short script like the following: #!/bin/sh exec rxvt -e mutt -f $2 -e \"push \\\" ~i $1\\\"\" " :type 'string :group 'planner-mutt) ;; Encoding according to RFC 1738. (defun planner-url-encode (string &optional reserved) (save-match-data (let ((chars (split-string string "")) (newchars) (not-to-encode (concat "\\(?:[0-9A-Za-z]\\|[$_.+!*'(),-]" (if (and (stringp reserved) (not (string= reserved ""))) (concat "\\|" reserved) "") "\\)"))) (while chars (setq newchars (cons (if (string-match not-to-encode (car chars)) (car chars) (format "%%%x" (string-to-char (car chars)))) newchars) chars (cdr chars))) (eval (cons 'concat (nreverse newchars)))))) ;; Decoding according to RFC 1738 (defun planner-url-decode (string) (save-match-data (let* ((parts (split-string string "%")) (newparts) (ignore)) (unless (string-match "^%" string) (setq ignore (car parts) parts (cdr parts))) (while parts (when (string-match "^\\(..\\)" (car parts)) (setq newparts (cons (replace-match (char-to-string (string-to-number (match-string 1 (car parts)) 16)) nil t (car parts) 1) newparts))) (setq parts (cdr parts))) (eval (cons 'concat (append (when ignore (list ignore)) (nreverse newparts))))))) (defun planner-mutt-create-task-with-plan-from-mutt (plan title from to mid folder) (save-match-data (let ((plan-page (or plan planner-mutt-default-plan-page)) (annotation (emacs-wiki-make-link (concat planner-mutt-protocol-name "://" folder "/" (if (string-match "^<\\(.+\\)>$" mid) (planner-url-encode (match-string 1 mid) "[/]") (error "Mal-formed Message-Id header field encountered"))) (if (and planner-ignored-from-addresses (string-match planner-ignored-from-addresses from) (to)) ; May be missing (concat "E-mail to " (planner-get-name-from-address to)) (concat "E-mail from " (planner-get-name-from-address from)))))) (planner-create-task title nil annotation plan-page))) ) (defun planner-mutt-create-task-from-mutt (title from to mid folder) (planner-mutt-create-task-with-plan-from-mutt nil title from to mid folder) ) ;;;###autoload (defun planner-mutt-browse-url (url) "If this is a MUTT MAIL URL, jump to it." (save-match-data (when (string-match (concat "^" planner-mutt-protocol-name "://\\(.+\\)/\\(.+?\\)$") url) (let* ((message-id (planner-url-decode (match-string 2 url))) (folder (match-string 1 url)) (args (concat message-id " " folder " &"))) (start-process-shell-command "mutt" nil planner-mutt-command args))))) (planner-add-protocol planner-mutt-protocol-name 'planner-mutt-browse-url nil) (planner-update-wiki-project) (provide 'planner-mutt)