File: ps-ccrypt.el

package info (click to toggle)
ccrypt 1.11-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye, buster, sid
  • size: 4,088 kB
  • sloc: ansic: 22,340; sh: 5,691; makefile: 664; lisp: 637; yacc: 291; sed: 35
file content (1071 lines) | stat: -rw-r--r-- 36,069 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
;;; ps-ccrypt.el --- reading/writing/loading encrypted files

;; Copyright (C) 1993, 1994, 1995, 1997  Free Software Foundation, Inc.
;; Copyright (C) 2001-2018 Peter Selinger

;; Author: jka@ece.cmu.edu (Jay K. Adams) (jka-compr.el)
;; Changes: selinger@users.sourceforge.net (Peter Selinger) (ps-ccrypt.el)
;; Maintainer: Peter Selinger
;; Keywords: data

;; 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 software 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 this software; see the file COPYING.  If not, write to the
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.

;;; Commentary: 

;; This package implements low-level support for reading, writing, and
;; loading encrypted files.  It hooks into the low-level file I/O
;; functions (including write-region and insert-file-contents) so that
;; they automatically encrypt or decrypt a file if the file appears to
;; need it (based on the extension of the file name).  Packages like
;; Rmail, VM, GNUS, and Info should be able to work with encrypted
;; files without modification.

;;; Commentary on ccrypt support:

;; support for encryption/decryption with ccrypt was added by Peter
;; Selinger.  We have to deal with prompting users for passwords,
;; remembering passwords for each buffer, etc.

;; limitations:

;; On some systems, passing the password to ccrypt in an environment
;; variable may not be safe; this happens if users are able to display the
;; environment of processes they don't own with the 'ps' program. I don't
;; know of any system where this is still the case, but I heard that they
;; may exist. In any case, ccrypt deletes the value from its environment as
;; soon as it has been read; so this should not be a problem in most
;; situations.

;; Sometimes emacs will choke if it wants to auto-save a buffer in the
;; absense of a password or a minibuffer for prompting for it. I have not
;; yet figured out how to handle this situation; however, it does not arise
;; during "normal" operation because when a buffer's name has the ".cpt"
;; extension, a password is normally always defined for that buffer.

;; Under certain circumstances, decrypted data will be written to disk
;; temporarily. For instance, during each encryption operation, the
;; decrypted data is written to a temporary file. This is because the
;; call-process built-in function is designed to read from a file and write
;; to a buffer, not the other way around. I don't know a way around this
;; problem at the moment. Another potential way in which decrypted data
;; might appear on disk is if buffer contents are swapped out from main
;; memory.

;; auto-save files should not be a problem, since such files are also
;; encrypted if the buffer's filename indicates that they should be so.

;;; todo:

;; fix bug where "sh" does not return proper return value.
;; add support for .gz.cpt and .gzc
;; prompt for password twice when opening a new file.

;; CHANGES:
;;

;; 2018/07/25: PS1 - Emacs 26 compatibility: fixed a bug caused by an
;; incompatible change in write-region.
;;
;; 2017/03/05: PS1 - handle variable rename
;; (inhibit-first-line-modes-suffixes -> inhibit-local-variables-suffixes)
;; in a backward compatible way.
;;
;; 2017/02/22: PS1 - fixed warnings: inhibit-first-line-modes-suffixes
;; -> inhibit-local-variables-suffixes, fix buffer-file-type warning.
;;
;; 2016/11/13: PS1 - delete KEY earlier (even on password mismatch).
;;
;; 2016/11/13: PS1 - move (setenv "KEY") into the unwind-protect.
;; Note: this still leaks the password if the user mistyped it.
;;
;; 2016/11/13: PS1 - delete password from environment after each use.
;;
;; 2010/12/28: PS1 - only display "Password does not match" message if
;; password was just entered by the user; if the non-matching password
;; is stored, just prompt for it without error.
;;
;; 2010/12/28: PS1 - moved "encrypting xx" and "decrypting xx"
;; messages inside ps-ccrypt-call-process; this ensures the message
;; will appear even after a mismatched password prompt.
;;
;; 2010/12/28: PS1 - when inserting a file in a buffer, use filename,
;; not buffer name, in password prompt.
;;
;; 2010/12/28: PS1 - use existing buffer password when re-reading a
;; file.
;;
;; 2010/11/10: PS1 - fix mapcar compiler warnings.
;;
;; 2008/02/04: PS1 - better error message if ccrypt command not found.
;;
;; 2006/08/11: PS1 - removed compression functionality, renamed
;; package as ps-ccrypt. This can now coexist peacefully with
;; jka-compr.
;;
;; 2003/08/25: PS1 - bugfix
;;
;; 2003/08/13: JR1 - provide jka-compr existence functions in
;; jka-compr-ccrypt.el, to keep info.el happy.
;;
;; 2001/10/27: PS1 - pass keyword to ccrypt in environment variable,
;; not on command line. Renamed package as jka-compr-ccrypt.

