;;; 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)