;; Copyright (C) 2014 Baptiste Fouques
;;
;; Author: Baptiste Fouques
;; Created: February 2014
;; Version: 1.0
;; Keywords: notmuch openssl smime encryption signature
;;
;; Notmuch-Openssl for Emacs 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 3, or (at your option) any later
;; version.
;;
;; Notmuch-Openssl for Emacs 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.
;; Options
;; customization is found in the groupe 'notmuch-openssl'
;; M-x customize-group notmuch-openssl
This package provides operations :
- openssl-create-sigstatus
- From a notmuch sexp representation of a message and a part, create relevant
:sigstatus
if message has been signed through x509 mechanism. Typically to be launch at construction of notmuch-show buffer.
It adds openssl-create-sigstatus as before advice to notmuch-show-insert-part-multipart/signed.
No Elisp dependency. Though there is no use whithout using notmuch as mail client.
It suppose that you have a working openssl environment. Openssl executable may be specified in custom group.
(defgroup notmuch-openssl nil "Openssl binding for Notmuch")
(defcustom notmuch-openssl/openssl-command "openssl"
"Command to call openssl base executable. Could be short if in $PATH, or full path."
:group 'notmuch-openssl
:type 'string)
(defcustom notmuch-openssl/save-received-cert "~/Mail/certs"
"If non nil, save received S/Mime certificate (in valid signed messages for example) in given path"
:group 'notmuch-openssl
:type '(restricted-sexp :match-alternatives (stringp 'nil)))
(defun notmuch-openssl/create-sigstatus-from-openssl (msg part content-type nth depth button)
"If message is signed and sigstatus is nil, try to create sigstatus with openssl"
(let ((tag-signed (member "signed" (plist-get msg :tags)))
(signed (plist-member part :sigstatus))
(sigstatus (plist-get part :sigstatus)))
(cond ((and tag-signed signed (or (not sigstatus) (equal sigstatus '((:status "error" :errors 2)))))
(let* ((filename (plist-get msg :filename))
(openssl-usercert-file (make-temp-file "openssl-signer"))
(args (list "smime" "-verify" "-in" filename "-signer" openssl-usercert-file "-out" "/dev/null"))
(args-noverify (list "smime" "-verify" "-noverify" "-in" filename "-signer" openssl-usercert-file "-out" "/dev/null"))
(good "")
(self "")
(new_status '()))
(with-temp-buffer
(apply #'call-process notmuch-openssl/openssl-command nil '(t t) nil args)
(delete-char -1)
(setq good (string= (buffer-string) "Verification successful")))
(with-temp-buffer
(apply #'call-process notmuch-openssl/openssl-command nil '(t t) nil args-noverify)
(delete-char -1)
(setq self (string= (buffer-string) "Verification successful")))
(cond
((or good self)
(setq new_status (plist-put new_status :status "good"))
(let ((args-email (list "x509" "-email" "-noout" "-in" openssl-usercert-file))
(args-serial (list "x509" "-serial" "-noout" "-in" openssl-usercert-file ))
(email "")
(serial ""))
(with-temp-buffer
(notmuch-check-exit-status (apply #'call-process notmuch-openssl/openssl-command nil '(t t) nil args-email)
(cons notmuch-openssl/openssl-command args)
(buffer-string))
(delete-char -1)
(setq email (buffer-string)))
(with-temp-buffer
(notmuch-check-exit-status (apply #'call-process notmuch-openssl/openssl-command nil '(t t) nil args-serial)
(cons notmuch-openssl/openssl-command args)
(buffer-string))
(delete-char -1) (goto-char (point-min)) (delete-char 7)
(setq serial (buffer-string)))
(if notmuch-openssl/save-received-cert
(let ((save-cert-filename
(concat (file-name-as-directory notmuch-openssl/save-received-cert) email "-" serial ".pem")))
(copy-file openssl-usercert-file save-cert-filename t)
(setq new_status (plist-put new_status :cert-file save-cert-filename))))
(if good
(setq new_status (plist-put new_status :userid (concat email " - " serial)))
(setq new_status (plist-put new_status :fingerprint (concat serial " self signed for " email))))))
((setq new_status (plist-put new_status :status "error"))))
(setq part (plist-put part :sigstatus (list new_status)))
(delete-file openssl-usercert-file)))
(nil))))
(defadvice notmuch-show-insert-part-multipart/signed (before notmuch-openssl/advice-create-sigstatus-from-openssl
(msg part content-type nth depth button))
(notmuch-openssl/create-sigstatus-from-openssl msg part content-type nth depth button))
(ad-activate 'notmuch-show-insert-part-multipart/signed nil)
(defun notmuch-openssl/sigstatus-good-callback (button)
(let* ((sigstatus (button-get button :notmuch-sigstatus))
(cert-file (expand-file-name (plist-get sigstatus :cert-file)))
(buffer (get-buffer-create "*notmuch-crypto-gpg-out*")))
(with-current-buffer buffer
(goto-char (point-max))
(if cert-file
(call-process notmuch-openssl/openssl-command nil '(t t) t "x509" "-in" cert-file "-text")))))
(defadvice notmuch-crypto-sigstatus-good-callback (after notmuch-openssl/advice-sigstatus-good-callback (button))
(notmuch-openssl/sigstatus-good-callback button))
(ad-activate 'notmuch-crypto-sigstatus-good-callback nil)
(provide 'notmuch-openssl)