;; INSTRUCTIONS:
;;
;; To use ps-ccrypt, simply load this package, and edit as usual.
;; One way to do this automatically is to include the lines
;;  (setq load-path (cons "<path>" load-path))
;;  (require 'ps-ccrypt "ps-ccrypt.el")
;; in your .emacs file, where <path> is the pathname where this file 
;; is found.
;;
;; The operation of this package should be transparent to the user
;; (except for messages appearing when a file is being encrypted or
;; decrypted).
;;
;; The variable, ps-ccrypt-encryption-info-list can be used to
;; customize ps-ccrypt to work with other encryption programs.
;; The default value of this variable allows ps-ccrypt to work with
;; ccrypt.
;;
;; If you don't want messages about encryption to show up in the echo
;; area, you can set the encrypt-name and deencrypt-name fields of
;; the ps-ccrypt-encryption-info-list to nil.
;;
;; The password for a buffer can be changed with the interactive command
;; M-x ccrypt-set-buffer-password. Note that the change does not affect
;; anything until the next time the buffer is saved.

;; ACKNOWLEDGMENTS
;;
;; ps-ccrypt is an adaptation of jka-compr, which is part of GNU Emacs.
;;
;; jka-compr is a V19 adaptation of jka-compr for V18 of Emacs.  Many people
;; have made helpful suggestions, reported bugs, and even fixed bugs in 
;; jka-compr.  I recall the following people as being particularly helpful.
;;
;;   Jean-loup Gailly
;;   David Hughes
;;   Richard Pieri
;;   Daniel Quinlan
;;   Chris P. Ross
;;   Rick Sladkey
;;
;; Andy Norman's ange-ftp was the inspiration for the original jka-compr for
;; Version 18 of Emacs.
;;
;; After I had made progress on the original jka-compr for V18, I learned of a
;; package written by Kazushi Jam Marukawa, called jam-zcat, that did exactly
;; what I was trying to do.  I looked over the jam-zcat source code and
;; probably got some ideas from it.
;;


;;; Code:

;; disable compiler warning about use of a "free variable"
(defvar buffer-file-type)

;; inhibit-first-line-modes-suffixes was renamed to
;; inhibit-local-variables-suffixes in Emacs 24.1. Ensure backward
;; compatibility with older versions.
(if (not (boundp 'inhibit-local-variables-suffixes))
    (defvaralias 'inhibit-local-variables-suffixes 'inhibit-first-line-modes-suffixes))

(defgroup encryption nil
  "Data encryption utilities"
  :group 'data)

(defgroup ps-ccrypt nil
  "ps-ccrypt customization"
  :group 'encryption)


(defcustom ps-ccrypt-shell "sh"
  "*Shell to be used for calling encryption programs. This is only used to
discard part of the output when a file is partially decrypted.  Note:
the hard-coded syntax in ps-ccrypt more or less assumes that this is
either sh or bash. See also the function ps-ccrypt-shell-escape."
  :type 'string
  :group 'ps-ccrypt)

(defun ps-ccrypt-shell-escape (x)
  "Takes a string and returns its escaped form to be used on the
command line of the shell whose name is set in ps-ccrypt-shell."
  (concat "\"" 
	  (apply 'concat 
		 (mapcar (function 
			  (lambda (c)
			    (if (memq c '(?\" ?\\ ?\$))
				(list ?\\ c)
			      (char-to-string c))))
			 x)) 
	  "\""))


;;; I have this defined so that .cpt files are assumed to be in ccrypt
;;; format.
(defcustom ps-ccrypt-encryption-info-list
  ;;[regexp
  ;; encr-message encr-prog encr-args
  ;; decr-message decr-prog decr-args
  ;; can-append auto-mode-flag retval-list password-flag]
  '(["\\.cpt\\(\\#\\|~\\|\\.~[0-9]+~\\)?\\'"
     "encrypting"     ("ccrypt" "-q" "-E" "KEY")
     "decrypting"     ("ccrypt" "-q" "-d" "-E" "KEY")
     nil t (0) t])

  "List of vectors that describe available encryption and encryption
techniques.  Each element, which describes an encryption or encryption
technique, is a vector of the form [REGEXP ENCRYPT-MSG ENCRYPT-COMMAND
DECRYPT-MSG DECRYPT-COMMAND APPEND-FLAG AUTO-MODE-FLAG RETVAL-LIST
PASSWORD-FLAG], where:

   regexp                is a regexp that matches filenames that are
                         encrypted with this format

   encrypt-msg          is the message to issue to the user when doing this
                         type of encryption (nil means no message)

   encrypt-command      is a command that performs this encryption, that
                         is, a list consisting of a program name and arguments

   decrypt-msg        is the message to issue to the user when doing this
                         type of decryption (nil means no message)

   decrypt-command    is a command that performs this encryption, that
                         is, a list consisting of a program name and arguments

   append-flag           is non-nil if this encryption technique can be
                         appended

   auto-mode-flag        non-nil means strip the regexp from file names
                         before attempting to set the mode

   retval-list           list of acceptable return values for encrypt
                         and decrypt program

   password-flag         non-nil if we are dealing with encryption rather
                         than encryption. In this case, the password is
                         passed to the ccrypt command in the environment 
                         variable KEY."

  :type '(repeat (vector regexp
			 (choice :tag "Encrypt Message"
				 (string :format "%v")
				 (const :tag "No Message" nil))
			 (repeat :tag "Encrypt Command" string)
			 (choice :tag "Decrypt Message"
				 (string :format "%v")
				 (const :tag "No Message" nil))
			 (repeat :tag "Decrypt Command" string)
			 (boolean :tag "Append")
			 (boolean :tag "Auto Mode")
                         (repeat :tag "Acceptable Return Values" integer)
                         (boolean :tag "Password Mode")))
  :group 'ps-ccrypt)

(defvar ps-ccrypt-mode-alist-additions
  (list (cons "\\.tgz\\'" 'tar-mode))
  "A list of pairs to add to `auto-mode-alist' when ps-ccrypt is installed.")

;; List of all the elements we actually added to file-coding-system-alist.
(defvar ps-ccrypt-added-to-file-coding-system-alist nil)

(defvar ps-ccrypt-file-name-handler-entry
  nil
  "The entry in `file-name-handler-alist' used by the ps-ccrypt I/O functions.")

;;; Functions for accessing the return value of ps-ccrypt-get-encryption-info
(defun ps-ccrypt-info-regexp               (info)  (aref info 0))
(defun ps-ccrypt-info-encrypt-message     (info)  (aref info 1))
(defun ps-ccrypt-info-encrypt-command     (info)  (aref info 2))
(defun ps-ccrypt-info-decrypt-message   (info)  (aref info 3))
(defun ps-ccrypt-info-decrypt-command   (info)  (aref info 4))
(defun ps-ccrypt-info-can-append           (info)  (aref info 5))
(defun ps-ccrypt-info-strip-extension      (info)  (aref info 6))
(defun ps-ccrypt-info-retval-list          (info)  (aref info 7))
(defun ps-ccrypt-info-password-flag        (info)  (aref info 8))

(defun ps-ccrypt-get-encryption-info (filename)
  "Return information about the encryption scheme of FILENAME.
The determination as to which encryption scheme, if any, to use is
based on the filename itself and `ps-ccrypt-encryption-info-list'."
  (catch 'encryption-info
    (let ((case-fold-search nil))
      (mapc
       (function (lambda (x)
		   (and (string-match (ps-ccrypt-info-regexp x) filename)
			(throw 'encryption-info x))))
       ps-ccrypt-encryption-info-list)
      nil)))

(defun ps-ccrypt-substitute (list key value)
  "Replace key by value in list"
  (mapcar (function (lambda (x) 
		      (if (eq x key)
			  value
			x)))
	  list))

(defvar ps-ccrypt-buffer-password nil
  "The encryption password. This variable is buffer-local.")

(make-variable-buffer-local 'ps-ccrypt-buffer-password)
(put 'ps-ccrypt-buffer-password 'permanent-local t)

(defun ps-ccrypt-read-passwd (&optional confirm filename)
  (read-passwd (format "Password for %s: " (or filename (buffer-name))) confirm nil))

(defun ps-ccrypt-get-buffer-password (&optional buffer)
  "Get encryption password for BUFFER (default: current buffer). 
Return nil if not set."
  (with-current-buffer (or buffer (current-buffer))
    ps-ccrypt-buffer-password))

(defun ps-ccrypt-set-buffer-password (password &optional buffer)
  "Set the encryption password for BUFFER (default: current buffer)."
  (with-current-buffer (or buffer (current-buffer))
    (setq ps-ccrypt-buffer-password password)))

(defun ccrypt-set-buffer-password ()
  "Set the encryption password for current buffer."
  (interactive "")
  (setq ps-ccrypt-buffer-password 
	(ps-ccrypt-read-passwd t)))

(put 'encryption-error 'error-conditions '(encryption-error file-error error))

;(defvar ps-ccrypt-acceptable-retval-list '(0 2 141))

(defun ps-ccrypt-error (command infile message &optional errfile)

  (let ((errbuf (get-buffer-create " *ps-ccrypt-error*"))
	(curbuf (current-buffer)))
    (with-current-buffer errbuf
      (widen) (erase-buffer)
      (insert (format "Error while executing \"%s < %s\"\n\n"
		      (mapconcat 'identity command " ")
		      infile))
      
      (and errfile
	   (insert-file-contents errfile)))
     (display-buffer errbuf))

  (signal 'encryption-error
	  (list "Opening input file" (format "error %s" message) infile)))

(defvar ps-ccrypt-dd-program
  "/bin/dd")

(defvar ps-ccrypt-dd-blocksize 256)

(defun ps-ccrypt-partial-decrypt (command message infile beg
					     len retvals &optional password)
  "Call program PROG with ARGS args taking input from INFILE.
Fourth and fifth args, BEG and LEN, specify which part of the output
to keep: LEN chars starting BEG chars from the beginning.
Sixth arg, RETVALS, specifies acceptable return values.
Seventh arg, &optional PASSWORD, specifies encryption password, if any."
  (let* ((skip (/ beg ps-ccrypt-dd-blocksize))
	 (prefix (- beg (* skip ps-ccrypt-dd-blocksize)))
	 (count (and len (1+ (/ (+ len prefix) ps-ccrypt-dd-blocksize))))
	 (start (point))
	 (dd (format "%s bs=%d skip=%d %s 2> /dev/null"
		     ps-ccrypt-dd-program
		     ps-ccrypt-dd-blocksize
		     skip
		     ;; dd seems to be unreliable about
		     ;; providing the last block.  So, always
		     ;; read one more than you think you need.
		     (if count (concat "count=" (1+ count)) "")))
	 (pipe-command (append command (list "|" dd))))
    
    (setq password (ps-ccrypt-call-process pipe-command
						  ps-ccrypt-shell 
						  message infile t retvals 
						  password))

    ;; Delete the stuff after what we want, if there is any.
    (and
     len
     (< (+ start prefix len) (point))
     (delete-region (+ start prefix len) (point)))

    ;; Delete the stuff before what we want.
    (delete-region start (+ start prefix)))
  password)

(defun ps-ccrypt-call-process2 (command infile buffer display &optional shell)
  "Similar to call-process. If SHELL is given and non-nil, then execute
the given command in the given shell. COMMAND in this case is a list
of strings, which are concatenated (with spaces) before execution.
Redirections, pipelines, etc, are permissible. If SHELL is absent or
nil, then execute the command directly, without a shell. In this case,
command must be a list of a program name, followed by individual
command line arguments."
  
  (if shell
      (call-process shell infile buffer display 
		    "-c" (mapconcat 'identity command " "))
    (apply 'call-process (car command) infile buffer display (cdr command)))
  )

;; pw-fresh is non-nil if the password was recently supplied by the user.
(defun ps-ccrypt-call-process (command shell message infile output retvals &optional password pw-fresh)

  (let ((filename (expand-file-name infile))
        (err-file (ps-ccrypt-make-temp-name))
	(coding-system-for-read (or coding-system-for-read 'undecided))
	(coding-system-for-write 'no-conversion)
	(buffer output)
	done)
    
    (unwind-protect

        (while (not done)
	  (message (format "%s..." message))
	  (if password
	      (setenv "KEY" password))
	  (let*
	      ((status
		(if shell
		    (call-process shell infile (list buffer err-file) nil 
				  "-c" (mapconcat 'identity command " "))
		  (condition-case err
		      (apply 'call-process (car command) infile 
			     (list buffer err-file) nil (cdr command))
		    (file-error 
		     (if (equal (nth 1 err) "Searching for program") 
			 ;; if command not found, output special error
			 (error "Failed to run %s: %s" (nth 3 err) (nth 2 err)) (sit-for 1)
		       ;; pass on other errors (e.g. input file not found)
		       (signal (car err) (cdr err))))))))

            ;; do not leave the password in the enviroment.
            (setenv "KEY")
	    (cond ((and password (eq status 4))
		   (cond (pw-fresh
			  (message "Password does not match; please try again")
			  (sit-for 1)))
		   (setq password (ps-ccrypt-read-passwd nil filename))
		   (setq pw-fresh t))
		  ((not (memq status retvals))
		   (ps-ccrypt-error command
				    infile message err-file))
		  (t
		   (setq done t)
		   (message (format "%s...done" message))))))
	  
      (ps-ccrypt-delete-temp-file err-file)
      (setenv "KEY"))

    password))
 

;;; Support for temp files.  Much of this was inspired if not lifted
;;; from ange-ftp.

(defcustom ps-ccrypt-temp-name-template
  (expand-file-name "jka-com" temporary-file-directory)
  "Prefix added to all temp files created by ps-ccrypt.
There should be no more than seven characters after the final `/'."
  :type 'string
  :group 'ps-ccrypt)

(defvar ps-ccrypt-temp-name-table (make-vector 31 nil))

(defun ps-ccrypt-make-temp-name (&optional local-copy)
  "This routine will return the name of a new file."
  (let* ((lastchar ?a)
	 (prevchar ?a)
	 (template (concat ps-ccrypt-temp-name-template "aa"))
	 (lastpos (1- (length template)))
	 (not-done t)
	 file
	 entry)

    (while not-done
      (aset template lastpos lastchar)
      (setq file (concat (make-temp-name template) "#"))
      (setq entry (intern file ps-ccrypt-temp-name-table))
      (if (or (get entry 'active)
	      (file-exists-p file))

	  (progn
	    (setq lastchar (1+ lastchar))
	    (if (> lastchar ?z)
		(progn
		  (setq prevchar (1+ prevchar))
		  (setq lastchar ?a)
		  (if (> prevchar ?z)
		      (error "Can't allocate temp file.")
		    (aset template (1- lastpos) prevchar)))))

	(put entry 'active (not local-copy))
	(setq not-done nil)))

    file))


(defun ps-ccrypt-delete-temp-file (temp)

  (put (intern temp ps-ccrypt-temp-name-table)
       'active nil)

  (condition-case ()
      (delete-file temp)
    (error nil)))


(defun ps-ccrypt-write-region (start end file &optional append visit lockname mustbenew)
  (let* ((filename (expand-file-name file))
	 (visit-file (if (stringp visit) (expand-file-name visit) filename))
	 (lock-file (if (stringp lockname) (expand-file-name lockname) nil))
	 (info (ps-ccrypt-get-encryption-info visit-file)))
      
      (if info

	  (let ((can-append (ps-ccrypt-info-can-append info))
		(encrypt-message (ps-ccrypt-info-encrypt-message info))
		(encrypt-command (ps-ccrypt-info-encrypt-command info))
		(password (if (ps-ccrypt-info-password-flag info) 
			      (or (ps-ccrypt-get-buffer-password) 
				  (ps-ccrypt-read-passwd t filename))
			    nil))
		(retvals (ps-ccrypt-info-retval-list info))
		(base-name (file-name-nondirectory visit-file))
		temp-file temp-buffer
		;; we need to leave `last-coding-system-used' set to its
		;; value after calling write-region the first time, so
		;; that `basic-save-buffer' sees the right value.
		(coding-system-used last-coding-system-used))

	    (setq temp-buffer (get-buffer-create " *ps-ccrypt-wr-temp*"))
	    (with-current-buffer temp-buffer
	      (widen) (erase-buffer))

	    (if (and append
		     (not can-append)
		     (file-exists-p filename))
		
		(let* ((local-copy (file-local-copy filename))
		       (local-file (or local-copy filename)))
		  
		  (setq temp-file local-file))

	      (setq temp-file (ps-ccrypt-make-temp-name)))

	    (ps-ccrypt-run-real-handler 'write-region
					(list start end temp-file t 'dont))
	    ;; save value used by the real write-region
	    (setq coding-system-used last-coding-system-used)

	    ;; Here we must read the output of encrypt program as is
	    ;; without any code conversion.
	    (let ((coding-system-for-read 'no-conversion))
	      (setq password 
		    (ps-ccrypt-call-process encrypt-command
					    nil
					    (concat encrypt-message
						    " " base-name)
					    temp-file
					    temp-buffer
					    retvals
					    password)))

	    (with-current-buffer temp-buffer
              (let ((coding-system-for-write 'no-conversion))
                (if (memq system-type '(ms-dos windows-nt))
                    (setq buffer-file-type t) )
                (ps-ccrypt-run-real-handler 'write-region
                                            (list (point-min) (point-max)
                                                  filename
                                                  (and append can-append) 'dont lock-file mustbenew))
                (erase-buffer)) )

	    (ps-ccrypt-delete-temp-file temp-file)

	    (cond
	     ((eq visit t)
	      (setq buffer-file-name filename)
	      (ps-ccrypt-set-buffer-password password)
	      (set-visited-file-modtime))
	     ((stringp visit)
	      (setq buffer-file-name visit)
	      (ps-ccrypt-set-buffer-password password)
	      (let ((buffer-file-name filename))
		(set-visited-file-modtime))))

	    (and (or (eq visit t)
		     (eq visit nil)
		     (stringp visit))
		 (message "Wrote %s" visit-file))

	    ;; ensure `last-coding-system-used' has an appropriate value
	    (setq last-coding-system-used coding-system-used)

	    nil)
	      
	(ps-ccrypt-run-real-handler 'write-region
				    (list start end filename append visit lock-file mustbenew)))))


(defun ps-ccrypt-insert-file-contents (file &optional visit beg end replace)
  (barf-if-buffer-read-only)

  (and (or beg end)
       visit
       (error "Attempt to visit less than an entire file"))

  (let* ((filename (expand-file-name file))
	 (info (ps-ccrypt-get-encryption-info filename)))

    (if info

	(let* ((pw-fresh nil)
	       (decrypt-message (ps-ccrypt-info-decrypt-message info))
	       (decrypt-command (ps-ccrypt-info-decrypt-command info))
	       (password (if (ps-ccrypt-info-password-flag info) 
			     (or (ps-ccrypt-get-buffer-password) 
				 (progn (setq pw-fresh t) 
					(ps-ccrypt-read-passwd nil filename))) 
			   nil))
	       (retvals (ps-ccrypt-info-retval-list info))
	       (base-name (file-name-nondirectory filename))
	       (notfound nil)
	       (local-copy
		(ps-ccrypt-run-real-handler 'file-local-copy (list filename)))
	       local-file
	       size start
	       (coding-system-for-read
		(or coding-system-for-read
		    ;; If multibyte characters are disabled,
		    ;; don't do that conversion.
		    (and (null enable-multibyte-characters)
			 (or (auto-coding-alist-lookup
			      (ps-ccrypt-byte-compiler-base-file-name file))
			     'raw-text))
		    (let ((coding (find-operation-coding-system
				   'insert-file-contents
				   (ps-ccrypt-byte-compiler-base-file-name file))))
		      (and (consp coding) (car coding)))
		    'undecided)) )
	  
	  (setq local-file (or local-copy filename))
	  
	  (if visit
	      (setq buffer-file-name filename))

	  (unwind-protect		; to make sure local-copy gets deleted

	      (progn
		  
		(condition-case error-code

		    (progn
		      (if replace
			  (goto-char (point-min)))
		      (setq start (point))
		      (if (or beg end)
			  (ps-ccrypt-partial-decrypt 
			   decrypt-command
			   (concat decrypt-message
				   " " base-name)
			   local-file
			   (or beg 0)
			   (if (and beg end)
			       (- end beg)
			     end)
			   retvals
			   password)
			;; If visiting, bind off buffer-file-name so that
			;; file-locking will not ask whether we should
			;; really edit the buffer.
			(let ((buffer-file-name
			       (if visit nil buffer-file-name)))
			  (setq password
				(ps-ccrypt-call-process 
				 decrypt-command
				 nil
				 (concat decrypt-message
					 " " base-name)
				 local-file
				 t
				 retvals
				 password
				 pw-fresh))))
		      (setq size (- (point) start))
		      (if replace
			  (let* ((del-beg (point))
				 (del-end (+ del-beg size)))
			    (delete-region del-beg
					   (min del-end (point-max)))))
		      (goto-char start))
		  (error
		   (if (and (eq (car error-code) 'file-error)
			    (eq (nth 3 error-code) local-file))
		       (if visit
			   (setq notfound error-code)
			 (signal 'file-error 
				 (cons "Opening input file"
				       (nthcdr 2 error-code))))
		     (signal (car error-code) (cdr error-code))))))

	    (and
	     local-copy
	     (file-exists-p local-copy)
	     (delete-file local-copy)))

	  (and
	   visit
	   (progn
	     (unlock-buffer)
	     (setq buffer-file-name filename)
	     (set-visited-file-modtime)))

	  (and visit
	     (ps-ccrypt-set-buffer-password password))
	    
	  (and
	   visit
	   notfound
	   (signal 'file-error
		   (cons "Opening input file" (nth 2 notfound))))

	  ;; This is done in insert-file-contents after we return.
	  ;; That is a little weird, but better to go along with it now
	  ;; than to change it now.

;;;	  ;; Run the functions that insert-file-contents would.
;;; 	  (let ((p after-insert-file-functions)
;;; 		(insval size))
;;; 	    (while p
;;; 	      (setq insval (funcall (car p) size))
;;; 	      (if insval
;;; 		  (progn
;;; 		    (or (integerp insval)
;;; 			(signal 'wrong-type-argument
;;; 				(list 'integerp insval)))
;;; 		    (setq size insval)))
;;; 	      (setq p (cdr p))))

	  (list filename size))

      (ps-ccrypt-run-real-handler 'insert-file-contents
				  (list file visit beg end replace)))))


(defun ps-ccrypt-file-local-copy (file)
  (let* ((filename (expand-file-name file))
	 (info (ps-ccrypt-get-encryption-info filename)))

    (if info

	(let* ((pw-fresh nil)
	       (decrypt-message (ps-ccrypt-info-decrypt-message info))
	       (decrypt-command (ps-ccrypt-info-decrypt-command info))
	       (password (if (ps-ccrypt-info-password-flag info) 
			     (or (ps-ccrypt-get-buffer-password) 
				 (progn (setq pw-fresh t)
					(ps-ccrypt-read-passwd nil filename)))
			   nil))
	       (retvals (ps-ccrypt-info-retval-list info))
	       (base-name (file-name-nondirectory filename))
	       (local-copy
		(ps-ccrypt-run-real-handler 'file-local-copy (list filename)))
	       (temp-file (ps-ccrypt-make-temp-name t))
	       (temp-buffer (get-buffer-create " *ps-ccrypt-flc-temp*"))
	       (notfound nil)
	       local-file)
	  
	  (setq local-file (or local-copy filename))

	  (unwind-protect

	      (with-current-buffer temp-buffer
		  
		;; Here we must read the output of decrypt program
		;; and write it to TEMP-FILE without any code
		;; conversion.  An appropriate code conversion (if
		;; necessary) is done by the later I/O operation
		;; (e.g. load).
		(let ((coding-system-for-read 'no-conversion)
		      (coding-system-for-write 'no-conversion))

		  (ps-ccrypt-call-process 
		   decrypt-command
		   nil
		   (concat decrypt-message
			   " " base-name)
		   local-file
		   t
		   retvals
		   password
		   pw-fresh)

		  (write-region
		   (point-min) (point-max) temp-file nil 'dont)))

	    (and
	     local-copy
	     (file-exists-p local-copy)
	     (delete-file local-copy))

	    (kill-buffer temp-buffer))

	  temp-file)
	    
      (ps-ccrypt-run-real-handler 'file-local-copy (list filename)))))


;;; Support for loading encrypted files.
(defun ps-ccrypt-load (file &optional noerror nomessage nosuffix)
  "Documented as original."

  (let* ((local-copy (ps-ccrypt-file-local-copy file))
	 (load-file (or local-copy file)))

    (unwind-protect

	(let (inhibit-file-name-operation
	      inhibit-file-name-handlers)
	  (or nomessage
	      (message "Loading %s..." file))

	  (let ((load-force-doc-strings t))
	    (load load-file noerror t t))

	  (or nomessage
	      (message "Loading %s...done." file)))

      (ps-ccrypt-delete-temp-file local-copy))

    t))

(defun ps-ccrypt-byte-compiler-base-file-name (file)
  (let ((info (ps-ccrypt-get-encryption-info file)))
    (if (and info (ps-ccrypt-info-strip-extension info))
	(save-match-data
	  (substring file 0 (string-match (ps-ccrypt-info-regexp info) file)))
      file)))

(put 'write-region 'ps-ccrypt 'ps-ccrypt-write-region)
(put 'insert-file-contents 'ps-ccrypt 'ps-ccrypt-insert-file-contents)
(put 'file-local-copy 'ps-ccrypt 'ps-ccrypt-file-local-copy)
(put 'load 'ps-ccrypt 'ps-ccrypt-load)
(put 'byte-compiler-base-file-name 'ps-ccrypt
     'ps-ccrypt-byte-compiler-base-file-name)

(defvar ps-ccrypt-inhibit nil
  "Non-nil means inhibit automatic decryption temporarily.
Lisp programs can bind this to t to do that.
It is not recommended to set this variable permanently to anything but nil.")

(defun ps-ccrypt-handler (operation &rest args)
  (save-match-data
    (let ((jka-op (get operation 'ps-ccrypt)))
      (if (and jka-op (not ps-ccrypt-inhibit))
	  (apply jka-op args)
	(ps-ccrypt-run-real-handler operation args)))))

;; If we are given an operation that we don't handle,
;; call the Emacs primitive for that operation,
;; and manipulate the inhibit variables
;; to prevent the primitive from calling our handler again.
(defun ps-ccrypt-run-real-handler (operation args)
  (let ((inhibit-file-name-handlers
	 (cons 'ps-ccrypt-handler
	       (and (eq inhibit-file-name-operation operation)
		    inhibit-file-name-handlers)))
	(inhibit-file-name-operation operation))
    (apply operation args)))

;;;###autoload(defun auto-encryption-mode (&optional arg)
;;;###autoload  "\
;;;###autoloadToggle automatic file encryption and decryption.
;;;###autoloadWith prefix argument ARG, turn auto encryption on if positive, else off.
;;;###autoloadReturns the new status of auto encryption (non-nil means on)."
;;;###autoload  (interactive "P")
;;;###autoload  (if (not (fboundp 'ps-ccrypt-installed-p))
;;;###autoload      (progn
;;;###autoload        (require 'ps-ccrypt)
;;;###autoload        ;; That turned the mode on, so make it initially off.
;;;###autoload        (toggle-auto-encryption)))
;;;###autoload  (toggle-auto-encryption arg t))

(defun toggle-auto-encryption (&optional arg message)
  "Toggle automatic file encryption and decryption.
With prefix argument ARG, turn auto encryption on if positive, else off.
Returns the new status of auto encryption (non-nil means on).
If the argument MESSAGE is non-nil, it means to print a message
saying whether the mode is now on or off."
  (interactive "P\np")
  (let* ((installed (ps-ccrypt-installed-p))
	 (flag (if (null arg)
		   (not installed)
		 (or (eq arg t) (listp arg) (and (integerp arg) (> arg 0))))))

    (cond
     ((and flag installed) t)		; already installed

     ((and (not flag) (not installed)) nil) ; already not installed

     (flag
      (ps-ccrypt-install))

     (t
      (ps-ccrypt-uninstall)))


    (and message
	 (if flag
	     (message "Automatic file (de)encryption is now ON.")
	   (message "Automatic file (de)encryption is now OFF.")))

    flag))

(defun ps-ccrypt-build-file-regexp ()
  (concat
   "\\("
   (mapconcat
    'ps-ccrypt-info-regexp
    ps-ccrypt-encryption-info-list
    "\\)\\|\\(")
   "\\)"))


(defun ps-ccrypt-install ()
  "Install ps-ccrypt.
This adds entries to `file-name-handler-alist' and `auto-mode-alist'
and `inhibit-local-variables-suffixes'."

  (setq ps-ccrypt-file-name-handler-entry
	(cons (ps-ccrypt-build-file-regexp) 'ps-ccrypt-handler))

  (setq file-name-handler-alist (cons ps-ccrypt-file-name-handler-entry
				      file-name-handler-alist))

  (setq ps-ccrypt-added-to-file-coding-system-alist nil)

  (mapc
   (function (lambda (x)
	       ;; Don't do multibyte encoding on the encrypted files.
	       (let ((elt (cons (ps-ccrypt-info-regexp x)
				 '(no-conversion . no-conversion))))
		 (setq file-coding-system-alist
		       (cons elt file-coding-system-alist))
		 (setq ps-ccrypt-added-to-file-coding-system-alist
		       (cons elt ps-ccrypt-added-to-file-coding-system-alist)))

	       (and (ps-ccrypt-info-strip-extension x)
		    ;; Make entries in auto-mode-alist so that modes
		    ;; are chosen right according to the file names
		    ;; sans `.gz'.
		    (setq auto-mode-alist
			  (cons (list (ps-ccrypt-info-regexp x)
				      nil 'ps-ccrypt)
				auto-mode-alist))
		    ;; Also add these regexps to
		    ;; inhibit-local-variables-suffixes, so that a
		    ;; -*- line in the first file of a encrypted tar
		    ;; file doesn't override tar-mode.
		    (setq inhibit-local-variables-suffixes
			  (cons (ps-ccrypt-info-regexp x)
				inhibit-local-variables-suffixes)))))
   ps-ccrypt-encryption-info-list)
  (setq auto-mode-alist
	(append auto-mode-alist ps-ccrypt-mode-alist-additions)))


(defun ps-ccrypt-uninstall ()
  "Uninstall ps-ccrypt.
This removes the entries in `file-name-handler-alist' and `auto-mode-alist'
and `inhibit-local-variables-suffixes' that were added
by `ps-ccrypt-installed'."
  ;; Delete from inhibit-local-variables-suffixes
  ;; what ps-ccrypt-install added.
  (mapc
     (function (lambda (x)
		 (and (ps-ccrypt-info-strip-extension x)
		      (setq inhibit-local-variables-suffixes
			    (delete (ps-ccrypt-info-regexp x)
				    inhibit-local-variables-suffixes)))))
     ps-ccrypt-encryption-info-list)

  (let* ((fnha (cons nil file-name-handler-alist))
	 (last fnha))

    (while (cdr last)
      (if (eq (cdr (car (cdr last))) 'ps-ccrypt-handler)
	  (setcdr last (cdr (cdr last)))
	(setq last (cdr last))))

    (setq file-name-handler-alist (cdr fnha)))

  (let* ((ama (cons nil auto-mode-alist))
	 (last ama)
	 entry)

    (while (cdr last)
      (setq entry (car (cdr last)))
      (if (or (member entry ps-ccrypt-mode-alist-additions)
	      (and (consp (cdr entry))
		   (eq (nth 2 entry) 'ps-ccrypt)))
	  (setcdr last (cdr (cdr last)))
	(setq last (cdr last))))
    
    (setq auto-mode-alist (cdr ama)))

  (let* ((ama (cons nil file-coding-system-alist))
	 (last ama)
	 entry)

    (while (cdr last)
      (setq entry (car (cdr last)))
      (if (member entry ps-ccrypt-added-to-file-coding-system-alist)
	  (setcdr last (cdr (cdr last)))
	(setq last (cdr last))))
    
    (setq file-coding-system-alist (cdr ama))))

      
(defun ps-ccrypt-installed-p ()
  "Return non-nil if ps-ccrypt is installed.
The return value is the entry in `file-name-handler-alist' for ps-ccrypt."

  (let ((fnha file-name-handler-alist)
	(installed nil))

    (while (and fnha (not installed))
     (and (eq (cdr (car fnha)) 'ps-ccrypt-handler)
	   (setq installed (car fnha)))
      (setq fnha (cdr fnha)))

    installed))

;;; Add the file I/O hook if it does not already exist.
;;; Make sure that ps-ccrypt-file-name-handler-entry is eq to the
;;; entry for ps-ccrypt in file-name-handler-alist.
(and (ps-ccrypt-installed-p)
     (ps-ccrypt-uninstall))

(ps-ccrypt-install)


(provide 'ps-ccrypt)

;;; ps-ccrypt.el ends here.