;;; w32-send-mapi.el --- send e-mail messages using MAPI

;; Copyright (C) 2005 Mathias Dahl

;; Version: 0.3
;; Keywords: w32, e-mail, vbscript, MAPI
;; Author: Mathias Dahl <mathias.removethis.dahl@gmail.com>
;; Maintainer: Mathias Dahl
;; URL: http://www.emacswiki.org/cgi-bin/wiki/WThirtyTwoSendMAPI

;; This file is not part of GNU Emacs.

;; This 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 is distributed in the hope that it will be useful, but WITHOUT
;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
;; 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:

;; Even though I have to use MS Outlook at work I like writing my
;; e-mails in a normal *mail* buffer in Emacs. I usually copy and
;; paste the text from there into the mail. One day I got tired of
;; that and hacked together this little package.
;; It uses MAPI to send a message by fetching information from a
;; normal *mail* buffer to build some vbscript code that is executed
;; by wscript.exe.

;;; Prerequisites:

;; * MAPI must be working on your machine
;; * It needs wscript.exe to be able to execute the vbscript doing the
;;   actual work.

;;; Usage:

;; Open up a mail buffer (C-x m).
;; Add one or more recipients, a subject and a body.
;; Execute it: M-x w32-send-mapi.

;;; History:

;; Version 0.3, 2007-07-04
;; * Added w32-send-mapi-send
;; * Added w32-send-mapi-send-region

;; Version 0.2, 2005-03-07
;; * Fixed some things after suggestions from Reiner Steib.
;; * Used `format' instead of a lot of calls to `insert'.
;; * Replaced "Chr(13) & Chr(10)" with "vbCrLf" in generated code

;; Version 0.1, 2005-03-07
;; * First release.

;;; Bugs:

;; Probably a bunch of them.

;;; Todo:

;;; Code:

(require 'message)

(defvar w32-send-mapi-vbscript-template "
Option Explicit
Dim oMess
Dim oSess

Sub Main

  ' To make generated code smaller and (probably) slightly faster
  Dim vbCrLf
  vbCrLf = Chr(13) & Chr(10)

  ' Constant for main recipient
  Const mapToList = 1

  ' Constant for CC
  Const mapCcList = 2

  ' Constant for BCC
  Const mapBccList = 3

  On Error Resume Next

  ' Try to create MAPI session
  Set oSess = CreateObject(\"MSMAPI.MAPISession\")

  If Err.Number <> 0 Then
    MsgBox \"Could not find MAPI. Exiting...\"
    Exit Sub
  End If

  Set oMess = CreateObject(\"MSMAPI.MAPIMessages\")

  On Error GoTo 0

  ' We do not care about new mails right now
  oSess.DownLoadMail = False

  ' Logon MAPI

  ' Set session ID for the message, using our active session
  oMess.SessionID = oSess.SessionID

  ' Start composing message

  ' Subject
  oMess.MsgSubject = \"%s\"

  ' Body
  oMess.MsgNoteText = \"%s\"

  ' For now, just add a normal recipient. For other types of
  ' recipients, use for exampe mapCcList to add CC
  ' recipients. Increase index for each recipient.

  ' Note that you do not need to add a recipient at all for the
  ' MAPI call to work.

  ' Recipient names inserted here

  ' Limitataion:

  ' Note that we make use of only the name, not the e-mail
  ' address. This means we rely on the MUA to expand the names to
  ' valid e-mail addresses before the mail is sent.

  ' Generated recipient-code inserted here:


  ' Catch error which is thrown by the Send method if user
  ' cancels mail

  On Error Resume Next

  ' Open up the mail application

  oMess.Send True

  If Err.Number <> 0 Then
    MsgBox \"Could not send message. Reason: \" & Err.Description
    Exit Sub
  End If

  On Error GoTo 0


End Sub

' Execute main Sub

' Clean up
Set oSess = Nothing
Set oMess = Nothing

  "VBScript code template")

(defun w32-send-mapi-mail-header-recipients ()
  "Fetch a list of strings containing the recipients from the
mail header"
    (goto-char (point-min))
     (cdr (assoc 'to (mail-header-extract-no-properties))) "[,;]")))

(defun w32-send-mapi-mail-header-subject ()
  "Fetch the subject from the mail header."
    (goto-char (point-min))
    (cdr (assoc 'subject (mail-header-extract-no-properties)))))

(defun w32-send-mapi-generate-recipients-code (to-list)
  "Using a list of recipients, create the vbscript code
necessary to add them."
  (let ((count 0)
        (to-list-code nil))
    (if (stringp to-list)
        (setq to-list (list to-list)))
    (while to-list
      (setq to-list-code
            (concat to-list-code
                    "  oMess.RecipType = mapToList\n"
                    "  oMess.RecipIndex = " (int-to-string count) "\n"
                    "  oMess.RecipDisplayName = Trim(\""
                    (car to-list) "\")\n"))
      (setq count (1+ count))
      (setq to-list (cdr to-list)))

(defun w32-send-mapi-send (recipients subject body)
  "Create MAPI message.
RECIPIENTS is a string of one recipient or a list of strings with
recipients. SUBJECT is the subject of the message and BODY the
message body."
  (let ((buf (get-buffer-create "*w32-send-mapi*"))
        (script-file-name (expand-file-name "~/w32-send-mapi.vbs")))
    (set-buffer buf)
    (insert (format w32-send-mapi-vbscript-template subject
                      (insert body)
                      (goto-char (point-min))
                      ;; Escape all quotation characters
                      (replace-string "\"" "\"\"")
                      (goto-char (point-min))
                      ;; Sligthly ugly way to handle newlines (seems to work well
                      ;; though)
                      (replace-string "\n" "\" & vbCrLf & _ \n       \"")
                       (point-min) (point-max)))
                    (if recipients
                        (w32-send-mapi-generate-recipients-code recipients)
                      "' No recipients")))
    ;; Write the script to a file
    (write-file script-file-name)
    (kill-buffer buf)
    (setq script-file-name
          (substitute ?\\ ?/ script-file-name))
    ;; Execute script
    (w32-shell-execute nil "wscript.exe" script-file-name)))

(defun w32-send-mapi ()
  "Send a mail using MAPI using the information in a normal
*mail* buffer. Requires wscript.exe to work."
    (let ((script-file-name (expand-file-name "~/w32-send-mapi.vbs"))
          (case-fold-search t))
      ;; Get recipients and subject
      (setq to-list (w32-send-mapi-mail-header-recipients))
      (setq subject-text (w32-send-mapi-mail-header-subject))
      ;; Body
      (goto-char (point-min))
      (re-search-forward (concat "^" (regexp-quote
      (skip-chars-forward "\n")
      (setq body-text (buffer-substring-no-properties
                       (point) (point-max)))
      (w32-send-mapi-send to-list subject-text body-text))))

(defun w32-send-mapi-send-region (beg end recipients subject)
  "Send region via MAPI."
  (interactive "r\nsRecipients (separate with comma or semicolon): \nsSubject: ")
  (w32-send-mapi-send (split-string recipients "[,;]")
                      (buffer-substring-no-properties beg end)))

(provide 'w32-send-mapi)

;;; w32-send-mapi.el ends here