Mercurial > hg > xemacs-beta
comparison lisp/mailcrypt/mc-remail.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:376386a54a3c |
---|---|
1 ;; mc-remail.el --- Remailer support for Mailcrypt | |
2 | |
3 ;; Copyright (C) 1995 Patrick LoPresti <patl@lcs.mit.edu> | |
4 | |
5 ;;{{{ Licensing | |
6 | |
7 ;; This file is intended to be used with GNU Emacs. | |
8 | |
9 ;; This program is free software; you can redistribute it and/or modify | |
10 ;; it under the terms of the GNU General Public License as published by | |
11 ;; the Free Software Foundation; either version 2, or (at your option) | |
12 ;; any later version. | |
13 | |
14 ;; This program is distributed in the hope that it will be useful, | |
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 ;; GNU General Public License for more details. | |
18 | |
19 ;; You should have received a copy of the GNU General Public License | |
20 ;; along with GNU Emacs; see the file COPYING. If not, write to | |
21 ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. | |
22 | |
23 ;;}}} | |
24 ;;{{{ Load required packages | |
25 | |
26 (require 'mail-utils) | |
27 (require 'sendmail) | |
28 (require 'mailcrypt) | |
29 | |
30 (eval-and-compile | |
31 (if (not mc-xemacs-p) | |
32 (progn | |
33 (autoload 'mc-cleanup-recipient-headers "mc-toplev") | |
34 (autoload 'mc-encrypt-message "mc-toplev")))) | |
35 | |
36 (eval-and-compile | |
37 (condition-case nil (require 'mailalias) (error nil))) | |
38 | |
39 ;;}}} | |
40 ;;{{{ Functions dealing with remailer structures | |
41 | |
42 (defsubst mc-remailer-create (addr id props pre-encr post-encr) | |
43 "Create a remailer structure. | |
44 | |
45 ADDR is the remailer's Email address, a string. | |
46 | |
47 ID is the remailer's public key ID (a string) or nil if the same as | |
48 ADDR. | |
49 | |
50 PROPS is a list of properties, as strings. | |
51 | |
52 PRE-ENCR is a list of pre-encryption functions. Its elements will be | |
53 called with the remailer structure itself as argument. | |
54 | |
55 POST-ENCR is similar, but for post-encryption functions." | |
56 (list 'remailer addr id props pre-encr post-encr)) | |
57 | |
58 (defsubst mc-remailerp (remailer) | |
59 "Test whether REMAILER is a valid remailer struct." | |
60 (and (listp remailer) (eq 'remailer (car-safe remailer)))) | |
61 | |
62 (defsubst mc-remailer-address (remailer) | |
63 "Return the Email address of REMAILER." | |
64 (nth 1 remailer)) | |
65 | |
66 (defsubst mc-remailer-userid (remailer) | |
67 "Return the userid with which to look up the public key for REMAILER." | |
68 (or (nth 2 remailer) | |
69 (mc-strip-address (mc-remailer-address remailer)))) | |
70 | |
71 (defsubst mc-remailer-properties (remailer) | |
72 "Return the property list for REMAILER" | |
73 (nth 3 remailer)) | |
74 | |
75 (defsubst mc-remailer-pre-encrypt-hooks (remailer) | |
76 "Return the list of pre-encryption hooks for REMAILER." | |
77 (nth 4 remailer)) | |
78 | |
79 (defsubst mc-remailer-post-encrypt-hooks (remailer) | |
80 "Return the list of post-encryption hooks for REMAILER." | |
81 (nth 5 remailer)) | |
82 | |
83 (defun mc-remailer-remove-property (remailer prop) | |
84 (let ((props (append (mc-remailer-properties remailer) nil))) | |
85 (setq props (delete prop props)) | |
86 (mc-remailer-create | |
87 (mc-remailer-address remailer) | |
88 (mc-remailer-userid remailer) | |
89 props | |
90 (mc-remailer-pre-encrypt-hooks remailer) | |
91 (mc-remailer-post-encrypt-hooks remailer)))) | |
92 | |
93 ;;}}} | |
94 ;;{{{ User variables | |
95 | |
96 (defvar mc-response-block-included-headers | |
97 '("From" "To" "Newsgroups") | |
98 "List of header fields to include in response blocks. | |
99 | |
100 These will be copied into the deepest layer of the response block to | |
101 help you identify it when it is used to Email you.") | |
102 | |
103 | |
104 (defvar mc-remailer-tag "(*REMAILER*)" | |
105 "A string which marks an Email address as belonging to a remailer.") | |
106 | |
107 (defvar mc-levien-file-name "~/.remailers" | |
108 "The file containing a Levien format list of remailers. | |
109 | |
110 The file is read by `mc-read-levien-file' and `mc-reread-levien-file'. | |
111 | |
112 The file should include lines of the following form (other lines | |
113 are ignored): | |
114 | |
115 $remailer{\"NAME\"} = \"<EMAIL ADDRESS> PROPERTIES\"; | |
116 | |
117 PROPERTIES is a space-separated set of strings. | |
118 | |
119 This format is named after Raphael Levien, who maintains a list of | |
120 active remailers. Do \"finger remailer-list@kiwi.cs.berkeley.edu\" | |
121 for the latest copy of his list.") | |
122 | |
123 (defvar mc-remailer-user-chains nil | |
124 "An alist of remailer chains defined by the user. | |
125 | |
126 Format is | |
127 | |
128 ((NAME . REMAILER-LIST) | |
129 (NAME . REMAILER-LIST) | |
130 ...) | |
131 | |
132 NAME must be a string. | |
133 | |
134 REMAILER-LIST may be an arbitrary sequence, not just a list. Its | |
135 elements may be any of the following: | |
136 | |
137 1) A remailer structure created by `mc-remailer-create'. This is | |
138 the base case. | |
139 | |
140 2) A string naming another remailer chain to be spliced in | |
141 at this point. | |
142 | |
143 3) A positive integer N representing a chain to be spliced in at this | |
144 point and consisting of a random permutation of the top N remailers | |
145 as ordered in the file `mc-levien-file-name'. | |
146 | |
147 4) An arbitrary Lisp form to be evaluated, which should | |
148 return another REMAILER-LIST to be recursively processed and | |
149 spliced in at this point. | |
150 | |
151 The complete alist of chains is given by the union of the two lists | |
152 `mc-remailer-internal-chains' and `mc-remailer-user-chains'.") | |
153 | |
154 (defvar mc-remailer-internal-chains nil | |
155 "List of \"internal\" remailer chains. | |
156 | |
157 This variable is normally generated automatically from a human-readable | |
158 list of remailers; see, for example, the function `mc-reread-levien-file'. | |
159 | |
160 To define your own chains, you probably want to use the variable | |
161 `mc-remailer-user-chains'. See that variable's documentation for | |
162 format information.") | |
163 | |
164 (defvar mc-remailer-internal-ranking nil | |
165 "Ordered list of remailers, most reliable first. | |
166 | |
167 This variable is normally generated automatically from a human-readable | |
168 list of remailers; see, for example, the function `mc-reread-levien-file'.") | |
169 | |
170 (defvar mc-remailer-user-response-block | |
171 (function | |
172 (lambda (addr lines block) | |
173 (concat | |
174 ";;;\n" | |
175 (format | |
176 "To reply to this message, take the following %d-line block, remove\n" | |
177 lines) | |
178 "leading \"- \" constructs (if any), and place it at the top of a\n" | |
179 (format "message to %s :\n" addr) | |
180 block))) | |
181 "A function called to generate response block text. | |
182 | |
183 Value should be a function taking three arguments (ADDR LINES BLOCK). | |
184 ADDR is the address to which the response should be sent. | |
185 LINES is the number of lines in the encrypted response block. | |
186 BLOCK is the response block itself. | |
187 Function should return a string to be inserted into the buffer | |
188 by mc-remailer-insert-response-block.") | |
189 | |
190 (defvar mc-remailer-pseudonyms nil | |
191 "*A list of your pseudonyms. | |
192 | |
193 This is a list of strings. Completion against it will be available | |
194 when you are prompted for your pseudonym.") | |
195 | |
196 (defvar mc-remailer-preserved-headers | |
197 '("References" "Followup-to" "In-reply-to") | |
198 "*Header fields which are preserved as hashmark headers when rewriting. | |
199 | |
200 This is a list of strings naming the preserved headers. Note that | |
201 \"Subject\", \"Newsgroups\", and \"To\" are handled specially and | |
202 should not be included in this list.") | |
203 | |
204 ;;}}} | |
205 ;;{{{ Handling Levien format remailer lists | |
206 | |
207 (defun mc-parse-levien-buffer () | |
208 ;; Parse a buffer in Levien format. | |
209 (goto-char (point-min)) | |
210 (let (chains remailer remailer-name ranking) | |
211 (while | |
212 (re-search-forward | |
213 "^\\$remailer{\"\\(.+\\)\"}[ \t]*=[ \t]*\"\\(.*\\)\";" | |
214 nil t) | |
215 (let ((name (buffer-substring-no-properties | |
216 (match-beginning 1) (match-end 1))) | |
217 property-list address | |
218 (value-start (match-beginning 2)) | |
219 (value-end (match-end 2))) | |
220 (goto-char value-start) | |
221 (while (re-search-forward "[^ \t]+" value-end 'no-error) | |
222 (setq property-list | |
223 (append | |
224 property-list | |
225 (list (buffer-substring-no-properties | |
226 (match-beginning 0) (match-end 0)))))) | |
227 (setq address (car property-list) | |
228 property-list (cdr property-list) | |
229 remailer-name name) | |
230 (if (not | |
231 (or (member "mix" property-list) | |
232 (and (or (member "pgp" property-list) | |
233 (member "pgp." property-list)) | |
234 (or (member "cpunk" property-list) | |
235 (member "eric" property-list))))) | |
236 (setq remailer nil) | |
237 (setq remailer | |
238 (mc-remailer-create | |
239 address ; Address | |
240 (if (member "pgp." property-list) | |
241 name) ; User ID | |
242 property-list | |
243 '(mc-generic-pre-encrypt-function) ; Pre-encrypt hooks | |
244 '(mc-generic-post-encrypt-function) ; Post-encrypt hooks | |
245 )))) | |
246 (if (not (null remailer)) | |
247 (setq chains (cons (list remailer-name remailer) chains)))) | |
248 (goto-char (point-min)) | |
249 (if (re-search-forward "----------" nil t) | |
250 (while (re-search-forward "^\\([a-zA-Z0-9\\-]+\\) " nil t) | |
251 (setq remailer-name (buffer-substring-no-properties | |
252 (match-beginning 1) (match-end 1))) | |
253 (if (assoc remailer-name chains) | |
254 (setq ranking (append ranking (list remailer-name)))))) | |
255 (cons chains ranking))) | |
256 | |
257 (defun mc-read-levien-file () | |
258 "Read the Levien format file specified in `mc-levien-file-name'. | |
259 Return an alist of length-1 chains, one for each remailer, named | |
260 after the remailer. Only include remailers supporting PGP | |
261 encryption." | |
262 (save-excursion | |
263 (if (file-readable-p mc-levien-file-name) | |
264 (prog2 | |
265 (find-file-read-only mc-levien-file-name) | |
266 (mc-parse-levien-buffer) | |
267 (bury-buffer))))) | |
268 | |
269 (defun mc-reread-levien-file () | |
270 "Read the Levien format file specified in `mc-levien-file-name'. | |
271 | |
272 Place result in `mc-remailer-internal-chains' and `mc-remailer-internal-ranking'. | |
273 | |
274 See the documentation for the variable `mc-levien-file-name' for | |
275 a description of Levien file format." | |
276 (interactive) | |
277 (let ((parsed-levien-file (mc-read-levien-file))) | |
278 (setq mc-remailer-internal-chains (car parsed-levien-file) | |
279 mc-remailer-internal-ranking (cdr parsed-levien-file)))) | |
280 | |
281 ;;}}} | |
282 ;;{{{ Arbitrary chain choice | |
283 | |
284 (defun mc-remailer-choose-first (n &optional l) | |
285 (cond | |
286 ((= n 0) nil) | |
287 ((null l) (mc-remailer-choose-first n mc-remailer-internal-ranking)) | |
288 (t (cons (car l) (mc-remailer-choose-first (1- n) (cdr l)))))) | |
289 | |
290 (defun mc-remailer-choose-chain (n) | |
291 (if (null mc-remailer-internal-ranking) | |
292 (error "No ranking information, cannot choose the %d best remailer%s" | |
293 n (if (> n 1) "s" ""))) | |
294 (append (shuffle-vector (vconcat (mc-remailer-choose-first n))) | |
295 nil)) | |
296 | |
297 ;;}}} | |
298 ;;{{{ Canonicalization function | |
299 | |
300 (defun mc-remailer-canonicalize-elmt (elmt chains-alist) | |
301 (cond | |
302 ((mc-remailerp elmt) (list elmt)) | |
303 ((stringp elmt) | |
304 (mc-remailer-canonicalize-chain (cdr (assoc elmt chains-alist)) | |
305 chains-alist)) | |
306 ((integerp elmt) | |
307 (mc-remailer-canonicalize-chain (mc-remailer-choose-chain elmt) | |
308 chains-alist)) | |
309 (t (mc-remailer-canonicalize-chain (eval elmt) chains-alist)))) | |
310 | |
311 (defun mc-remailer-canonicalize-chain (chain &optional chains-alist) | |
312 ;; Canonicalize a remailer chain with respect to CHAINS-ALIST. | |
313 ;; That is, use CHAINS-ALIST to resolve strings. | |
314 ;; Here is where we implement the functionality described in | |
315 ;; the documentation for the variable `mc-remailer-user-chains'. | |
316 (if (null chains-alist) | |
317 (setq chains-alist (mc-remailer-make-chains-alist))) | |
318 (cond | |
319 ((null chain) nil) | |
320 ;; Handle case where chain is actually a string or a single | |
321 ;; remailer. | |
322 ((or (stringp chain) (mc-remailerp chain) (integerp chain)) | |
323 (mc-remailer-canonicalize-elmt chain chains-alist)) | |
324 (t | |
325 (let ((first (elt chain 0)) | |
326 (rest (cdr (append chain nil)))) | |
327 (append | |
328 (mc-remailer-canonicalize-elmt first chains-alist) | |
329 (mc-remailer-canonicalize-chain rest chains-alist)))))) | |
330 | |
331 ;;}}} | |
332 ;;{{{ Auxiliaries for mail header munging | |
333 | |
334 (defsubst mc-nuke-field (field &optional bounds) | |
335 ;; Delete all fields exactly matching regexp FIELD from header, | |
336 ;; bounded by BOUNDS. Default is entire visible region of buffer. | |
337 (mc-get-fields field bounds t)) | |
338 | |
339 (defun mc-replace-field (field-name replacement header) | |
340 (save-excursion | |
341 (save-restriction | |
342 (if (not (string-match "^[ \t]" replacement)) | |
343 (setq replacement (concat " " replacement))) | |
344 (if (not (string-match "\n$" replacement)) | |
345 (setq replacement (concat replacement "\n"))) | |
346 (let ((case-fold-search t) | |
347 (field-regexp (regexp-quote field-name))) | |
348 (narrow-to-region (car header) (cdr header)) | |
349 (goto-char (point-min)) | |
350 (re-search-forward | |
351 (concat "^" field-regexp ":" mc-field-body-regexp) | |
352 nil t) | |
353 (mc-nuke-field field-regexp header) | |
354 (insert field-name ":" replacement))))) | |
355 | |
356 (defun mc-find-main-header (&optional ignored) | |
357 ;; Find the main header of the mail message; return as a pair of | |
358 ;; markers (START . END). | |
359 (save-excursion | |
360 (goto-char (point-min)) | |
361 (re-search-forward | |
362 (concat "^" (regexp-quote mail-header-separator) "\n")) | |
363 (forward-line -1) | |
364 (cons (copy-marker (point-min)) (copy-marker (point))))) | |
365 | |
366 (defun mc-find-colon-header (&optional insert) | |
367 ;; Find the header with a "::" immediately after the | |
368 ;; mail-header-separator. Return region enclosing header. Optional | |
369 ;; arg INSERT means insert the header if it does not exist already. | |
370 (save-excursion | |
371 (goto-char (point-min)) | |
372 (re-search-forward | |
373 (concat "^" (regexp-quote mail-header-separator) "\n")) | |
374 (if (or (and (looking-at "::\n") (forward-line 1)) | |
375 (and insert | |
376 (progn | |
377 (insert-before-markers "::\n\n") | |
378 (forward-line -1)))) | |
379 (let ((start (point))) | |
380 (re-search-forward "^$" nil 'move) | |
381 (cons (copy-marker start) (copy-marker (point))))))) | |
382 | |
383 (defun mc-find-hash-header (&optional insert) | |
384 (save-excursion | |
385 (goto-char (point-min)) | |
386 (re-search-forward | |
387 (concat "^" (regexp-quote mail-header-separator) "\n")) | |
388 (if (or (and (looking-at "##\n") (forward-line 1)) | |
389 (and (looking-at "::\n") | |
390 (re-search-forward "^\n" nil 'move) | |
391 (looking-at "##\n") | |
392 (forward-line 1)) | |
393 (and insert | |
394 (progn | |
395 (insert-before-markers "##\n\n") | |
396 (forward-line -1)))) | |
397 (let ((start (point))) | |
398 (re-search-forward "^$" nil 'move) | |
399 (cons (copy-marker start) (copy-marker (point))))))) | |
400 | |
401 | |
402 (defsubst mc-replace-main-field (field replacement) | |
403 (mc-replace-field field replacement (mc-find-main-header t))) | |
404 | |
405 (defsubst mc-replace-hash-field (field replacement) | |
406 (mc-replace-field field replacement (mc-find-hash-header t))) | |
407 | |
408 (defsubst mc-replace-colon-field (field replacement) | |
409 (mc-replace-field field replacement (mc-find-colon-header t))) | |
410 | |
411 (defun mc-recipient-is-remailerp () | |
412 (let ((to (mc-get-fields "To" (mc-find-main-header)))) | |
413 (and to | |
414 (string-match (regexp-quote mc-remailer-tag) (cdr (car to)))))) | |
415 | |
416 ;;}}} | |
417 ;;{{{ Pre-encryption and post-encryption hook defaults | |
418 | |
419 (defun mc-generic-post-encrypt-function (remailer) | |
420 (let ((main-header (mc-find-main-header)) | |
421 (colon-header (mc-find-colon-header t))) | |
422 (mc-replace-field "Encrypted" "PGP" colon-header) | |
423 (mc-replace-field | |
424 "To" | |
425 (concat (mc-remailer-address remailer) " " mc-remailer-tag) | |
426 main-header))) | |
427 | |
428 (defun mc-generic-pre-encrypt-function (remailer) | |
429 (let ((addr (mc-remailer-address remailer)) | |
430 (props (mc-remailer-properties remailer)) | |
431 (main-header (mc-find-main-header)) | |
432 (colon-header (mc-find-colon-header t)) | |
433 to to-field preserved-regexp preserved) | |
434 | |
435 (setq preserved-regexp | |
436 (mc-disjunction-regexp mc-remailer-preserved-headers)) | |
437 (setq preserved (mc-get-fields preserved-regexp main-header t)) | |
438 (if preserved (goto-char (cdr (mc-find-hash-header t)))) | |
439 (mapcar (function | |
440 (lambda (c) | |
441 (insert (car c) ":" | |
442 (mc-eliminate-continuation-lines (cdr c))))) | |
443 preserved) | |
444 | |
445 (if (and (mc-find-hash-header) (not (member "hash" props))) | |
446 (error "Remailer %s does not support hashmarks" addr)) | |
447 | |
448 (if (mc-get-fields "Newsgroups" main-header) | |
449 (cond ((not (member "post" props)) | |
450 (error "Remailer %s does not support posting" addr)) | |
451 ((not (member "hash" props)) | |
452 (error "Remailer %s does not support hashmarks" addr)) | |
453 (t (mc-rewrite-news-to-mail remailer))) | |
454 (and (featurep 'mailalias) | |
455 (not (featurep 'mail-abbrevs)) | |
456 mail-aliases | |
457 (expand-mail-aliases (car main-header) (cdr main-header))) | |
458 (setq to (mc-strip-addresses | |
459 (mapcar 'cdr (mc-get-fields "To" main-header)))) | |
460 (if (string-match "," to) | |
461 (error "Remailer %s does not support multiple recipients." addr)) | |
462 (setq to-field | |
463 (if (mc-get-fields "From" colon-header) | |
464 "Send-To" | |
465 (cond | |
466 ((member "eric" props) "Anon-Send-To") | |
467 ((member "cpunk" props) "Request-Remailing-To") | |
468 (t (error "Remailer %s is not type-1" addr))))) | |
469 (mc-replace-field to-field to colon-header) | |
470 (mc-nuke-field "Reply-to" main-header)))) | |
471 | |
472 ;;}}} | |
473 ;;{{{ Misc. random | |
474 | |
475 (defun mc-disjunction-regexp (regexps) | |
476 ;; Take a list of regular expressions and return a single | |
477 ;; regular expression which matches anything that any of the | |
478 ;; original regexps match. | |
479 (concat "\\(" | |
480 (mapconcat 'identity regexps "\\)\\|\\(") | |
481 "\\)")) | |
482 | |
483 (defun mc-user-mail-address () | |
484 "Figure out the user's Email address as best we can." | |
485 (mc-strip-address | |
486 (cond ((and (boundp 'gnus-user-from-line) | |
487 (stringp gnus-user-from-line)) | |
488 gnus-user-from-line) | |
489 ((stringp mail-default-reply-to) mail-default-reply-to) | |
490 ((boundp 'user-mail-address) user-mail-address) | |
491 (t (concat (user-login-name) "@" (system-name)))))) | |
492 | |
493 (defun mc-eliminate-continuation-lines (string) | |
494 (while (string-match "\n[\t ]+" string) | |
495 (setq string (replace-match " " t nil string))) | |
496 string) | |
497 | |
498 (defun mc-remailer-make-chains-alist () | |
499 (if (null mc-remailer-internal-chains) | |
500 (mc-reread-levien-file)) | |
501 (append mc-remailer-internal-chains mc-remailer-user-chains)) | |
502 | |
503 ;;;###autoload | |
504 (defun mc-remailer-insert-pseudonym () | |
505 "Insert pseudonym as a From field in the hash-mark header. | |
506 | |
507 See the documentation for the variable `mc-remailer-pseudonyms' for | |
508 more information." | |
509 (interactive) | |
510 (let ((completion-ignore-case t) | |
511 pseudonym) | |
512 (setq pseudonym | |
513 (cond ((null mc-remailer-pseudonyms) | |
514 (read-from-minibuffer "Pseudonym: ")) | |
515 (t | |
516 (completing-read "Pseudonym: " | |
517 (mapcar 'list mc-remailer-pseudonyms))))) | |
518 (if (not (string-match "\\S +@\\S +" pseudonym)) | |
519 (setq pseudonym (concat pseudonym " <x@x.x>"))) | |
520 (mc-replace-colon-field "From" pseudonym))) | |
521 | |
522 ;;}}} | |
523 ;;{{{ Mixmaster support | |
524 (defvar mc-mixmaster-path nil | |
525 "*Path to the Mixmaster binary. If defined, Mixmaster chains will | |
526 be passed to this program for rewriting.") | |
527 | |
528 (defvar mc-mixmaster-list-path nil | |
529 "*Path to the Mixmaster type2.list file.") | |
530 | |
531 (defun mc-demix (&rest chain) | |
532 "Use arguments as a remailer-list and return a new list with the | |
533 \"mix\" property removed from all the elements." | |
534 (mapcar (function (lambda (r) (mc-remailer-remove-property r "mix"))) | |
535 (mc-remailer-canonicalize-chain chain))) | |
536 | |
537 (defun mc-mixmaster-process (beg end recipients preserved mix-chain) | |
538 ;; Run a region through Mixmaster. | |
539 (let (ret) | |
540 (if (not (markerp end)) | |
541 (setq end (copy-marker end))) | |
542 (goto-char beg) | |
543 (mapcar (function (lambda (x) (insert x ?\n))) recipients) | |
544 (insert ?\n) | |
545 (mapcar (function (lambda (x) (insert x))) preserved) | |
546 (insert ?\n) | |
547 (setq mix-chain (mapcar (function (lambda (x) (format "%d" x))) mix-chain)) | |
548 ;; Handle case of empty message | |
549 (if (< end (point)) (setq end (point))) | |
550 (setq ret | |
551 (apply 'call-process-region beg end mc-mixmaster-path t t nil | |
552 "-f" "-o" "stdout" "-l" mix-chain)) | |
553 (if (not (eq ret 0)) (error "Mixmaster barfed.")) | |
554 (goto-char beg) | |
555 (re-search-forward "^::$") | |
556 (delete-region beg (match-beginning 0)))) | |
557 | |
558 (defun mc-mixmaster-build-alist (&optional n) | |
559 ;; Construct an alist mapping Mixmaster Email addresses to integers. | |
560 ;; FIXME; this is terrible | |
561 (let (buf) | |
562 (save-excursion | |
563 (unwind-protect | |
564 (progn | |
565 (setq n (or n 1)) | |
566 (setq buf (find-file-noselect mc-mixmaster-list-path)) | |
567 (set-buffer buf) | |
568 (if (re-search-forward "^[^ \t]+[ \t]+\\([^ \t]+\\)" nil t) | |
569 (cons (cons (buffer-substring-no-properties | |
570 (match-beginning 1) (match-end 1)) | |
571 n) | |
572 (mc-mixmaster-build-alist (+ n 1))))) | |
573 (if buf (kill-buffer buf)))))) | |
574 | |
575 (defvar mc-mixmaster-alist nil) | |
576 | |
577 (defsubst mc-mixmaster-alist () | |
578 (or mc-mixmaster-alist | |
579 (setq mc-mixmaster-alist (mc-mixmaster-build-alist)))) | |
580 | |
581 (defun mc-mixmaster-translate-chain (chain) | |
582 ;; Take a chain of Mixmaster remailers and convert it to the list | |
583 ;; of integers which represents them. | |
584 (if (or (null chain) | |
585 (not (member "mix" (mc-remailer-properties (car chain))))) | |
586 nil | |
587 (cons (cdr (assoc (mc-strip-address (mc-remailer-address (car chain))) | |
588 (mc-mixmaster-alist))) | |
589 (mc-mixmaster-translate-chain (cdr chain))))) | |
590 | |
591 (defun mc-mixmaster-skip (chain) | |
592 ;; Return the largest possible suffix of CHAIN whose first element | |
593 ;; is not a Mixmaster. | |
594 (cond ((null chain) nil) | |
595 ((not (member "mix" (mc-remailer-properties (car chain)))) | |
596 chain) | |
597 (t (mc-mixmaster-skip (cdr chain))))) | |
598 | |
599 (defun mc-rewrite-for-mixmaster (chain &optional pause) | |
600 ;; Rewrite the current mail buffer for a chain of Mixmasters. | |
601 (let ((mix-chain (mc-mixmaster-translate-chain chain)) | |
602 (main-header (mc-find-main-header)) | |
603 (colon-header (mc-find-colon-header)) | |
604 (hash-header (mc-find-hash-header)) | |
605 recipients preserved newsgroups first last rest preserved-regexp) | |
606 | |
607 ;; Figure out FIRST and LAST. FIRST is the first Mixmaster in the | |
608 ;; chain. LAST is the last. | |
609 (setq first (car chain) | |
610 rest chain) | |
611 (while (and rest (member "mix" (mc-remailer-properties (car rest)))) | |
612 (setq last (car rest) | |
613 rest (cdr rest))) | |
614 | |
615 ;; If recipient is not a remailer, deal with hashmark and colon | |
616 ;; headers and get rid of them. | |
617 (if (mc-recipient-is-remailerp) | |
618 nil | |
619 (if hash-header | |
620 (progn | |
621 (setq preserved (mc-get-fields nil hash-header)) | |
622 (goto-char (car hash-header)) | |
623 (forward-line -1) | |
624 (delete-region (point) (+ (cdr hash-header) 1)))) | |
625 ;; Preserve pseduonym line... | |
626 (if colon-header | |
627 (progn | |
628 (setq preserved | |
629 (append (mc-get-fields "From" colon-header) preserved)) | |
630 (goto-char (car colon-header)) | |
631 (forward-line -1) | |
632 (delete-region (point) (+ (cdr colon-header) 1))))) | |
633 | |
634 ;; Expand aliases and get recipients. | |
635 (and (featurep 'mailalias) | |
636 (not (featurep 'mail-abbrevs)) | |
637 mail-aliases | |
638 (expand-mail-aliases (car main-header) (cdr main-header))) | |
639 (setq recipients | |
640 (mc-cleanup-recipient-headers | |
641 (mapconcat 'cdr (mc-get-fields "To" main-header t) ", "))) | |
642 (setq newsgroups (mc-get-fields "Newsgroups" nil t)) | |
643 ;; Mixmaster does not support posting... | |
644 ;;; (if (and newsgroups | |
645 ;;; (not (member "post" (mc-remailer-properties last)))) | |
646 (if newsgroups | |
647 (error "Remailer %s does not support posting" | |
648 (mc-remailer-address last))) | |
649 (setq | |
650 recipients | |
651 (append (mapcar | |
652 (function (lambda (c) (concat "Post:" (cdr c)))) newsgroups) | |
653 recipients)) | |
654 | |
655 (setq | |
656 preserved-regexp | |
657 (mc-disjunction-regexp (cons "Subject" mc-remailer-preserved-headers))) | |
658 | |
659 (setq preserved | |
660 (append (mc-get-fields preserved-regexp main-header t) preserved)) | |
661 | |
662 ;; Convert preserved header alist to simple list of strings | |
663 (setq preserved | |
664 (mapcar | |
665 (function | |
666 (lambda (c) | |
667 (concat (car c) ":" | |
668 (mc-eliminate-continuation-lines (cdr c))))) | |
669 preserved)) | |
670 | |
671 ;; Do the conversion | |
672 (goto-char (cdr main-header)) | |
673 (forward-line 1) | |
674 (mc-mixmaster-process (point) (point-max) recipients preserved | |
675 mix-chain) | |
676 | |
677 (mc-replace-field "To" | |
678 (concat | |
679 (mc-remailer-address first) " " mc-remailer-tag) | |
680 main-header))) | |
681 | |
682 ;;}}} | |
683 ;;{{{ High level message rewriting | |
684 | |
685 (defun mc-rewrite-news-to-mail (remailer) | |
686 (let ((main-header (mc-find-main-header)) | |
687 newsgroups) | |
688 (setq newsgroups (mc-get-fields "Newsgroups" main-header t)) | |
689 (mc-replace-colon-field "Post-To" (cdr (car newsgroups))) | |
690 (mail-mode))) | |
691 | |
692 (defun mc-rewrite-for-remailer (remailer &optional pause) | |
693 ;; Rewrite the current mail buffer for a single remailer. This | |
694 ;; includes running the pre-encryption hooks, modifying the To: | |
695 ;; field, encrypting with the remailer's public key, and running the | |
696 ;; post-encryption hooks. | |
697 (let ((addr (mc-remailer-address remailer)) | |
698 (main-header (mc-find-main-header))) | |
699 ;; If recipient is already a remailer, make sure the "::" and "##" | |
700 ;; headers get to it | |
701 (if (mc-recipient-is-remailerp) | |
702 (progn | |
703 (goto-char (cdr main-header)) | |
704 (forward-line 1) | |
705 (insert "::\n\n"))) | |
706 | |
707 (mapcar | |
708 (function (lambda (hook) (funcall hook remailer))) | |
709 (mc-remailer-pre-encrypt-hooks remailer)) | |
710 | |
711 ;; Move "Subject" lines down. | |
712 (goto-char (car (mc-find-colon-header t))) | |
713 (mapcar | |
714 (function (lambda (f) (insert (car f) ":" (cdr f)))) | |
715 (mc-get-fields "Subject" main-header t)) | |
716 | |
717 (if pause | |
718 (let ((cursor-in-echo-area t)) | |
719 (message "SPC to encrypt for %s : " addr) | |
720 (read-char-exclusive))) | |
721 (setq main-header (mc-find-main-header)) | |
722 (goto-char (cdr main-header)) | |
723 (forward-line 1) | |
724 (if (let ((mc-pgp-always-sign 'never) | |
725 (mc-encrypt-for-me nil)) | |
726 (mc-encrypt-message (mc-remailer-userid remailer) nil (point))) | |
727 (progn | |
728 (mapcar | |
729 (function (lambda (hook) (funcall hook remailer))) | |
730 (mc-remailer-post-encrypt-hooks remailer)) | |
731 (mc-nuke-field "Comment") | |
732 (mc-nuke-field "From")) | |
733 (error "Unable to encrypt message to %s" | |
734 (mc-remailer-userid remailer))))) | |
735 | |
736 (defun mc-rewrite-for-chain (chain &optional pause) | |
737 ;; Rewrite the current buffer for a chain of remailers. | |
738 ;; CHAIN must be in canonical form. | |
739 (let (rest) | |
740 (if mc-mixmaster-path | |
741 (setq rest (mc-mixmaster-skip chain)) | |
742 (setq rest chain)) | |
743 (if (null chain) nil | |
744 (mc-rewrite-for-chain | |
745 (if (eq rest chain) (cdr rest) rest) pause) | |
746 (if (eq rest chain) | |
747 (mc-rewrite-for-remailer (car chain) pause) | |
748 (mc-rewrite-for-mixmaster chain pause))))) | |
749 | |
750 (defun mc-unparse-chain (chain) | |
751 ;; Unparse CHAIN into a string suitable for printing. | |
752 (if (null chain) | |
753 nil | |
754 (concat (mc-remailer-address (car chain)) "\n" | |
755 (mc-unparse-chain (cdr chain))))) | |
756 | |
757 (defun mc-disallow-field (field &optional header) | |
758 (let ((case-fold-search t)) | |
759 (if (null header) | |
760 (setq header (mc-find-main-header))) | |
761 (goto-char (car header)) | |
762 (if (re-search-forward (concat "^" (regexp-quote field) ":") | |
763 (cdr header) t) | |
764 | |
765 (progn | |
766 (goto-char (match-beginning 0)) | |
767 (error "Cannot use a %s field." field))))) | |
768 | |
769 ;;;###autoload | |
770 (defun mc-remailer-encrypt-for-chain (&optional pause) | |
771 "Encrypt message for a remailer chain, prompting for chain to use. | |
772 | |
773 With \\[universal-argument], pause before each encryption." | |
774 (interactive "P") | |
775 (let ((chains (mc-remailer-make-chains-alist)) | |
776 (buffer (get-buffer-create mc-buffer-name)) | |
777 chain-name chain) | |
778 (mc-disallow-field "CC") | |
779 (mc-disallow-field "FCC") | |
780 (mc-disallow-field "BCC") | |
781 (setq chain-name | |
782 (completing-read | |
783 "Choose a remailer or chain: " chains nil 'strict-match)) | |
784 (setq chain | |
785 (mc-remailer-canonicalize-chain | |
786 (cdr (assoc chain-name chains)) | |
787 chains)) | |
788 (mc-rewrite-for-chain chain pause) | |
789 (if chain | |
790 (save-excursion | |
791 (set-buffer buffer) | |
792 (erase-buffer) | |
793 (insert "Rewritten for chain `" chain-name "':\n\n" | |
794 (mc-unparse-chain chain)) | |
795 (message "Done. See %s buffer for details." mc-buffer-name))))) | |
796 | |
797 ;;}}} | |
798 ;;{{{ Response block generation | |
799 | |
800 ;;;###autoload | |
801 (defun mc-remailer-insert-response-block (&optional arg) | |
802 "Insert response block at point, prompting for chain to use. | |
803 | |
804 With \\[universal-argument], enter a recursive edit of the innermost | |
805 layer of the block before encrypting it." | |
806 (interactive "p") | |
807 (let (buf main-header to addr block lines) | |
808 (save-excursion | |
809 (setq buf | |
810 (mc-remailer-make-response-block (if (> arg 1) t))) | |
811 (set-buffer buf) | |
812 (setq main-header (mc-find-main-header)) | |
813 (setq to (cdr (car (mc-get-fields "To" main-header)))) | |
814 (setq addr (concat "<" (mc-strip-address to) ">")) | |
815 (goto-char (cdr main-header)) | |
816 (forward-line 1) | |
817 (setq block (buffer-substring-no-properties | |
818 (point) (point-max)) | |
819 lines (count-lines (point) (point-max))) | |
820 (kill-buffer buf)) | |
821 (let ((opoint (point))) | |
822 (insert (funcall mc-remailer-user-response-block | |
823 addr lines block)) | |
824 (goto-char opoint)) | |
825 (mc-nuke-field "Reply-to" (mc-find-main-header)) | |
826 (mc-replace-hash-field "Reply-to" addr))) | |
827 | |
828 (defun mc-remailer-make-response-block (&optional recurse) | |
829 ;; Return a buffer which contains a response block | |
830 ;; for the user, and a To: header for the remailer to use. | |
831 (let ((buf (generate-new-buffer " *Remailer Response Block*")) | |
832 (original-buf (current-buffer)) | |
833 (mc-mixmaster-path nil) | |
834 all-headers included-regexp included) | |
835 (setq all-headers (mc-find-main-header)) | |
836 (setcdr all-headers | |
837 (max | |
838 (cdr all-headers) | |
839 (or (cdr-safe (mc-find-colon-header)) 0) | |
840 (or (cdr-safe (mc-find-hash-header)) 0))) | |
841 (save-excursion | |
842 (setq | |
843 included-regexp | |
844 (mc-disjunction-regexp mc-response-block-included-headers)) | |
845 (setq included (mc-get-fields included-regexp all-headers)) | |
846 (set-buffer buf) | |
847 (insert "To: " (mc-user-mail-address) "\n" mail-header-separator "\n") | |
848 (insert ";; Response block created " (current-time-string) "\n") | |
849 (mapcar (function (lambda (c) (insert "; " (car c) ":" (cdr c)))) | |
850 included) | |
851 (if recurse | |
852 (progn | |
853 (switch-to-buffer buf) | |
854 (message "Editing response block ; %s when done." | |
855 (substitute-command-keys "\\[exit-recursive-edit]")) | |
856 (recursive-edit))) | |
857 (set-buffer buf) | |
858 (mc-remailer-encrypt-for-chain) | |
859 (switch-to-buffer original-buf)) | |
860 buf)) | |
861 | |
862 ;;}}} |