Xref: utzoo comp.emacs:6665 gnu.emacs:1411
Path: utzoo!utgpu!watmath!uunet!tut.cis.ohio-state.edu!ucbvax!hplabs!otter!hplb!anorman!ange
From: ange@hplb.hpl.hp.com (Andy Norman)
Newsgroups: comp.emacs,gnu.emacs,hp.emacs
Subject: ange-crypt: simple encrypt / decrypt support for GNU Emacs
Summary: Simple encrypt / decrypt support for GNU Emacs
Keywords: encrypt,decrypt,GNU Emacs,ange-crypt,minor mode
Message-ID: 
Date: 14 Aug 89 22:48:58 GMT
Sender: news@hplb.hpl.hp.com
Organization: Hewlett-Packard Laboratories, Bristol, UK.
Lines: 196

I enclose 'ange-crypt.el' which enables GNU Emacs to encrypt and decrypt files
upon reading and writing.

Enjoy...

P.S. I don't wish to open the worm-can surrounding exportation of crypt from
the USA. If you haven't got crypt, use something different.

--------------------------------------------------------------------------------
; -*-Emacs-Lisp-*-
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; File:         ange-crypt.el
; RCS:          $Header: ange-crypt.el,v 1.6 89/08/14 18:35:50 ange Exp $
; Description:  semi-transparent encrypt / decrypt for files
; Author:       Andy Norman, Kraken
; Created:      Fri Aug 11 16:59:26 1989
; Modified:     Mon Aug 14 18:35:05 1989 (Ange) ange@hplb.hpl.hp.com
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; ange-crypt-mode is a minor mode that can be used with most buffers, and when
;; switched on makes sure that all future writes of the buffer are piped through
;; your favorite encryption command. Visiting a file created by saving a buffer
;; with this mode on will cause the buffer to be initially piped through your
;; favorite decryption command.
;; 
;; Note that by default auto-saving is disabled upon entering ange-crypt-mode so
;; that the plain-text version of the buffer never gets written to file. This
;; action can be disabled by setting the variable ange-crypt-disable-auto-saving
;; to nil.
;;
;; This file is not part of GNU Emacs, but FSF are welcome to it if they want it.
;;
;; Copying is permitted under those conditions described by the GNU General
;; Public License.
;;
;; Copyright (C) 1989 Andy Norman.
;;
;; Author: Andy Norman (ange@hplb.hpl.hp.com)
;;
;; Please mail bugs and suggestions to the author at the above address.

(defvar ange-crypt-encryption-cmd "crypt %s"
  "*The command (with key) to pipe the buffer through to get encryption")

(defvar ange-crypt-decryption-cmd "crypt %s"
  "*The command (with key) to pipe the buffer through to get decryption")

(defvar ange-crypt-disable-auto-saving t
  "*Disable auto-saving when in ange-crypt mode.")

(defvar ange-crypt-key nil
  "The key to encrypt / decrypt a buffer.")

