Teaching Emacs how to open mail attachments


On Windows Emacs is a bit handicapped, because it does not know how to operate on many file types. One solution is to write your own .mailcap file, which Emacs will happily parse.

# plays AIFF files
audio/x-aiff; sfplay %s

# plays AIFC files
audio/x-aifc; sfplay %s

# open PDFs
application/pdf; xpdf %s

Unfortunately, this only allows you to specify external programs, not actions within Emacs, and it is yet another configuration file to handle.

A different solution is to directly configure Emacs' MIME environment. You need to configure two variables

  • mailcap-mime-data is a tree of associations from MIME types and subtypes, to viewers and flags. This is where .mailcap information lands.
  • mailcap-mime-extensions is an association list between file name extensions and MIME types. Unfortunately, Emacs does not always respect the MIME types in attachments and sometimes exclusively guesses the type from the name.

My solution

Because of the clash between functions that focus on the file name extension and others that look for MIME types, it has taken me an inordinate amount of time to find the right way to configure these options. I believe I have found a simple path to keep everything configured from within Emacs, but I am still looking for an option that teaches Emacs to delegate to Windows' routines for opening all documents.

The code below contains some example configurations that I use in my system, and which are likely to grow. Note that I use doc-view. See my other posts to learn how to configure DocView for Emacs on Windows. Even with those changes, we still need to tweak doc-view to make it work with MIME buffers, because they have no filename and doc-view has no way to identify the document type—hence the use of the odf-doc-view-mode wrapper.

(use-package mailcap
  (mailcap-user-mime-data nil)
  (defvar jjgr-mailcap-additional-application-types
       "start \"%s\""
       "\"C:/Program Files (x86)/Microsoft Office/root/Office16/WINWORD.EXE\" \"%s\""
       "\"C:/Program Files (x86)/Microsoft Office/root/Office16/POWERPNT.EXE\" \"%s\""
       "\"C:/Program Files (x86)/Microsoft Office/root/Office16/EXCEL.EXE\" \"%s\""

  (defun odf-doc-view-mode ()
    (require 'doc-view)
    (let ((doc-view-saved-settings (cons '(doc-view-doc-type . odf) doc-view-saved-settings)))
      (print doc-view-doc-type)))

  (defun jjgr-add-simple-mailcap-entry (subtypes viewer &rest extensions)
    (when subtypes
      (dolist (subtype subtypes)
        (let ((regexp (replace-regexp-in-string "\\." "\\\\." subtype))
              (type (concat "application/" subtype)))
          (mailcap-add-mailcap-entry "application" regexp
                                     `((viewer . ,viewer)
                                       (type . ,type)))))
      (dolist (extension extensions)
        (add-to-list 'mailcap-mime-extensions (cons extension (concat "application/" (car subtypes)))))))

  (dolist (i jjgr-mailcap-additional-application-types)
    (apply 'jjgr-add-simple-mailcap-entry i)))

Important bonus

On top of this, you will want to make sure that Emacs' MIME parser stores the attachments with the right encoding, free of strange characters, on a temporary location that is compatible with Windows' expectations

(use-package mm-decode
  ;; Clean up file names, removing accents and spaces, so that the operating
  ;; system does not get confused.
  (add-to-list 'mm-file-name-rewrite-functions 'xah-asciify-string)
  (add-to-list 'mm-file-name-rewrite-functions 'mm-file-name-replace-whitespace)