]> code.delx.au - gnu-emacs/blob - lisp/gnus/mm-uu.el
Revision: miles@gnu.org--gnu-2005/emacs--cvs-trunk--0--patch-248
[gnu-emacs] / lisp / gnus / mm-uu.el
1 ;;; mm-uu.el --- Return uu stuff as mm handles
2 ;; Copyright (c) 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
3
4 ;; Author: Shenghuo Zhu <zsh@cs.rochester.edu>
5 ;; Keywords: postscript uudecode binhex shar forward gnatsweb pgp
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs 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 ;; GNU Emacs 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 the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
23
24 ;;; Commentary:
25
26 ;;; Code:
27
28 (eval-when-compile (require 'cl))
29 (require 'mail-parse)
30 (require 'nnheader)
31 (require 'mm-decode)
32 (require 'mailcap)
33 (require 'mml2015)
34
35 (autoload 'uudecode-decode-region "uudecode")
36 (autoload 'uudecode-decode-region-external "uudecode")
37 (autoload 'uudecode-decode-region-internal "uudecode")
38
39 (autoload 'binhex-decode-region "binhex")
40 (autoload 'binhex-decode-region-external "binhex")
41 (autoload 'binhex-decode-region-internal "binhex")
42
43 (autoload 'yenc-decode-region "yenc")
44 (autoload 'yenc-extract-filename "yenc")
45
46 (defcustom mm-uu-decode-function 'uudecode-decode-region
47 "*Function to uudecode.
48 Internal function is done in Lisp by default, therefore decoding may
49 appear to be horribly slow. You can make Gnus use an external
50 decoder, such as uudecode."
51 :type '(choice
52 (function-item :tag "Auto detect" uudecode-decode-region)
53 (function-item :tag "Internal" uudecode-decode-region-internal)
54 (function-item :tag "External" uudecode-decode-region-external))
55 :group 'gnus-article-mime)
56
57 (defcustom mm-uu-binhex-decode-function 'binhex-decode-region
58 "*Function to binhex decode.
59 Internal function is done in elisp by default, therefore decoding may
60 appear to be horribly slow . You can make Gnus use the external Unix
61 decoder, such as hexbin."
62 :type '(choice (function-item :tag "Auto detect" binhex-decode-region)
63 (function-item :tag "Internal" binhex-decode-region-internal)
64 (function-item :tag "External" binhex-decode-region-external))
65 :group 'gnus-article-mime)
66
67 (defvar mm-uu-yenc-decode-function 'yenc-decode-region)
68
69 (defvar mm-uu-pgp-beginning-signature
70 "^-----BEGIN PGP SIGNATURE-----")
71
72 (defvar mm-uu-beginning-regexp nil)
73
74 (defvar mm-dissect-disposition "inline"
75 "The default disposition of uu parts.
76 This can be either \"inline\" or \"attachment\".")
77
78 (defvar mm-uu-emacs-sources-regexp "gnu\\.emacs\\.sources"
79 "The regexp of Emacs sources groups.")
80
81 (defcustom mm-uu-diff-groups-regexp "gnus\\.commits"
82 "*Regexp matching diff groups."
83 :version "22.1"
84 :type 'regexp
85 :group 'gnus-article-mime)
86
87 (defvar mm-uu-type-alist
88 '((postscript
89 "^%!PS-"
90 "^%%EOF$"
91 mm-uu-postscript-extract
92 nil)
93 (uu
94 "^begin[ \t]+0?[0-7][0-7][0-7][ \t]+"
95 "^end[ \t]*$"
96 mm-uu-uu-extract
97 mm-uu-uu-filename)
98 (binhex
99 "^:...............................................................$"
100 ":$"
101 mm-uu-binhex-extract
102 nil
103 mm-uu-binhex-filename)
104 (yenc
105 "^=ybegin.*size=[0-9]+.*name=.*$"
106 "^=yend.*size=[0-9]+"
107 mm-uu-yenc-extract
108 mm-uu-yenc-filename)
109 (shar
110 "^#! */bin/sh"
111 "^exit 0$"
112 mm-uu-shar-extract)
113 (forward
114 ;;; Thanks to Edward J. Sabol <sabol@alderaan.gsfc.nasa.gov> and
115 ;;; Peter von der Ah\'e <pahe@daimi.au.dk>
116 "^-+ \\(Start of \\)?Forwarded message"
117 "^-+ End \\(of \\)?forwarded message"
118 mm-uu-forward-extract
119 nil
120 mm-uu-forward-test)
121 (gnatsweb
122 "^----gnatsweb-attachment----"
123 nil
124 mm-uu-gnatsweb-extract)
125 (pgp-signed
126 "^-----BEGIN PGP SIGNED MESSAGE-----"
127 "^-----END PGP SIGNATURE-----"
128 mm-uu-pgp-signed-extract
129 nil
130 nil)
131 (pgp-encrypted
132 "^-----BEGIN PGP MESSAGE-----"
133 "^-----END PGP MESSAGE-----"
134 mm-uu-pgp-encrypted-extract
135 nil
136 nil)
137 (pgp-key
138 "^-----BEGIN PGP PUBLIC KEY BLOCK-----"
139 "^-----END PGP PUBLIC KEY BLOCK-----"
140 mm-uu-pgp-key-extract
141 mm-uu-gpg-key-skip-to-last
142 nil)
143 (emacs-sources
144 "^;;;?[ \t]*[^ \t]+\\.el[ \t]*--"
145 "^;;;?[ \t]*\\([^ \t]+\\.el\\)[ \t]+ends here"
146 mm-uu-emacs-sources-extract
147 nil
148 mm-uu-emacs-sources-test)
149 (diff
150 "^Index: "
151 nil
152 mm-uu-diff-extract
153 nil
154 mm-uu-diff-test)))
155
156 (defcustom mm-uu-configure-list '((shar . disabled))
157 "A list of mm-uu configuration.
158 To disable dissecting shar codes, for instance, add
159 `(shar . disabled)' to this list."
160 :type 'alist
161 :options (mapcar (lambda (entry)
162 (list (car entry) '(const disabled)))
163 mm-uu-type-alist)
164 :group 'gnus-article-mime)
165
166 ;; functions
167
168 (defsubst mm-uu-type (entry)
169 (car entry))
170
171 (defsubst mm-uu-beginning-regexp (entry)
172 (nth 1 entry))
173
174 (defsubst mm-uu-end-regexp (entry)
175 (nth 2 entry))
176
177 (defsubst mm-uu-function-extract (entry)
178 (nth 3 entry))
179
180 (defsubst mm-uu-function-1 (entry)
181 (nth 4 entry))
182
183 (defsubst mm-uu-function-2 (entry)
184 (nth 5 entry))
185
186 (defun mm-uu-copy-to-buffer (&optional from to)
187 "Copy the contents of the current buffer to a fresh buffer.
188 Return that buffer."
189 (save-excursion
190 (let ((obuf (current-buffer))
191 (coding-system
192 ;; Might not exist in non-MULE XEmacs
193 (when (boundp 'buffer-file-coding-system)
194 buffer-file-coding-system)))
195 (set-buffer (generate-new-buffer " *mm-uu*"))
196 (setq buffer-file-coding-system coding-system)
197 (insert-buffer-substring obuf from to)
198 (current-buffer))))
199
200 (defun mm-uu-configure-p (key val)
201 (member (cons key val) mm-uu-configure-list))
202
203 (defun mm-uu-configure (&optional symbol value)
204 (if symbol (set-default symbol value))
205 (setq mm-uu-beginning-regexp nil)
206 (mapcar (lambda (entry)
207 (if (mm-uu-configure-p (mm-uu-type entry) 'disabled)
208 nil
209 (setq mm-uu-beginning-regexp
210 (concat mm-uu-beginning-regexp
211 (if mm-uu-beginning-regexp "\\|")
212 (mm-uu-beginning-regexp entry)))))
213 mm-uu-type-alist))
214
215 (mm-uu-configure)
216
217 (eval-when-compile
218 (defvar file-name)
219 (defvar start-point)
220 (defvar end-point)
221 (defvar entry))
222
223 (defun mm-uu-uu-filename ()
224 (if (looking-at ".+")
225 (setq file-name
226 (let ((nnheader-file-name-translation-alist
227 '((?/ . ?,) (?\ . ?_) (?* . ?_) (?$ . ?_))))
228 (nnheader-translate-file-chars (match-string 0))))))
229
230 (defun mm-uu-binhex-filename ()
231 (setq file-name
232 (ignore-errors
233 (binhex-decode-region start-point end-point t))))
234
235 (defun mm-uu-yenc-filename ()
236 (goto-char start-point)
237 (setq file-name
238 (ignore-errors
239 (yenc-extract-filename))))
240
241 (defun mm-uu-forward-test ()
242 (save-excursion
243 (goto-char start-point)
244 (forward-line)
245 (looking-at "[\r\n]*[a-zA-Z][a-zA-Z0-9-]*:")))
246
247 (defun mm-uu-postscript-extract ()
248 (mm-make-handle (mm-uu-copy-to-buffer start-point end-point)
249 '("application/postscript")))
250
251 (defun mm-uu-emacs-sources-extract ()
252 (mm-make-handle (mm-uu-copy-to-buffer start-point end-point)
253 '("application/emacs-lisp")
254 nil nil
255 (list mm-dissect-disposition
256 (cons 'filename file-name))))
257
258 (eval-when-compile
259 (defvar gnus-newsgroup-name))
260
261 (defun mm-uu-emacs-sources-test ()
262 (setq file-name (match-string 1))
263 (and gnus-newsgroup-name
264 mm-uu-emacs-sources-regexp
265 (string-match mm-uu-emacs-sources-regexp gnus-newsgroup-name)))
266
267 (defun mm-uu-diff-extract ()
268 (mm-make-handle (mm-uu-copy-to-buffer start-point end-point)
269 '("text/x-patch")))
270
271 (defun mm-uu-diff-test ()
272 (and gnus-newsgroup-name
273 mm-uu-diff-groups-regexp
274 (string-match mm-uu-diff-groups-regexp gnus-newsgroup-name)))
275
276 (defun mm-uu-forward-extract ()
277 (mm-make-handle (mm-uu-copy-to-buffer
278 (progn (goto-char start-point) (forward-line) (point))
279 (progn (goto-char end-point) (forward-line -1) (point)))
280 '("message/rfc822" (charset . gnus-decoded))))
281
282 (defun mm-uu-uu-extract ()
283 (mm-make-handle (mm-uu-copy-to-buffer start-point end-point)
284 (list (or (and file-name
285 (string-match "\\.[^\\.]+$"
286 file-name)
287 (mailcap-extension-to-mime
288 (match-string 0 file-name)))
289 "application/octet-stream"))
290 'x-uuencode nil
291 (if (and file-name (not (equal file-name "")))
292 (list mm-dissect-disposition
293 (cons 'filename file-name)))))
294
295 (defun mm-uu-binhex-extract ()
296 (mm-make-handle (mm-uu-copy-to-buffer start-point end-point)
297 (list (or (and file-name
298 (string-match "\\.[^\\.]+$" file-name)
299 (mailcap-extension-to-mime
300 (match-string 0 file-name)))
301 "application/octet-stream"))
302 'x-binhex nil
303 (if (and file-name (not (equal file-name "")))
304 (list mm-dissect-disposition
305 (cons 'filename file-name)))))
306
307 (defun mm-uu-yenc-extract ()
308 (mm-make-handle (mm-uu-copy-to-buffer start-point end-point)
309 (list (or (and file-name
310 (string-match "\\.[^\\.]+$" file-name)
311 (mailcap-extension-to-mime
312 (match-string 0 file-name)))
313 "application/octet-stream"))
314 'x-yenc nil
315 (if (and file-name (not (equal file-name "")))
316 (list mm-dissect-disposition
317 (cons 'filename file-name)))))
318
319
320 (defun mm-uu-shar-extract ()
321 (mm-make-handle (mm-uu-copy-to-buffer start-point end-point)
322 '("application/x-shar")))
323
324 (defun mm-uu-gnatsweb-extract ()
325 (save-restriction
326 (goto-char start-point)
327 (forward-line)
328 (narrow-to-region (point) end-point)
329 (mm-dissect-buffer t)))
330
331 (defun mm-uu-pgp-signed-test (&rest rest)
332 (and
333 mml2015-use
334 (mml2015-clear-verify-function)
335 (cond
336 ((eq mm-verify-option 'never) nil)
337 ((eq mm-verify-option 'always) t)
338 ((eq mm-verify-option 'known) t)
339 (t (y-or-n-p "Verify pgp signed part? ")))))
340
341 (eval-when-compile
342 (defvar gnus-newsgroup-charset))
343
344 (defun mm-uu-pgp-signed-extract-1 (handles ctl)
345 (let ((buf (mm-uu-copy-to-buffer (point-min) (point-max))))
346 (with-current-buffer buf
347 (if (mm-uu-pgp-signed-test)
348 (progn
349 (mml2015-clean-buffer)
350 (let ((coding-system-for-write (or gnus-newsgroup-charset
351 'iso-8859-1)))
352 (funcall (mml2015-clear-verify-function))))
353 (when (and mml2015-use (null (mml2015-clear-verify-function)))
354 (mm-set-handle-multipart-parameter
355 mm-security-handle 'gnus-details
356 (format "Clear verification not supported by `%s'.\n" mml2015-use))))
357 (goto-char (point-min))
358 (if (search-forward "\n\n" nil t)
359 (delete-region (point-min) (point)))
360 (if (re-search-forward mm-uu-pgp-beginning-signature nil t)
361 (delete-region (match-beginning 0) (point-max)))
362 (goto-char (point-min))
363 (while (re-search-forward "^- " nil t)
364 (replace-match "" t t)
365 (forward-line 1)))
366 (list (mm-make-handle buf '("text/plain" (charset . gnus-decoded))))))
367
368 (defun mm-uu-pgp-signed-extract ()
369 (let ((mm-security-handle (list (format "multipart/signed"))))
370 (mm-set-handle-multipart-parameter
371 mm-security-handle 'protocol "application/x-gnus-pgp-signature")
372 (save-restriction
373 (narrow-to-region start-point end-point)
374 (add-text-properties 0 (length (car mm-security-handle))
375 (list 'buffer (mm-uu-copy-to-buffer))
376 (car mm-security-handle))
377 (setcdr mm-security-handle
378 (mm-uu-pgp-signed-extract-1 nil
379 mm-security-handle)))
380 mm-security-handle))
381
382 (defun mm-uu-pgp-encrypted-test (&rest rest)
383 (and
384 mml2015-use
385 (mml2015-clear-decrypt-function)
386 (cond
387 ((eq mm-decrypt-option 'never) nil)
388 ((eq mm-decrypt-option 'always) t)
389 ((eq mm-decrypt-option 'known) t)
390 (t (y-or-n-p "Decrypt pgp encrypted part? ")))))
391
392 (defun mm-uu-pgp-encrypted-extract-1 (handles ctl)
393 (let ((buf (mm-uu-copy-to-buffer (point-min) (point-max))))
394 (if (mm-uu-pgp-encrypted-test)
395 (with-current-buffer buf
396 (mml2015-clean-buffer)
397 (funcall (mml2015-clear-decrypt-function))))
398 (list
399 (mm-make-handle buf
400 '("text/plain" (charset . gnus-decoded))))))
401
402 (defun mm-uu-pgp-encrypted-extract ()
403 (let ((mm-security-handle (list (format "multipart/encrypted"))))
404 (mm-set-handle-multipart-parameter
405 mm-security-handle 'protocol "application/x-gnus-pgp-encrypted")
406 (save-restriction
407 (narrow-to-region start-point end-point)
408 (add-text-properties 0 (length (car mm-security-handle))
409 (list 'buffer (mm-uu-copy-to-buffer))
410 (car mm-security-handle))
411 (setcdr mm-security-handle
412 (mm-uu-pgp-encrypted-extract-1 nil
413 mm-security-handle)))
414 mm-security-handle))
415
416 (defun mm-uu-gpg-key-skip-to-last ()
417 (let ((point (point))
418 (end-regexp (mm-uu-end-regexp entry))
419 (beginning-regexp (mm-uu-beginning-regexp entry)))
420 (when (and end-regexp
421 (not (mm-uu-configure-p (mm-uu-type entry) 'disabled)))
422 (while (re-search-forward end-regexp nil t)
423 (skip-chars-forward " \t\n\r")
424 (if (looking-at beginning-regexp)
425 (setq point (match-end 0)))))
426 (goto-char point)))
427
428 (defun mm-uu-pgp-key-extract ()
429 (let ((buf (mm-uu-copy-to-buffer start-point end-point)))
430 (mm-make-handle buf
431 '("application/pgp-keys"))))
432
433 ;;;###autoload
434 (defun mm-uu-dissect ()
435 "Dissect the current buffer and return a list of uu handles."
436 (let ((case-fold-search t)
437 text-start start-point end-point file-name result
438 text-plain-type entry func)
439 (save-excursion
440 (goto-char (point-min))
441 (cond
442 ((looking-at "\n")
443 (forward-line))
444 ((search-forward "\n\n" nil t)
445 t)
446 (t (goto-char (point-max))))
447 ;;; gnus-decoded is a fake charset, which means no further
448 ;;; decoding.
449 (setq text-start (point)
450 text-plain-type '("text/plain" (charset . gnus-decoded)))
451 (while (re-search-forward mm-uu-beginning-regexp nil t)
452 (setq start-point (match-beginning 0))
453 (let ((alist mm-uu-type-alist)
454 (beginning-regexp (match-string 0)))
455 (while (not entry)
456 (if (string-match (mm-uu-beginning-regexp (car alist))
457 beginning-regexp)
458 (setq entry (car alist))
459 (pop alist))))
460 (if (setq func (mm-uu-function-1 entry))
461 (funcall func))
462 (forward-line);; in case of failure
463 (when (and (not (mm-uu-configure-p (mm-uu-type entry) 'disabled))
464 (let ((end-regexp (mm-uu-end-regexp entry)))
465 (if (not end-regexp)
466 (or (setq end-point (point-max)) t)
467 (prog1
468 (re-search-forward end-regexp nil t)
469 (forward-line)
470 (setq end-point (point)))))
471 (or (not (setq func (mm-uu-function-2 entry)))
472 (funcall func)))
473 (if (and (> start-point text-start)
474 (progn
475 (goto-char text-start)
476 (re-search-forward "." start-point t)))
477 (push
478 (mm-make-handle (mm-uu-copy-to-buffer text-start start-point)
479 text-plain-type)
480 result))
481 (push
482 (funcall (mm-uu-function-extract entry))
483 result)
484 (goto-char (setq text-start end-point))))
485 (when result
486 (if (and (> (point-max) (1+ text-start))
487 (save-excursion
488 (goto-char text-start)
489 (re-search-forward "." nil t)))
490 (push
491 (mm-make-handle (mm-uu-copy-to-buffer text-start (point-max))
492 text-plain-type)
493 result))
494 (setq result (cons "multipart/mixed" (nreverse result))))
495 result)))
496
497 (provide 'mm-uu)
498
499 ;;; arch-tag: 7db076bf-53db-4320-aa19-ca76a1d2ab2c
500 ;;; mm-uu.el ends here