;; Put the next three lines in your .emacs if you wish to have different keys
;; for different buffers.
;;
;; (make-variable-buffer-local 'ange-crypt-key)
;; (setq-default ange-crypt-mode nil)

(defvar ange-crypt-mode nil
  "This buffer is to be read/written encrypted.")

(make-variable-buffer-local 'ange-crypt-mode)
(setq-default ange-crypt-mode nil)

(or (assq 'ange-crypt-mode minor-mode-alist)
    (setq minor-mode-alist (cons '(ange-crypt-mode " Crypt") minor-mode-alist)))

(defun ange-crypt-mode (arg)
  "Toggle ange-crypt mode.
With arg, turn ange-crypt mode on iff arg is positive.
In ange-crypt mode, when the buffer is saved, it is encrypted first,
and when restored, it is decrypted first."
  (interactive "P")
  (setq ange-crypt-mode
	(if (null arg) (not ange-crypt-mode)
	  (> (prefix-numeric-value arg) 0)))
  (if ange-crypt-disable-auto-saving
      (progn
	(delete-auto-save-file-if-necessary)
	(auto-save-mode (not ange-crypt-mode)))) ;enable/disable auto-saves
  (set-buffer-modified-p t))

(defun ange-crypt-get-key ()
  "Prompt the user for the key to encrypt / decrypt the current buffer.
Echos a . for each character typed.
End with , , or .  DEL or backspace rubs out."
  (let ((pass "")
	(c 0)
	(echo-keystrokes 0))
    (while (and (/= c ?\r) (/= c ?\n) (/= c ?\e))
      (message "Enter key to encrypt/decrypt %s: %s"
	       (buffer-name)
	       (make-string (length pass) ?.))
      (setq c (read-char))
      (if (and (/= c ?\b) (/= c ?\177))
	  (setq pass (concat pass (char-to-string c)))
	(if (> (length pass) 0)
	    (setq pass (substring pass 0 -1)))))
    (setq pass (substring pass 0 -1))))

(defun ange-decrypt-buffer ()
  "Decrypts the current buffer if in ange-crypt-mode."
  (if ange-crypt-mode
      (progn
	(let ((mod-p (buffer-modified-p)))
	  (or ange-crypt-key (setq ange-crypt-key (ange-crypt-get-key)))
	  (save-excursion
	    (goto-char (point-max))
	    (search-backward "\n\^L" (max (- (point-max) 3000) (point-min)) 'move)
	    (delete-region (point) (point-max))
	    (message "decrypting...")
	    (shell-command-on-region (point-min)
				     (point-max)
				     (format ange-crypt-decryption-cmd ange-crypt-key)
				     t)
	    (message "decrypting... done"))
	  (if ange-crypt-disable-auto-saving
	      (progn
		(delete-auto-save-file-if-necessary)
		(auto-save-mode 0)))		;disable auto-saving
	  (set-buffer-modified-p mod-p)))))

(defun ange-encrypt-and-write-buffer ()
  "If the buffer is in ange-crypt-mode then encrypt and write to file, otherwise
return nil. This function is mean to be used as a write-file-hook entry."
  (if (not ange-crypt-mode)
      nil				;let basic-save-buffer do the write
    (let ((contents (buffer-substring (point-min) (point-max))))
      (or ange-crypt-key (setq ange-crypt-key (ange-crypt-get-key)))
      (save-excursion
	(message "encrypting...")
	(shell-command-on-region (point-min)
				 (point-max)
				 (format ange-crypt-encryption-cmd ange-crypt-key)
				 t)
	(message "encrypting... done")
	(goto-char (point-max))
	(insert "\n\f\nLocal variables:\nange-crypt-mode:1\nend:\n")
	(if file-precious-flag
	    ;; If file is precious, rename it away before
	    ;; overwriting it.
	    (let ((rename t)
		  (file (concat buffer-file-name "#")))
	      (condition-case ()
		  (progn (rename-file buffer-file-name file t)
			 (setq setmodes (file-modes file)))
		(file-error (setq rename nil)))
	      (unwind-protect
		  (progn (clear-visited-file-modtime)
			 (write-region (point-min) (point-max)
				       buffer-file-name nil t)
			 (setq rename nil))
		;; If rename is still t, writing failed.
		;; So rename the old file back to original name,
		(if rename
		    (progn
		      (rename-file file buffer-file-name t)
		      (clear-visited-file-modtime))
		  ;; Otherwise we don't need the original file,
		  ;; so flush it.
		  (condition-case ()
		      (delete-file file)
		    (error nil)))))
	  ;; If file not writable, see if we can make it writable
	  ;; temporarily while we write it.
	  ;; But no need to do so if we have just backed it up
	  ;; (setmodes is set) because that says we're superseding.
	  (cond ((and tempsetmodes (not setmodes))
		 ;; Change the mode back, after writing.
		 (setq setmodes (file-modes buffer-file-name))
		 (set-file-modes buffer-file-name 511)))
	  (write-region (point-min) (point-max) 
			buffer-file-name nil t)
	  (erase-buffer)
	  (insert contents)
	  (set-buffer-modified-p nil))
	t					;have done the write already
      ))))

(or (memq 'ange-encrypt-and-write-buffer  write-file-hooks)
    (setq write-file-hooks
	  (append write-file-hooks
		  (list 'ange-encrypt-and-write-buffer)))) ;stick it on the end

(or (memq 'ange-decrypt-buffer find-file-hooks)
    (setq find-file-hooks
	  (append find-file-hooks
		  (list 'ange-decrypt-buffer))))

--
					-- ange --

					ange@hplb.hpl.hp.com