Mercurial > hg > xemacs-beta
comparison lisp/vm/vm-mime.el @ 20:859a2309aef8 r19-15b93
Import from CVS: tag r19-15b93
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:50:05 +0200 |
parents | |
children | 4103f0995bd7 |
comparison
equal
deleted
inserted
replaced
19:ac1f612d5250 | 20:859a2309aef8 |
---|---|
1 ;;; MIME support functions | |
2 ;;; Copyright (C) 1997 Kyle E. Jones | |
3 ;;; | |
4 ;;; This program is free software; you can redistribute it and/or modify | |
5 ;;; it under the terms of the GNU General Public License as published by | |
6 ;;; the Free Software Foundation; either version 1, or (at your option) | |
7 ;;; any later version. | |
8 ;;; | |
9 ;;; This program is distributed in the hope that it will be useful, | |
10 ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 ;;; GNU General Public License for more details. | |
13 ;;; | |
14 ;;; You should have received a copy of the GNU General Public License | |
15 ;;; along with this program; if not, write to the Free Software | |
16 ;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
17 | |
18 (provide 'vm-mime) | |
19 | |
20 (defun vm-mime-error (&rest args) | |
21 (signal 'vm-mime-error (list (apply 'format args))) | |
22 (error "can't return from vm-mime-error")) | |
23 | |
24 (if (fboundp 'define-error) | |
25 (define-error 'vm-mime-error "MIME error") | |
26 (put 'vm-mime-error 'error-conditions '(vm-mime-error error)) | |
27 (put 'vm-mime-error 'error-message "MIME error")) | |
28 | |
29 (defun vm-mm-layout-type (e) (aref e 0)) | |
30 (defun vm-mm-layout-encoding (e) (aref e 1)) | |
31 (defun vm-mm-layout-id (e) (aref e 2)) | |
32 (defun vm-mm-layout-description (e) (aref e 3)) | |
33 (defun vm-mm-layout-disposition (e) (aref e 4)) | |
34 (defun vm-mm-layout-header-start (e) (aref e 5)) | |
35 (defun vm-mm-layout-body-start (e) (aref e 6)) | |
36 (defun vm-mm-layout-body-end (e) (aref e 7)) | |
37 (defun vm-mm-layout-parts (e) (aref e 8)) | |
38 (defun vm-mm-layout-cache (e) (aref e 9)) | |
39 | |
40 (defun vm-set-mm-layout-cache (e c) (aset e 8 c)) | |
41 | |
42 (defun vm-mm-layout (m) | |
43 (or (vm-mime-layout-of m) | |
44 (progn (vm-set-mime-layout-of | |
45 m | |
46 (condition-case data | |
47 (vm-mime-parse-entity m) | |
48 (vm-mime-error (apply 'message (cdr data))))) | |
49 (vm-mime-layout-of m)))) | |
50 | |
51 (defun vm-mm-encoded-header (m) | |
52 (or (vm-mime-encoded-header-flag-of m) | |
53 (progn (setq m (vm-real-message-of m)) | |
54 (vm-set-mime-encoded-header-flag-of | |
55 m | |
56 (save-excursion | |
57 (set-buffer (vm-buffer-of m)) | |
58 (save-excursion | |
59 (save-restriction | |
60 (widen) | |
61 (goto-char (vm-headers-of m)) | |
62 (or (re-search-forward vm-mime-encoded-word-regexp | |
63 (vm-text-of m) t) | |
64 'none))))) | |
65 (vm-mime-encoded-header-flag-of m)))) | |
66 | |
67 (defun vm-mime-Q-decode-region (start end) | |
68 (let ((buffer-read-only nil)) | |
69 (subst-char-in-region start end ?_ (string-to-char " ") t) | |
70 (vm-mime-qp-decode-region start end))) | |
71 | |
72 (fset 'vm-mime-B-decode-region 'vm-mime-base64-decode-region) | |
73 | |
74 (defun vm-mime-Q-encode-region (start end) | |
75 (let ((buffer-read-only nil)) | |
76 (subst-char-in-region start end (string-to-char " ") ?_ t) | |
77 (vm-mime-qp-encode-region start end))) | |
78 | |
79 (fset 'vm-mime-B-encode-region 'vm-mime-base64-encode-region) | |
80 | |
81 (defun vm-mime-Q-decode-string (string) | |
82 (vm-with-string-as-region string 'vm-mime-Q-decode-region)) | |
83 | |
84 (defun vm-mime-B-decode-string (string) | |
85 (vm-with-string-as-region string 'vm-mime-B-decode-region)) | |
86 | |
87 (defun vm-mime-Q-encode-string (string) | |
88 (vm-with-string-as-region string 'vm-mime-Q-encode-region)) | |
89 | |
90 (defun vm-mime-B-encode-string (string) | |
91 (vm-with-string-as-region string 'vm-mime-B-encode-region)) | |
92 | |
93 (defun vm-mime-crlf-to-lf-region (start end) | |
94 (let ((buffer-read-only nil)) | |
95 (save-excursion | |
96 (save-restriction | |
97 (narrow-to-region start end) | |
98 (goto-char start) | |
99 (while (search-forward "\r\n" nil t) | |
100 (delete-char -2) | |
101 (insert "\n")))))) | |
102 | |
103 (defun vm-mime-lf-to-crlf-region (start end) | |
104 (let ((buffer-read-only nil)) | |
105 (save-excursion | |
106 (save-restriction | |
107 (narrow-to-region start end) | |
108 (goto-char start) | |
109 (while (search-forward "\n" nil t) | |
110 (delete-char -1) | |
111 (insert "\r\n")))))) | |
112 | |
113 (defun vm-mime-charset-decode-region (charset start end) | |
114 (let ((buffer-read-only nil) | |
115 (cell (vm-mime-charset-internally-displayable-p charset)) | |
116 (opoint (point))) | |
117 (cond ((and cell (vm-xemacs-mule-p) (eq (device-type) 'x)) | |
118 (decode-coding-region start end (car cell)))) | |
119 ;; In XEmacs 20.0 beta93 decode-coding-region moves point. | |
120 (goto-char opoint))) | |
121 | |
122 (defun vm-mime-transfer-decode-region (layout start end) | |
123 (let ((case-fold-search t) (crlf nil)) | |
124 (cond ((string-match "^base64$" (vm-mm-layout-encoding layout)) | |
125 (cond ((vm-mime-types-match "text" | |
126 (car (vm-mm-layout-type layout))) | |
127 (setq crlf t)) | |
128 ((vm-mime-types-match "message" | |
129 (car (vm-mm-layout-type layout))) | |
130 (setq crlf t))) | |
131 (vm-mime-base64-decode-region start end crlf)) | |
132 ((string-match "^quoted-printable$" | |
133 (vm-mm-layout-encoding layout)) | |
134 (vm-mime-qp-decode-region start end))))) | |
135 | |
136 (defun vm-mime-base64-decode-region (start end &optional crlf) | |
137 (vm-unsaved-message "Decoding base64...") | |
138 (let ((work-buffer nil) | |
139 (done nil) | |
140 (counter 0) | |
141 (bits 0) | |
142 (lim 0) inputpos | |
143 (non-data-chars (concat "^=" vm-mime-base64-alphabet))) | |
144 (unwind-protect | |
145 (save-excursion | |
146 (setq work-buffer (generate-new-buffer " *vm-work*")) | |
147 (buffer-disable-undo work-buffer) | |
148 (if vm-mime-base64-decoder-program | |
149 (let* ((binary-process-output t) ; any text already has CRLFs | |
150 (status (apply 'vm-run-command-on-region | |
151 start end work-buffer | |
152 vm-mime-base64-decoder-program | |
153 vm-mime-base64-decoder-switches))) | |
154 (if (not (eq status t)) | |
155 (vm-mime-error "%s" (cdr status)))) | |
156 (goto-char start) | |
157 (skip-chars-forward non-data-chars end) | |
158 (while (not done) | |
159 (setq inputpos (point)) | |
160 (cond | |
161 ((> (skip-chars-forward vm-mime-base64-alphabet end) 0) | |
162 (setq lim (point)) | |
163 (while (< inputpos lim) | |
164 (setq bits (+ bits | |
165 (aref vm-mime-base64-alphabet-decoding-vector | |
166 (char-after inputpos)))) | |
167 (vm-increment counter) | |
168 (vm-increment inputpos) | |
169 (cond ((= counter 4) | |
170 (vm-insert-char (lsh bits -16) 1 nil work-buffer) | |
171 (vm-insert-char (logand (lsh bits -8) 255) 1 nil | |
172 work-buffer) | |
173 (vm-insert-char (logand bits 255) 1 nil work-buffer) | |
174 (setq bits 0 counter 0)) | |
175 (t (setq bits (lsh bits 6))))))) | |
176 (cond | |
177 ((= (point) end) | |
178 (if (not (zerop counter)) | |
179 (vm-mime-error "at least %d bits missing at end of base64 encoding" | |
180 (* (- 4 counter) 6))) | |
181 (setq done t)) | |
182 ((= (char-after (point)) 61) ; 61 is ASCII equals | |
183 (setq done t) | |
184 (cond ((= counter 1) | |
185 (vm-mime-error "at least 2 bits missing at end of base64 encoding")) | |
186 ((= counter 2) | |
187 (vm-insert-char (lsh bits -10) 1 nil work-buffer)) | |
188 ((= counter 3) | |
189 (vm-insert-char (lsh bits -16) 1 nil work-buffer) | |
190 (vm-insert-char (logand (lsh bits -8) 255) | |
191 1 nil work-buffer)) | |
192 ((= counter 0) t))) | |
193 (t (skip-chars-forward non-data-chars end))))) | |
194 (and crlf | |
195 (save-excursion | |
196 (set-buffer work-buffer) | |
197 (vm-mime-crlf-to-lf-region (point-min) (point-max)))) | |
198 (or (markerp end) (setq end (vm-marker end))) | |
199 (goto-char start) | |
200 (insert-buffer-substring work-buffer) | |
201 (delete-region (point) end)) | |
202 (and work-buffer (kill-buffer work-buffer)))) | |
203 (vm-unsaved-message "Decoding base64... done")) | |
204 | |
205 (defun vm-mime-base64-encode-region (start end &optional crlf) | |
206 (vm-unsaved-message "Encoding base64...") | |
207 (let ((work-buffer nil) | |
208 (counter 0) | |
209 (cols 0) | |
210 (bits 0) | |
211 (alphabet vm-mime-base64-alphabet) | |
212 inputpos) | |
213 (unwind-protect | |
214 (save-excursion | |
215 (setq work-buffer (generate-new-buffer " *vm-work*")) | |
216 (buffer-disable-undo work-buffer) | |
217 (if crlf | |
218 (progn | |
219 (or (markerp end) (setq end (vm-marker end))) | |
220 (vm-mime-lf-to-crlf-region start end))) | |
221 (if vm-mime-base64-encoder-program | |
222 (let ((status (apply 'vm-run-command-on-region | |
223 start end work-buffer | |
224 vm-mime-base64-encoder-program | |
225 vm-mime-base64-encoder-switches))) | |
226 (if (not (eq status t)) | |
227 (vm-mime-error "%s" (cdr status)))) | |
228 (setq inputpos start) | |
229 (while (< inputpos end) | |
230 (setq bits (+ bits (char-after inputpos))) | |
231 (vm-increment counter) | |
232 (cond ((= counter 3) | |
233 (vm-insert-char (aref alphabet (lsh bits -18)) 1 nil | |
234 work-buffer) | |
235 (vm-insert-char (aref alphabet (logand (lsh bits -12) 63)) | |
236 1 nil work-buffer) | |
237 (vm-insert-char (aref alphabet (logand (lsh bits -6) 63)) | |
238 1 nil work-buffer) | |
239 (vm-insert-char (aref alphabet (logand bits 63)) 1 nil | |
240 work-buffer) | |
241 (setq cols (+ cols 4)) | |
242 (cond ((= cols 72) | |
243 (vm-insert-char ?\n 1 nil work-buffer) | |
244 (setq cols 0))) | |
245 (setq bits 0 counter 0)) | |
246 (t (setq bits (lsh bits 8)))) | |
247 (vm-increment inputpos)) | |
248 ;; write out any remaining bits with appropriate padding | |
249 (if (= counter 0) | |
250 nil | |
251 (setq bits (lsh bits (- 16 (* 8 counter)))) | |
252 (vm-insert-char (aref alphabet (lsh bits -18)) 1 nil | |
253 work-buffer) | |
254 (vm-insert-char (aref alphabet (logand (lsh bits -12) 63)) | |
255 1 nil work-buffer) | |
256 (if (= counter 1) | |
257 (vm-insert-char ?= 2 nil work-buffer) | |
258 (vm-insert-char (aref alphabet (logand (lsh bits -6) 63)) | |
259 1 nil work-buffer) | |
260 (vm-insert-char ?= 1 nil work-buffer))) | |
261 (if (> cols 0) | |
262 (vm-insert-char ?\n 1 nil work-buffer))) | |
263 (or (markerp end) (setq end (vm-marker end))) | |
264 (goto-char start) | |
265 (insert-buffer-substring work-buffer) | |
266 (delete-region (point) end)) | |
267 (and work-buffer (kill-buffer work-buffer)))) | |
268 (vm-unsaved-message "Encoding base64... done")) | |
269 | |
270 (defun vm-mime-qp-decode-region (start end) | |
271 (vm-unsaved-message "Decoding quoted-printable...") | |
272 (let ((work-buffer nil) | |
273 (buf (current-buffer)) | |
274 (case-fold-search nil) | |
275 (hex-digit-alist '((?0 . 0) (?1 . 1) (?2 . 2) (?3 . 3) | |
276 (?4 . 4) (?5 . 5) (?6 . 6) (?7 . 7) | |
277 (?8 . 8) (?9 . 9) (?A . 10) (?B . 11) | |
278 (?C . 12) (?D . 13) (?E . 14) (?F . 15))) | |
279 inputpos stop-point copy-point) | |
280 (unwind-protect | |
281 (save-excursion | |
282 (setq work-buffer (generate-new-buffer " *vm-work*")) | |
283 (buffer-disable-undo work-buffer) | |
284 (goto-char start) | |
285 (setq inputpos start) | |
286 (while (< inputpos end) | |
287 (skip-chars-forward "^=\n" end) | |
288 (setq stop-point (point)) | |
289 (cond ((looking-at "\n") | |
290 ;; spaces or tabs before a hard line break must be ignored | |
291 (skip-chars-backward " \t") | |
292 (setq copy-point (point)) | |
293 (goto-char stop-point)) | |
294 (t (setq copy-point stop-point))) | |
295 (save-excursion | |
296 (set-buffer work-buffer) | |
297 (insert-buffer-substring buf inputpos copy-point)) | |
298 (cond ((= (point) end) t) | |
299 ((looking-at "\n") | |
300 (vm-insert-char ?\n 1 nil work-buffer) | |
301 (forward-char)) | |
302 (t ;; looking at = | |
303 (forward-char) | |
304 (cond ((looking-at "[0-9A-F][0-9A-F]") | |
305 (vm-insert-char (+ (* (cdr (assq (char-after (point)) | |
306 hex-digit-alist)) | |
307 16) | |
308 (cdr (assq (char-after | |
309 (1+ (point))) | |
310 hex-digit-alist))) | |
311 1 nil work-buffer) | |
312 (forward-char 2)) | |
313 ((looking-at "\n") ; soft line break | |
314 (forward-char)) | |
315 ((looking-at "\r") | |
316 ;; assume the user's goatfucking | |
317 ;; delivery software didn't convert | |
318 ;; from Internet's CRLF newline | |
319 ;; convention to the local LF | |
320 ;; convention. | |
321 (forward-char)) | |
322 ((looking-at "[ \t]") | |
323 ;; garbage added in transit | |
324 (skip-chars-forward " \t" end)) | |
325 (t (vm-mime-error "something other than line break or hex digits after = in quoted-printable encoding"))))) | |
326 (setq inputpos (point))) | |
327 (or (markerp end) (setq end (vm-marker end))) | |
328 (goto-char start) | |
329 (insert-buffer-substring work-buffer) | |
330 (delete-region (point) end)) | |
331 (and work-buffer (kill-buffer work-buffer)))) | |
332 (vm-unsaved-message "Decoding quoted-printable... done")) | |
333 | |
334 (defun vm-mime-qp-encode-region (start end) | |
335 (vm-unsaved-message "Encoding quoted-printable...") | |
336 (let ((work-buffer nil) | |
337 (buf (current-buffer)) | |
338 (cols 0) | |
339 (hex-digit-alist '((?0 . 0) (?1 . 1) (?2 . 2) (?3 . 3) | |
340 (?4 . 4) (?5 . 5) (?6 . 6) (?7 . 7) | |
341 (?8 . 8) (?9 . 9) (?A . 10) (?B . 11) | |
342 (?C . 12) (?D . 13) (?E . 14) (?F . 15))) | |
343 char inputpos) | |
344 (unwind-protect | |
345 (save-excursion | |
346 (setq work-buffer (generate-new-buffer " *vm-work*")) | |
347 (buffer-disable-undo work-buffer) | |
348 (setq inputpos start) | |
349 (while (< inputpos end) | |
350 (setq char (char-after inputpos)) | |
351 (cond ((= char ?\n) | |
352 (vm-insert-char char 1 nil work-buffer) | |
353 (setq cols 0)) | |
354 ((and (= char 32) (not (= ?\n (char-after (1+ inputpos))))) | |
355 (vm-insert-char char 1 nil work-buffer) | |
356 (vm-increment cols)) | |
357 ((or (< char 33) (> char 126) (= char 61)) | |
358 (vm-insert-char ?= 1 nil work-buffer) | |
359 (vm-insert-char (car (rassq (lsh char -4) hex-digit-alist)) | |
360 1 nil work-buffer) | |
361 (vm-insert-char (car (rassq (logand char 15) | |
362 hex-digit-alist)) | |
363 1 nil work-buffer) | |
364 (setq cols (+ cols 3))) | |
365 (t (vm-insert-char char 1 nil work-buffer) | |
366 (vm-increment cols))) | |
367 (cond ((> cols 70) | |
368 (vm-insert-char ?= 1 nil work-buffer) | |
369 (vm-insert-char ?\n 1 nil work-buffer) | |
370 (setq cols 0))) | |
371 (vm-increment inputpos)) | |
372 (or (markerp end) (setq end (vm-marker end))) | |
373 (goto-char start) | |
374 (insert-buffer-substring work-buffer) | |
375 (delete-region (point) end)) | |
376 (and work-buffer (kill-buffer work-buffer)))) | |
377 (vm-unsaved-message "Encoding quoted-printable... done")) | |
378 | |
379 (defun vm-decode-mime-message-headers (m) | |
380 (let ((case-fold-search t) | |
381 (buffer-read-only nil) | |
382 charset encoding match-start match-end start end) | |
383 (save-excursion | |
384 (goto-char (vm-headers-of m)) | |
385 (while (re-search-forward vm-mime-encoded-word-regexp (vm-text-of m) t) | |
386 (setq match-start (match-beginning 0) | |
387 match-end (match-end 0) | |
388 charset (match-string 1) | |
389 encoding (match-string 2) | |
390 start (match-beginning 3) | |
391 end (vm-marker (match-end 3))) | |
392 ;; don't change anything if we can't display the | |
393 ;; character set properly. | |
394 (if (not (vm-mime-charset-internally-displayable-p charset)) | |
395 nil | |
396 (delete-region end match-end) | |
397 (cond ((string-match "B" encoding) | |
398 (vm-mime-B-decode-region start end)) | |
399 ((string-match "Q" encoding) | |
400 (vm-mime-Q-decode-region start end)) | |
401 (t (vm-mime-error "unknown encoded word encoding, %s" | |
402 encoding))) | |
403 (vm-mime-charset-decode-region charset start end) | |
404 (delete-region match-start start)))))) | |
405 | |
406 (defun vm-decode-mime-encoded-words () | |
407 (let ((case-fold-search t) | |
408 (buffer-read-only nil) | |
409 charset encoding match-start match-end start end) | |
410 (save-excursion | |
411 (goto-char (point-min)) | |
412 (while (re-search-forward vm-mime-encoded-word-regexp nil t) | |
413 (setq match-start (match-beginning 0) | |
414 match-end (match-end 0) | |
415 charset (match-string 1) | |
416 encoding (match-string 2) | |
417 start (match-beginning 3) | |
418 end (vm-marker (match-end 3))) | |
419 ;; don't change anything if we can't display the | |
420 ;; character set properly. | |
421 (if (not (vm-mime-charset-internally-displayable-p charset)) | |
422 nil | |
423 (delete-region end match-end) | |
424 (cond ((string-match "B" encoding) | |
425 (vm-mime-B-decode-region start end)) | |
426 ((string-match "Q" encoding) | |
427 (vm-mime-Q-decode-region start end)) | |
428 (t (vm-mime-error "unknown encoded word encoding, %s" | |
429 encoding))) | |
430 (vm-mime-charset-decode-region charset start end) | |
431 (delete-region match-start start)))))) | |
432 | |
433 (defun vm-decode-mime-encoded-words-maybe (string) | |
434 (if (and vm-display-using-mime | |
435 (string-match vm-mime-encoded-word-regexp string)) | |
436 (vm-with-string-as-temp-buffer string 'vm-decode-mime-encoded-words) | |
437 string )) | |
438 | |
439 (defun vm-mime-parse-content-header (string &optional sepchar) | |
440 (if (null string) | |
441 () | |
442 (let ((work-buffer nil)) | |
443 (save-excursion | |
444 (unwind-protect | |
445 (let ((list nil) | |
446 (nonspecials "^\"\\( \t\n\r\f") | |
447 start s char sp+sepchar) | |
448 (if sepchar | |
449 (setq nonspecials (concat nonspecials (list sepchar)) | |
450 sp+sepchar (concat "\t\f\n\r " (list sepchar)))) | |
451 (setq work-buffer (generate-new-buffer "*vm-work*")) | |
452 (buffer-disable-undo work-buffer) | |
453 (set-buffer work-buffer) | |
454 (insert string) | |
455 (goto-char (point-min)) | |
456 (skip-chars-forward "\t\f\n\r ") | |
457 (setq start (point)) | |
458 (while (not (eobp)) | |
459 (skip-chars-forward nonspecials) | |
460 (setq char (following-char)) | |
461 (cond ((looking-at "[ \t\n\r\f]") | |
462 (delete-char 1)) | |
463 ((= char ?\\) | |
464 (forward-char 1) | |
465 (if (not (eobp)) | |
466 (forward-char 1))) | |
467 ((and sepchar (= char sepchar)) | |
468 (setq s (buffer-substring start (point))) | |
469 (if (or (null (string-match "^[\t\f\n\r ]+$" s)) | |
470 (not (string= s ""))) | |
471 (setq list (cons s list))) | |
472 (skip-chars-forward sp+sepchar) | |
473 (setq start (point))) | |
474 ((looking-at " \t\n\r\f") | |
475 (skip-chars-forward " \t\n\r\f")) | |
476 ((= char ?\") | |
477 (delete-char 1) | |
478 (cond ((= (char-after (point)) ?\") | |
479 (delete-char 1)) | |
480 ((re-search-forward "[^\\]\"" nil 0) | |
481 (delete-char -1)))) | |
482 ((= char ?\() | |
483 (let ((parens 1) | |
484 (pos (point))) | |
485 (forward-char 1) | |
486 (while (and (not (eobp)) (not (zerop parens))) | |
487 (re-search-forward "[()]" nil 0) | |
488 (cond ((or (eobp) | |
489 (= (char-after (- (point) 2)) ?\\))) | |
490 ((= (preceding-char) ?\() | |
491 (setq parens (1+ parens))) | |
492 (t | |
493 (setq parens (1- parens))))) | |
494 (delete-region pos (point)))))) | |
495 (setq s (buffer-substring start (point))) | |
496 (if (and (null (string-match "^[\t\f\n\r ]+$" s)) | |
497 (not (string= s ""))) | |
498 (setq list (cons s list))) | |
499 (nreverse list)) | |
500 (and work-buffer (kill-buffer work-buffer))))))) | |
501 | |
502 (defun vm-mime-get-header-contents (header-name-regexp) | |
503 (let ((contents nil) | |
504 regexp) | |
505 (setq regexp (concat "^\\(" header-name-regexp "\\)\\|\\(^$\\)")) | |
506 (save-excursion | |
507 (let ((case-fold-search t)) | |
508 (if (and (re-search-forward regexp nil t) | |
509 (match-beginning 1) | |
510 (progn (goto-char (match-beginning 0)) | |
511 (vm-match-header))) | |
512 (vm-matched-header-contents) | |
513 nil ))))) | |
514 | |
515 (defun vm-mime-parse-entity (&optional m default-type default-encoding) | |
516 (let ((case-fold-search t) version type encoding id description | |
517 disposition boundary boundary-regexp start | |
518 multipart-list c-t c-t-e done p returnval) | |
519 (and m (vm-unsaved-message "Parsing MIME message...")) | |
520 (prog1 | |
521 (catch 'return-value | |
522 (save-excursion | |
523 (if m | |
524 (progn | |
525 (setq m (vm-real-message-of m)) | |
526 (set-buffer (vm-buffer-of m)))) | |
527 (save-excursion | |
528 (save-restriction | |
529 (if m | |
530 (progn | |
531 (setq version (vm-get-header-contents m "MIME-Version:") | |
532 version (car (vm-mime-parse-content-header version)) | |
533 type (vm-get-header-contents m "Content-Type:") | |
534 type (vm-mime-parse-content-header type ?\;) | |
535 encoding (or (vm-get-header-contents | |
536 m "Content-Transfer-Encoding:") | |
537 "7bit") | |
538 encoding (car (vm-mime-parse-content-header encoding)) | |
539 id (vm-get-header-contents m "Content-ID:") | |
540 id (car (vm-mime-parse-content-header id)) | |
541 description (vm-get-header-contents | |
542 m "Content-Description:") | |
543 description (and description | |
544 (if (string-match "^[ \t\n]$" | |
545 description) | |
546 nil | |
547 description)) | |
548 disposition (vm-get-header-contents | |
549 m "Content-Disposition:") | |
550 disposition (and disposition | |
551 (vm-mime-parse-content-header | |
552 disposition ?\;))) | |
553 (widen) | |
554 (narrow-to-region (vm-headers-of m) (vm-text-end-of m))) | |
555 (goto-char (point-min)) | |
556 (setq type (vm-mime-get-header-contents "Content-Type:") | |
557 type (or (vm-mime-parse-content-header type ?\;) | |
558 default-type) | |
559 encoding (or (vm-mime-get-header-contents | |
560 "Content-Transfer-Encoding:") | |
561 default-encoding) | |
562 encoding (car (vm-mime-parse-content-header encoding)) | |
563 id (vm-mime-get-header-contents "Content-ID:") | |
564 id (car (vm-mime-parse-content-header id)) | |
565 description (vm-mime-get-header-contents | |
566 "Content-Description:") | |
567 description (and description (if (string-match "^[ \t\n]+$" | |
568 description) | |
569 nil | |
570 description)) | |
571 disposition (vm-mime-get-header-contents | |
572 "Content-Disposition:") | |
573 disposition (and disposition | |
574 (vm-mime-parse-content-header | |
575 disposition ?\;)))) | |
576 (cond ((null m) t) | |
577 ((null version) | |
578 (throw 'return-value 'none)) | |
579 ((string= version "1.0") t) | |
580 (t (vm-mime-error "Unsupported MIME version: %s" version))) | |
581 (cond ((and m (null type)) | |
582 (throw 'return-value | |
583 (vector '("text/plain" "charset=us-ascii") | |
584 encoding id description disposition | |
585 (vm-headers-of m) | |
586 (vm-text-of m) | |
587 (vm-text-end-of m) | |
588 nil nil nil ))) | |
589 ((null type) | |
590 (goto-char (point-min)) | |
591 (or (re-search-forward "^\n\\|\n\\'" nil t) | |
592 (vm-mime-error "MIME part missing header/body separator line")) | |
593 (vector default-type encoding id description disposition | |
594 (vm-marker (point-min)) | |
595 (vm-marker (point)) | |
596 (vm-marker (point-max)) | |
597 nil nil nil )) | |
598 ((null (string-match "[^/ ]+/[^/ ]+" (car type))) | |
599 (vm-mime-error "Malformed MIME content type: %s" (car type))) | |
600 ((and (string-match "^multipart/\\|^message/" (car type)) | |
601 (null (string-match "^\\(7bit\\|8bit\\|binary\\)$" | |
602 encoding))) | |
603 (vm-mime-error "Opaque transfer encoding used with multipart or message type: %s, %s" (car type) encoding)) | |
604 ((and (string-match "^message/partial$" (car type)) | |
605 (null (string-match "^7bit$" encoding))) | |
606 (vm-mime-error "Non-7BIT transfer encoding used with message/partial message: %s" encoding)) | |
607 ((string-match "^multipart/digest" (car type)) | |
608 (setq c-t '("message/rfc822") | |
609 c-t-e "7bit")) | |
610 ((string-match "^multipart/" (car type)) | |
611 (setq c-t '("text/plain" "charset=us-ascii") | |
612 c-t-e "7bit")) ; below | |
613 ((string-match "^message/rfc822" (car type)) | |
614 (setq c-t '("text/plain" "charset=us-ascii") | |
615 c-t-e "7bit") | |
616 (goto-char (point-min)) | |
617 (or (re-search-forward "^\n\\|\n\\'" nil t) | |
618 (vm-mime-error "MIME part missing header/body separator line")) | |
619 (throw 'return-value | |
620 (vector type encoding id description disposition | |
621 (vm-marker (point-min)) | |
622 (vm-marker (point)) | |
623 (vm-marker (point-max)) | |
624 (list | |
625 (save-restriction | |
626 (narrow-to-region (point) (point-max)) | |
627 (vm-mime-parse-entity nil c-t c-t-e))) | |
628 nil ))) | |
629 (t | |
630 (goto-char (point-min)) | |
631 (or (re-search-forward "^\n\\|\n\\'" nil t) | |
632 (vm-mime-error "MIME part missing header/body separator line")) | |
633 (throw 'return-value | |
634 (vector type encoding id description disposition | |
635 (vm-marker (point-min)) | |
636 (vm-marker (point)) | |
637 (vm-marker (point-max)) | |
638 nil nil )))) | |
639 (setq p (cdr type) | |
640 boundary nil) | |
641 (while p | |
642 (if (string-match "^boundary=" (car p)) | |
643 (setq boundary (car (vm-parse (car p) "=\\(.+\\)")) | |
644 p nil) | |
645 (setq p (cdr p)))) | |
646 (or boundary | |
647 (vm-mime-error | |
648 "Boundary parameter missing in %s type specification" | |
649 (car type))) | |
650 (setq boundary-regexp (regexp-quote boundary) | |
651 boundary-regexp (concat "^--" boundary-regexp "\\(--\\)?\n")) | |
652 (goto-char (point-min)) | |
653 (setq start nil | |
654 multipart-list nil | |
655 done nil) | |
656 (while (and (not done) (re-search-forward boundary-regexp nil t)) | |
657 (cond ((null start) | |
658 (setq start (match-end 0))) | |
659 (t | |
660 (and (match-beginning 1) | |
661 (setq done t)) | |
662 (save-excursion | |
663 (save-restriction | |
664 (narrow-to-region start (1- (match-beginning 0))) | |
665 (setq start (match-end 0)) | |
666 (setq multipart-list | |
667 (cons (vm-mime-parse-entity-safe nil c-t c-t-e) | |
668 multipart-list))))))) | |
669 (if (not done) | |
670 (vm-mime-error "final %s boundary missing" boundary)) | |
671 (goto-char (point-min)) | |
672 (or (re-search-forward "^\n\\|\n\\'" nil t) | |
673 (vm-mime-error "MIME part missing header/body separator line")) | |
674 (vector type encoding id description disposition | |
675 (vm-marker (point-min)) | |
676 (vm-marker (point)) | |
677 (vm-marker (point-max)) | |
678 (nreverse multipart-list) | |
679 nil ))))) | |
680 (and m (vm-unsaved-message "Parsing MIME message... done")) | |
681 ))) | |
682 | |
683 (defun vm-mime-parse-entity-safe (&optional m c-t c-t-e) | |
684 (or c-t (setq c-t '("text/plain" "charset=us-ascii"))) | |
685 ;; don't let subpart parse errors make the whole parse fail. use default | |
686 ;; type if the parse fails. | |
687 (condition-case error-data | |
688 (vm-mime-parse-entity nil c-t c-t-e) | |
689 (vm-mime-error | |
690 (let ((header (if m | |
691 (vm-headers-of m) | |
692 (vm-marker (point-min)))) | |
693 (text (if m | |
694 (vm-text-of m) | |
695 (save-excursion | |
696 (re-search-forward "^\n\\|\n\\'" | |
697 nil 0) | |
698 (vm-marker (point))))) | |
699 (text-end (if m | |
700 (vm-text-end-of m) | |
701 (vm-marker (point-max))))) | |
702 (vector c-t | |
703 (vm-determine-proper-content-transfer-encoding text text-end) | |
704 nil | |
705 ;; cram the error message into the description slot | |
706 (car error-data) | |
707 ;; mark as an attachment to improve the chance that the user | |
708 ;; will see the description. | |
709 '("attachment") | |
710 header | |
711 text | |
712 text-end))))) | |
713 | |
714 (defun vm-mime-get-xxx-parameter (layout name param-list) | |
715 (let ((match-end (1+ (length name))) | |
716 (name-regexp (concat (regexp-quote name) "=")) | |
717 (case-fold-search t) | |
718 (done nil)) | |
719 (while (and param-list (not done)) | |
720 (if (and (string-match name-regexp (car param-list)) | |
721 (= (match-end 0) match-end)) | |
722 (setq done t) | |
723 (setq param-list (cdr param-list)))) | |
724 (and (car param-list) (car (vm-parse (car param-list) "=\\(.*\\)"))))) | |
725 | |
726 (defun vm-mime-get-parameter (layout name) | |
727 (vm-mime-get-xxx-parameter layout name (cdr (vm-mm-layout-type layout)))) | |
728 | |
729 (defun vm-mime-get-disposition-parameter (layout name) | |
730 (vm-mime-get-xxx-parameter layout name | |
731 (cdr (vm-mm-layout-disposition layout)))) | |
732 | |
733 (defun vm-mime-insert-mime-body (layout) | |
734 (vm-insert-region-from-buffer (marker-buffer (vm-mm-layout-body-start layout)) | |
735 (vm-mm-layout-body-start layout) | |
736 (vm-mm-layout-body-end layout))) | |
737 | |
738 (defun vm-mime-insert-mime-headers (layout) | |
739 (vm-insert-region-from-buffer (marker-buffer (vm-mm-layout-body-start layout)) | |
740 (vm-mm-layout-header-start layout) | |
741 (vm-mm-layout-body-start layout)) | |
742 (if (and (not (bobp)) (char-equal (char-after (1- (point))) ?\n)) | |
743 (delete-char -1))) | |
744 | |
745 (defun vm-make-presentation-copy (m) | |
746 (let ((mail-buffer (current-buffer)) | |
747 b mm | |
748 (real-m (vm-real-message-of m)) | |
749 (modified (buffer-modified-p))) | |
750 (cond ((or (null vm-presentation-buffer-handle) | |
751 (null (buffer-name vm-presentation-buffer-handle))) | |
752 (setq b (generate-new-buffer (concat (buffer-name) | |
753 " Presentation"))) | |
754 (save-excursion | |
755 (set-buffer b) | |
756 (if (fboundp 'buffer-disable-undo) | |
757 (buffer-disable-undo (current-buffer)) | |
758 ;; obfuscation to make the v19 compiler not whine | |
759 ;; about obsolete functions. | |
760 (let ((x 'buffer-flush-undo)) | |
761 (funcall x (current-buffer)))) | |
762 (setq mode-name "VM Presentation" | |
763 major-mode 'vm-presentation-mode | |
764 vm-message-pointer (list nil) | |
765 vm-mail-buffer mail-buffer | |
766 mode-popup-menu (and vm-use-menus vm-popup-menu-on-mouse-3 | |
767 (vm-menu-support-possible-p) | |
768 (vm-menu-mode-menu)) | |
769 buffer-read-only t | |
770 mode-line-format vm-mode-line-format) | |
771 (cond ((vm-fsfemacs-19-p) | |
772 ;; need to do this outside the let because | |
773 ;; loading disp-table initializes | |
774 ;; standard-display-table. | |
775 (require 'disp-table) | |
776 (let* ((standard-display-table | |
777 (copy-sequence standard-display-table))) | |
778 (standard-display-european t) | |
779 (setq buffer-display-table standard-display-table)))) | |
780 (if vm-frame-per-folder | |
781 (vm-set-hooks-for-frame-deletion)) | |
782 (use-local-map vm-mode-map) | |
783 (and (vm-toolbar-support-possible-p) vm-use-toolbar | |
784 (vm-toolbar-install-toolbar)) | |
785 (and (vm-menu-support-possible-p) | |
786 (vm-menu-install-menus))) | |
787 (setq vm-presentation-buffer-handle b))) | |
788 ;; do this (widen) outside save-restricton intentionally. since | |
789 ;; we're using the presentation buffer, make the folder | |
790 ;; buffer unpretty so maybe the user gets the idea. | |
791 ;;(widen) | |
792 ;; widening isn't enough. users just complain that "I'm | |
793 ;; looking at the wrong message." Curse their miserable hides. | |
794 ;; bury the buffer so they'll have a tough time finding it. | |
795 (bury-buffer (current-buffer)) | |
796 (setq b vm-presentation-buffer-handle | |
797 vm-presentation-buffer vm-presentation-buffer-handle | |
798 vm-mime-decoded nil) | |
799 (save-excursion | |
800 (set-buffer (vm-buffer-of real-m)) | |
801 (save-restriction | |
802 (widen) | |
803 ;; must reference this now so that headers will be in | |
804 ;; their final position before the message is copied. | |
805 ;; otherwise the vheader offset computed below will be | |
806 ;; wrong. | |
807 (vm-vheaders-of real-m) | |
808 (set-buffer b) | |
809 (widen) | |
810 (let ((buffer-read-only nil) | |
811 (modified (buffer-modified-p))) | |
812 (unwind-protect | |
813 (progn | |
814 (erase-buffer) | |
815 (insert-buffer-substring (vm-buffer-of real-m) | |
816 (vm-start-of real-m) | |
817 (vm-end-of real-m))) | |
818 (set-buffer-modified-p modified))) | |
819 (setq mm (copy-sequence m)) | |
820 (vm-set-location-data-of mm (vm-copy (vm-location-data-of m))) | |
821 (set-marker (vm-start-of mm) (point-min)) | |
822 (set-marker (vm-headers-of mm) (+ (vm-start-of mm) | |
823 (- (vm-headers-of real-m) | |
824 (vm-start-of real-m)))) | |
825 (set-marker (vm-vheaders-of mm) (+ (vm-start-of mm) | |
826 (- (vm-vheaders-of real-m) | |
827 (vm-start-of real-m)))) | |
828 (set-marker (vm-text-of mm) (+ (vm-start-of mm) | |
829 (- (vm-text-of real-m) | |
830 (vm-start-of real-m)))) | |
831 (set-marker (vm-text-end-of mm) (+ (vm-start-of mm) | |
832 (- (vm-text-end-of real-m) | |
833 (vm-start-of real-m)))) | |
834 (set-marker (vm-end-of mm) (+ (vm-start-of mm) | |
835 (- (vm-end-of real-m) | |
836 (vm-start-of real-m)))) | |
837 (setcar vm-message-pointer mm))))) | |
838 | |
839 (fset 'vm-presentation-mode 'vm-mode) | |
840 (put 'vm-presentation-mode 'mode-class 'special) | |
841 | |
842 (defun vm-determine-proper-charset (beg end) | |
843 (save-excursion | |
844 (save-restriction | |
845 (narrow-to-region beg end) | |
846 (catch 'done | |
847 (goto-char (point-min)) | |
848 (and (re-search-forward "[^\000-\177]" nil t) | |
849 (throw 'done (or vm-mime-8bit-composition-charset "iso-8859-1"))) | |
850 (throw 'done "us-ascii"))))) | |
851 | |
852 (defun vm-determine-proper-content-transfer-encoding (beg end) | |
853 (save-excursion | |
854 (save-restriction | |
855 (narrow-to-region beg end) | |
856 (catch 'done | |
857 (goto-char (point-min)) | |
858 (and (re-search-forward "[\000\015]" nil t) | |
859 (throw 'done "binary")) | |
860 | |
861 (let ((toolong nil) bol) | |
862 (goto-char (point-min)) | |
863 (setq bol (point)) | |
864 (while (and (not (eobp)) (not toolong)) | |
865 (forward-line) | |
866 (setq toolong (> (- (point) bol) 998) | |
867 bol (point))) | |
868 (and toolong (throw 'done "binary"))) | |
869 | |
870 (goto-char (point-min)) | |
871 (and (re-search-forward "[\200-\377]" nil t) | |
872 (throw 'done "8bit")) | |
873 | |
874 "7bit")))) | |
875 | |
876 (defun vm-mime-types-match (type type/subtype) | |
877 (let ((case-fold-search t)) | |
878 (cond ((string-match "/" type) | |
879 (if (and (string-match (regexp-quote type) type/subtype) | |
880 (equal 0 (match-beginning 0)) | |
881 (equal (length type/subtype) (match-end 0))) | |
882 t | |
883 nil )) | |
884 ((and (string-match (regexp-quote type) type/subtype) | |
885 (equal 0 (match-beginning 0)) | |
886 (equal (save-match-data | |
887 (string-match "/" type/subtype (match-end 0))) | |
888 (match-end 0))))))) | |
889 | |
890 (defvar native-sound-only-on-console) | |
891 | |
892 (defun vm-mime-can-display-internal (layout) | |
893 (let ((type (car (vm-mm-layout-type layout)))) | |
894 (cond ((vm-mime-types-match "image/jpeg" type) | |
895 (and (vm-xemacs-p) | |
896 (featurep 'jpeg) | |
897 (eq (device-type) 'x))) | |
898 ((vm-mime-types-match "image/gif" type) | |
899 (and (vm-xemacs-p) | |
900 (featurep 'gif) | |
901 (eq (device-type) 'x))) | |
902 ((vm-mime-types-match "image/png" type) | |
903 (and (vm-xemacs-p) | |
904 (featurep 'png) | |
905 (eq (device-type) 'x))) | |
906 ((vm-mime-types-match "image/tiff" type) | |
907 (and (vm-xemacs-p) | |
908 (featurep 'tiff) | |
909 (eq (device-type) 'x))) | |
910 ((vm-mime-types-match "audio/basic" type) | |
911 (and (vm-xemacs-p) | |
912 (or (featurep 'native-sound) | |
913 (featurep 'nas-sound)) | |
914 (or (device-sound-enabled-p) | |
915 (and (featurep 'native-sound) | |
916 (not native-sound-only-on-console) | |
917 (eq (device-type) 'x))))) | |
918 ((vm-mime-types-match "multipart" type) t) | |
919 ((vm-mime-types-match "message/external-body" type) nil) | |
920 ((vm-mime-types-match "message" type) t) | |
921 ((or (vm-mime-types-match "text/plain" type) | |
922 (vm-mime-types-match "text/enriched" type)) | |
923 (let ((charset (or (vm-mime-get-parameter layout "charset") | |
924 "us-ascii"))) | |
925 (vm-mime-charset-internally-displayable-p charset))) | |
926 ((vm-mime-types-match "text/html" type) | |
927 (condition-case () | |
928 (progn (require 'w3) | |
929 (fboundp 'w3-region)) | |
930 (error nil))) | |
931 (t nil)))) | |
932 | |
933 (defun vm-mime-can-convert (type) | |
934 (let ((alist vm-mime-type-converter-alist) | |
935 ;; fake layout. make it the wrong length so an error will | |
936 ;; be signaled if vm-mime-can-display-internal ever asks | |
937 ;; for one of the other fields | |
938 (fake-layout (make-vector 1 (list nil))) | |
939 (done nil)) | |
940 (while (and alist (not done)) | |
941 (cond ((and (vm-mime-types-match (car (car alist)) type) | |
942 (or (progn | |
943 (setcar (aref fake-layout 0) (nth 1 (car alist))) | |
944 (vm-mime-can-display-internal fake-layout)) | |
945 (vm-mime-find-external-viewer (nth 1 (car alist))))) | |
946 (setq done t)) | |
947 (t (setq alist (cdr alist))))) | |
948 (and alist (car alist)))) | |
949 | |
950 (defun vm-mime-convert-undisplayable-layout (layout) | |
951 (let ((ooo (vm-mime-can-convert (car (vm-mm-layout-type layout))))) | |
952 (vm-unsaved-message "Converting %s to %s..." | |
953 (car (vm-mm-layout-type layout)) | |
954 (nth 1 ooo)) | |
955 (save-excursion | |
956 (set-buffer (generate-new-buffer " *mime object*")) | |
957 (setq vm-message-garbage-alist | |
958 (cons (cons (current-buffer) 'kill-buffer) | |
959 vm-message-garbage-alist)) | |
960 (vm-mime-insert-mime-body layout) | |
961 (vm-mime-transfer-decode-region layout (point-min) (point-max)) | |
962 (call-process-region (point-min) (point-max) shell-file-name | |
963 t t nil shell-command-switch (nth 2 ooo)) | |
964 (goto-char (point-min)) | |
965 (insert "Content-Type: " (nth 1 ooo) "\n") | |
966 (insert "Content-Transfer-Encoding: binary\n\n") | |
967 (set-buffer-modified-p nil) | |
968 (vm-unsaved-message "Converting %s to %s... done" | |
969 (car (vm-mm-layout-type layout)) | |
970 (nth 1 ooo)) | |
971 (vector (list (nth 1 ooo)) | |
972 "binary" | |
973 (vm-mm-layout-id layout) | |
974 (vm-mm-layout-description layout) | |
975 (vm-mm-layout-disposition layout) | |
976 (vm-marker (point-min)) | |
977 (vm-marker (point)) | |
978 (vm-marker (point-max)) | |
979 nil | |
980 nil )))) | |
981 | |
982 (defun vm-mime-should-display-button (layout dont-honor-content-disposition) | |
983 (if (and vm-honor-mime-content-disposition | |
984 (not dont-honor-content-disposition) | |
985 (vm-mm-layout-disposition layout)) | |
986 (let ((case-fold-search t)) | |
987 (string-match "^attachment$" (car (vm-mm-layout-disposition layout)))) | |
988 (let ((i-list vm-auto-displayed-mime-content-types) | |
989 (type (car (vm-mm-layout-type layout))) | |
990 (matched nil)) | |
991 (if (eq i-list t) | |
992 nil | |
993 (while (and i-list (not matched)) | |
994 (if (vm-mime-types-match (car i-list) type) | |
995 (setq matched t) | |
996 (setq i-list (cdr i-list)))) | |
997 (not matched) )))) | |
998 | |
999 (defun vm-mime-should-display-internal (layout dont-honor-content-disposition) | |
1000 (if (and vm-honor-mime-content-disposition | |
1001 (not dont-honor-content-disposition) | |
1002 (vm-mm-layout-disposition layout)) | |
1003 (let ((case-fold-search t)) | |
1004 (string-match "^inline$" (car (vm-mm-layout-disposition layout)))) | |
1005 (let ((i-list vm-mime-internal-content-types) | |
1006 (type (car (vm-mm-layout-type layout))) | |
1007 (matched nil)) | |
1008 (if (eq i-list t) | |
1009 t | |
1010 (while (and i-list (not matched)) | |
1011 (if (vm-mime-types-match (car i-list) type) | |
1012 (setq matched t) | |
1013 (setq i-list (cdr i-list)))) | |
1014 matched )))) | |
1015 | |
1016 (defun vm-mime-find-external-viewer (type) | |
1017 (let ((e-alist vm-mime-external-content-types-alist) | |
1018 (matched nil)) | |
1019 (while (and e-alist (not matched)) | |
1020 (if (and (vm-mime-types-match (car (car e-alist)) type) | |
1021 (cdr (car e-alist))) | |
1022 (setq matched (cdr (car e-alist))) | |
1023 (setq e-alist (cdr e-alist)))) | |
1024 matched )) | |
1025 (fset 'vm-mime-should-display-external 'vm-mime-find-external-viewer) | |
1026 | |
1027 (defun vm-mime-delete-button-maybe (extent) | |
1028 (let ((buffer-read-only)) | |
1029 ;; if displayed MIME object should replace the button | |
1030 ;; remove the button now. | |
1031 (cond ((vm-extent-property extent 'vm-mime-disposable) | |
1032 (delete-region (vm-extent-start-position extent) | |
1033 (vm-extent-end-position extent)) | |
1034 (vm-detach-extent extent))))) | |
1035 | |
1036 (defun vm-decode-mime-message () | |
1037 "Decode the MIME objects in the current message. | |
1038 | |
1039 The first time this command is run on a message, decoding is done. | |
1040 The second time, buttons for all the objects are displayed instead. | |
1041 The third time, the raw, undecoded data is displayed. | |
1042 | |
1043 If decoding, the decoded objects might be displayed immediately, or | |
1044 buttons might be displayed that you need to activate to view the | |
1045 object. See the documentation for the variables | |
1046 | |
1047 vm-auto-displayed-mime-content-types | |
1048 vm-mime-internal-content-types | |
1049 vm-mime-external-content-types-alist | |
1050 | |
1051 to see how to control whether you see buttons or objects. | |
1052 | |
1053 If the variable vm-mime-display-function is set, then its value | |
1054 is called as a function with no arguments, and none of the | |
1055 actions mentioned in the preceding paragraphs are done. At the | |
1056 time of the call, the current buffer will be the presentation | |
1057 buffer for the folder and a copy of the current message will be | |
1058 in the buffer. The function is expected to make the message | |
1059 `MIME presentable' to the user in whatever manner it sees fit." | |
1060 (interactive) | |
1061 (vm-follow-summary-cursor) | |
1062 (vm-select-folder-buffer) | |
1063 (vm-check-for-killed-summary) | |
1064 (vm-check-for-killed-presentation) | |
1065 (vm-error-if-folder-empty) | |
1066 (if (and (not vm-display-using-mime) | |
1067 (null vm-mime-display-function)) | |
1068 (error "MIME display disabled, set vm-display-using-mime non-nil to enable.")) | |
1069 (if vm-mime-display-function | |
1070 (progn | |
1071 (vm-make-presentation-copy (car vm-message-pointer)) | |
1072 (set-buffer vm-presentation-buffer) | |
1073 (funcall vm-mime-display-function)) | |
1074 (if vm-mime-decoded | |
1075 (if (eq vm-mime-decoded 'decoded) | |
1076 (let ((vm-preview-read-messages nil) | |
1077 (vm-auto-decode-mime-messages t) | |
1078 (vm-honor-mime-content-disposition nil) | |
1079 (vm-auto-displayed-mime-content-types '("multipart"))) | |
1080 (setq vm-mime-decoded nil) | |
1081 (intern (buffer-name) vm-buffers-needing-display-update) | |
1082 (save-excursion | |
1083 (vm-preview-current-message)) | |
1084 (setq vm-mime-decoded 'buttons)) | |
1085 (let ((vm-preview-read-messages nil) | |
1086 (vm-auto-decode-mime-messages nil)) | |
1087 (intern (buffer-name) vm-buffers-needing-display-update) | |
1088 (vm-preview-current-message))) | |
1089 (let ((layout (vm-mm-layout (car vm-message-pointer))) | |
1090 (m (car vm-message-pointer))) | |
1091 (vm-unsaved-message "Decoding MIME message...") | |
1092 (cond ((stringp layout) | |
1093 (error "Invalid MIME message: %s" layout))) | |
1094 (if (vm-mime-plain-message-p m) | |
1095 (error "Message needs no decoding.")) | |
1096 (or vm-presentation-buffer | |
1097 ;; maybe user killed it | |
1098 (error "No presentation buffer.")) | |
1099 (set-buffer vm-presentation-buffer) | |
1100 (setq m (car vm-message-pointer)) | |
1101 (vm-save-restriction | |
1102 (widen) | |
1103 (goto-char (vm-text-of m)) | |
1104 (let ((buffer-read-only nil) | |
1105 (modified (buffer-modified-p))) | |
1106 (unwind-protect | |
1107 (save-excursion | |
1108 (and (not (eq (vm-mm-encoded-header m) 'none)) | |
1109 (vm-decode-mime-message-headers m)) | |
1110 (if (vectorp layout) | |
1111 (progn | |
1112 (vm-decode-mime-layout layout) | |
1113 (delete-region (point) (point-max))))) | |
1114 (set-buffer-modified-p modified)))) | |
1115 (save-excursion (set-buffer vm-mail-buffer) | |
1116 (setq vm-mime-decoded 'decoded)) | |
1117 (intern (buffer-name vm-mail-buffer) vm-buffers-needing-display-update) | |
1118 (vm-update-summary-and-mode-line) | |
1119 (vm-unsaved-message "Decoding MIME message... done")))) | |
1120 (vm-display nil nil '(vm-decode-mime-message) | |
1121 '(vm-decode-mime-message reading-message))) | |
1122 | |
1123 (defun vm-decode-mime-layout (layout &optional dont-honor-c-d) | |
1124 (let ((modified (buffer-modified-p)) type type-no-subtype (extent nil)) | |
1125 (unwind-protect | |
1126 (progn | |
1127 (if (not (vectorp layout)) | |
1128 (progn | |
1129 (setq extent layout | |
1130 layout (vm-extent-property extent 'vm-mime-layout)) | |
1131 (goto-char (vm-extent-start-position extent)))) | |
1132 (setq type (downcase (car (vm-mm-layout-type layout))) | |
1133 type-no-subtype (car (vm-parse type "\\([^/]+\\)"))) | |
1134 (cond ((and (vm-mime-should-display-button layout dont-honor-c-d) | |
1135 (or (condition-case nil | |
1136 (funcall (intern | |
1137 (concat "vm-mime-display-button-" | |
1138 type)) | |
1139 layout) | |
1140 (void-function nil)) | |
1141 (condition-case nil | |
1142 (funcall (intern | |
1143 (concat "vm-mime-display-button-" | |
1144 type-no-subtype)) | |
1145 layout) | |
1146 (void-function nil))))) | |
1147 ((and (vm-mime-should-display-internal layout dont-honor-c-d) | |
1148 (condition-case nil | |
1149 (funcall (intern | |
1150 (concat "vm-mime-display-internal-" | |
1151 type)) | |
1152 layout) | |
1153 (void-function nil)))) | |
1154 ((vm-mime-types-match "multipart" type) | |
1155 (or (condition-case nil | |
1156 (funcall (intern | |
1157 (concat "vm-mime-display-internal-" | |
1158 type)) | |
1159 layout) | |
1160 (void-function nil)) | |
1161 (vm-mime-display-internal-multipart/mixed layout))) | |
1162 ((and (vm-mime-should-display-external type) | |
1163 (vm-mime-display-external-generic layout)) | |
1164 (and extent (vm-set-extent-property | |
1165 extent 'vm-mime-disposable nil))) | |
1166 ((vm-mime-can-convert type) | |
1167 (vm-decode-mime-layout | |
1168 (vm-mime-convert-undisplayable-layout layout))) | |
1169 ((and (or (vm-mime-types-match "message" type) | |
1170 (vm-mime-types-match "text" type)) | |
1171 ;; display unmatched message and text types as | |
1172 ;; text/plain. | |
1173 (vm-mime-display-internal-text/plain layout))) | |
1174 (t (vm-mime-display-internal-application/octet-stream | |
1175 (or extent layout)))) | |
1176 (and extent (vm-mime-delete-button-maybe extent))) | |
1177 (set-buffer-modified-p modified))) | |
1178 t ) | |
1179 | |
1180 (defun vm-mime-display-button-text (layout) | |
1181 (vm-mime-display-button-xxxx layout t)) | |
1182 | |
1183 (defun vm-mime-display-internal-text/html (layout) | |
1184 (let ((buffer-read-only nil) | |
1185 (work-buffer nil)) | |
1186 (vm-unsaved-message "Inlining text/html, be patient...") | |
1187 ;; w3-region is not as tame as we would like. | |
1188 ;; make sure the yoke is firmly attached. | |
1189 (unwind-protect | |
1190 (progn | |
1191 (save-excursion | |
1192 (set-buffer (setq work-buffer | |
1193 (generate-new-buffer " *workbuf*"))) | |
1194 (vm-mime-insert-mime-body layout) | |
1195 (vm-mime-transfer-decode-region layout (point-min) (point-max)) | |
1196 (save-excursion | |
1197 (save-window-excursion | |
1198 (w3-region (point-min) (point-max))))) | |
1199 (insert-buffer-substring work-buffer)) | |
1200 (and work-buffer (kill-buffer work-buffer))) | |
1201 (vm-unsaved-message "Inlining text/html... done") | |
1202 t )) | |
1203 | |
1204 (defun vm-mime-display-internal-text/plain (layout &optional ignore-urls) | |
1205 (let ((start (point)) end | |
1206 (buffer-read-only nil) | |
1207 (charset (or (vm-mime-get-parameter layout "charset") "us-ascii"))) | |
1208 (if (not (vm-mime-charset-internally-displayable-p charset)) | |
1209 nil | |
1210 (vm-mime-insert-mime-body layout) | |
1211 (setq end (point-marker)) | |
1212 (vm-mime-transfer-decode-region layout start end) | |
1213 (vm-mime-charset-decode-region charset start end) | |
1214 (or ignore-urls (vm-energize-urls-in-message-region start end)) | |
1215 t ))) | |
1216 | |
1217 (defun vm-mime-display-internal-text/enriched (layout) | |
1218 (require 'enriched) | |
1219 (let ((start (point)) end | |
1220 (buffer-read-only nil) | |
1221 (enriched-verbose t)) | |
1222 (vm-unsaved-message "Decoding text/enriched, be patient...") | |
1223 (vm-mime-insert-mime-body layout) | |
1224 (setq end (point-marker)) | |
1225 (vm-mime-transfer-decode-region layout start end) | |
1226 ;; enriched-decode expects a couple of headers at the top of | |
1227 ;; the region and will remove anything that looks like a | |
1228 ;; header. Put a header section here for it to eat so it | |
1229 ;; won't eat message text instead. | |
1230 (goto-char start) | |
1231 (insert "Comment: You should not see this header\n\n") | |
1232 (enriched-decode start end) | |
1233 (vm-energize-urls-in-message-region start end) | |
1234 (goto-char end) | |
1235 (vm-unsaved-message "Decoding text/enriched... done") | |
1236 t )) | |
1237 | |
1238 (defun vm-mime-display-external-generic (layout) | |
1239 (let ((program-list (vm-mime-find-external-viewer | |
1240 (car (vm-mm-layout-type layout)))) | |
1241 (process (nth 0 (vm-mm-layout-cache layout))) | |
1242 (tempfile (nth 1 (vm-mm-layout-cache layout))) | |
1243 (buffer-read-only nil) | |
1244 (start (point)) | |
1245 end) | |
1246 (if (and (processp process) (eq (process-status process) 'run)) | |
1247 nil | |
1248 (cond ((or (null tempfile) (null (file-exists-p tempfile))) | |
1249 (vm-mime-insert-mime-body layout) | |
1250 (setq end (point-marker)) | |
1251 (vm-mime-transfer-decode-region layout start end) | |
1252 (setq tempfile (vm-make-tempfile-name)) | |
1253 ;; Tell DOS/Windows NT whether the file is binary | |
1254 (setq buffer-file-type (not (vm-mime-text-type-p layout))) | |
1255 (write-region start end tempfile nil 0) | |
1256 (delete-region start end) | |
1257 (save-excursion | |
1258 (vm-select-folder-buffer) | |
1259 (setq vm-folder-garbage-alist | |
1260 (cons (cons tempfile 'delete-file) | |
1261 vm-folder-garbage-alist))))) | |
1262 (vm-unsaved-message "Launching %s..." (mapconcat 'identity | |
1263 program-list | |
1264 " ")) | |
1265 (setq process | |
1266 (apply 'start-process | |
1267 (format "view %25s" (vm-mime-layout-description layout)) | |
1268 nil (append program-list (list tempfile)))) | |
1269 (process-kill-without-query process t) | |
1270 (vm-unsaved-message "Launching %s... done" (mapconcat 'identity | |
1271 program-list | |
1272 " ")) | |
1273 (save-excursion | |
1274 (vm-select-folder-buffer) | |
1275 (setq vm-message-garbage-alist | |
1276 (cons (cons process 'delete-process) | |
1277 vm-message-garbage-alist))) | |
1278 (vm-set-mm-layout-cache layout (list process tempfile)))) | |
1279 t ) | |
1280 | |
1281 (defun vm-mime-display-internal-application/octet-stream (layout) | |
1282 (if (vectorp layout) | |
1283 (let ((buffer-read-only nil) | |
1284 (description (vm-mm-layout-description layout))) | |
1285 (vm-mime-insert-button | |
1286 (format "%-35s [%s to save to a file]" | |
1287 (vm-mime-layout-description layout) | |
1288 (if (vm-mouse-support-possible-p) | |
1289 "Click mouse-2" | |
1290 "Press RETURN")) | |
1291 (function | |
1292 (lambda (layout) | |
1293 (save-excursion | |
1294 (vm-mime-display-internal-application/octet-stream layout)))) | |
1295 layout nil)) | |
1296 (goto-char (vm-extent-start-position layout)) | |
1297 (setq layout (vm-extent-property layout 'vm-mime-layout)) | |
1298 ;; support old "name" paramater for application/octet-stream | |
1299 ;; but don't override the "filename" parameter extracted from | |
1300 ;; Content-Disposition, if any. | |
1301 (let ((default-filename | |
1302 (if (vm-mime-get-disposition-parameter layout "filename") | |
1303 nil | |
1304 (vm-mime-get-parameter layout "name")))) | |
1305 (vm-mime-send-body-to-file layout default-filename))) | |
1306 t ) | |
1307 (fset 'vm-mime-display-button-application | |
1308 'vm-mime-display-internal-application/octet-stream) | |
1309 | |
1310 (defun vm-mime-display-button-image (layout) | |
1311 (vm-mime-display-button-xxxx layout t)) | |
1312 | |
1313 (defun vm-mime-display-button-audio (layout) | |
1314 (vm-mime-display-button-xxxx layout nil)) | |
1315 | |
1316 (defun vm-mime-display-button-video (layout) | |
1317 (vm-mime-display-button-xxxx layout t)) | |
1318 | |
1319 (defun vm-mime-display-button-message (layout) | |
1320 (vm-mime-display-button-xxxx layout t)) | |
1321 | |
1322 (defun vm-mime-display-button-multipart (layout) | |
1323 (vm-mime-display-button-xxxx layout t)) | |
1324 | |
1325 (defun vm-mime-display-internal-multipart/mixed (layout) | |
1326 (let ((part-list (vm-mm-layout-parts layout))) | |
1327 (while part-list | |
1328 (vm-decode-mime-layout (car part-list)) | |
1329 (setq part-list (cdr part-list))) | |
1330 t )) | |
1331 | |
1332 (defun vm-mime-display-internal-multipart/alternative (layout) | |
1333 (let (best-layout) | |
1334 (cond ((eq vm-mime-alternative-select-method 'best) | |
1335 (let ((done nil) | |
1336 (best nil) | |
1337 part-list type) | |
1338 (setq part-list (vm-mm-layout-parts layout) | |
1339 part-list (nreverse (copy-sequence part-list))) | |
1340 (while (and part-list (not done)) | |
1341 (setq type (car (vm-mm-layout-type (car part-list)))) | |
1342 (if (or (vm-mime-can-display-internal (car part-list)) | |
1343 (vm-mime-find-external-viewer type)) | |
1344 (setq best (car part-list) | |
1345 done t) | |
1346 (setq part-list (cdr part-list)))) | |
1347 (setq best-layout (or best (car (vm-mm-layout-parts layout)))))) | |
1348 ((eq vm-mime-alternative-select-method 'best-internal) | |
1349 (let ((done nil) | |
1350 (best nil) | |
1351 (second-best nil) | |
1352 part-list type) | |
1353 (setq part-list (vm-mm-layout-parts layout) | |
1354 part-list (nreverse (copy-sequence part-list))) | |
1355 (while (and part-list (not done)) | |
1356 (setq type (car (vm-mm-layout-type (car part-list)))) | |
1357 (cond ((vm-mime-can-display-internal (car part-list)) | |
1358 (setq best (car part-list) | |
1359 done t)) | |
1360 ((and (null second-best) | |
1361 (vm-mime-find-external-viewer type)) | |
1362 (setq second-best (car part-list)))) | |
1363 (setq part-list (cdr part-list))) | |
1364 (setq best-layout (or best second-best | |
1365 (car (vm-mm-layout-parts layout))))))) | |
1366 (vm-decode-mime-layout best-layout))) | |
1367 | |
1368 (defun vm-mime-display-button-multipart/parallel (layout) | |
1369 (vm-mime-insert-button | |
1370 (format "%-35s [%s to display in parallel]" | |
1371 (vm-mime-layout-description layout) | |
1372 (if (vm-mouse-support-possible-p) | |
1373 "Click mouse-2" | |
1374 "Press RETURN")) | |
1375 (function | |
1376 (lambda (layout) | |
1377 (save-excursion | |
1378 (let ((vm-auto-displayed-mime-content-types t)) | |
1379 (vm-decode-mime-layout layout t))))) | |
1380 layout t)) | |
1381 | |
1382 (fset 'vm-mime-display-internal-multipart/parallel | |
1383 'vm-mime-display-internal-multipart/mixed) | |
1384 | |
1385 (defun vm-mime-display-internal-multipart/digest (layout) | |
1386 (if (vectorp layout) | |
1387 (let ((buffer-read-only nil)) | |
1388 (vm-mime-insert-button | |
1389 (format "%-35s [%s to display]" | |
1390 (vm-mime-layout-description layout) | |
1391 (if (vm-mouse-support-possible-p) | |
1392 "Click mouse-2" | |
1393 "Press RETURN")) | |
1394 (function | |
1395 (lambda (layout) | |
1396 (save-excursion | |
1397 (vm-mime-display-internal-multipart/digest layout)))) | |
1398 layout nil)) | |
1399 (goto-char (vm-extent-start-position layout)) | |
1400 (setq layout (vm-extent-property layout 'vm-mime-layout)) | |
1401 (set-buffer (generate-new-buffer (format "digest from %s/%s" | |
1402 (buffer-name vm-mail-buffer) | |
1403 (vm-number-of | |
1404 (car vm-message-pointer))))) | |
1405 (setq vm-folder-type vm-default-folder-type) | |
1406 (vm-mime-burst-layout layout nil) | |
1407 (vm-save-buffer-excursion | |
1408 (vm-goto-new-folder-frame-maybe 'folder) | |
1409 (vm-mode)) | |
1410 ;; temp buffer, don't offer to save it. | |
1411 (setq buffer-offer-save nil) | |
1412 (vm-display nil nil (list this-command) '(vm-mode startup))) | |
1413 t ) | |
1414 (fset 'vm-mime-display-button-multipart/digest | |
1415 'vm-mime-display-internal-multipart/digest) | |
1416 | |
1417 (defun vm-mime-display-internal-message/rfc822 (layout) | |
1418 (if (vectorp layout) | |
1419 (let ((buffer-read-only nil)) | |
1420 (vm-mime-insert-button | |
1421 (format "%-35s [%s to display]" | |
1422 (vm-mime-layout-description layout) | |
1423 (if (vm-mouse-support-possible-p) | |
1424 "Click mouse-2" | |
1425 "Press RETURN")) | |
1426 (function | |
1427 (lambda (layout) | |
1428 (save-excursion | |
1429 (vm-mime-display-internal-message/rfc822 layout)))) | |
1430 layout nil)) | |
1431 (goto-char (vm-extent-start-position layout)) | |
1432 (setq layout (vm-extent-property layout 'vm-mime-layout)) | |
1433 (set-buffer (generate-new-buffer | |
1434 (format "message from %s/%s" | |
1435 (buffer-name vm-mail-buffer) | |
1436 (vm-number-of | |
1437 (car vm-message-pointer))))) | |
1438 (setq vm-folder-type vm-default-folder-type) | |
1439 (vm-mime-burst-layout layout nil) | |
1440 (set-buffer-modified-p nil) | |
1441 (vm-save-buffer-excursion | |
1442 (vm-goto-new-folder-frame-maybe 'folder) | |
1443 (vm-mode)) | |
1444 ;; temp buffer, don't offer to save it. | |
1445 (setq buffer-offer-save nil) | |
1446 (vm-display (or vm-presentation-buffer (current-buffer)) t | |
1447 (list this-command) '(vm-mode startup))) | |
1448 t ) | |
1449 (fset 'vm-mime-display-button-message/rfc822 | |
1450 'vm-mime-display-internal-message/rfc822) | |
1451 | |
1452 (defun vm-mime-display-internal-message/partial (layout) | |
1453 (if (vectorp layout) | |
1454 (let ((buffer-read-only nil) | |
1455 (number (vm-mime-get-parameter layout "number")) | |
1456 (total (vm-mime-get-parameter layout "total"))) | |
1457 (vm-mime-insert-button | |
1458 (format "%-35s [%s to attempt assembly]" | |
1459 (concat (vm-mime-layout-description layout) | |
1460 (and number (concat ", part " number)) | |
1461 (and number total (concat " of " total))) | |
1462 (if (vm-mouse-support-possible-p) | |
1463 "Click mouse-2" | |
1464 "Press RETURN")) | |
1465 (function | |
1466 (lambda (layout) | |
1467 (save-excursion | |
1468 (vm-mime-display-internal-message/partial layout)))) | |
1469 layout nil)) | |
1470 (vm-unsaved-message "Assembling message...") | |
1471 (let ((parts nil) | |
1472 (missing nil) | |
1473 (work-buffer nil) | |
1474 extent id o number total m i prev part-header-pos | |
1475 p-id p-number p-total p-list) | |
1476 (setq extent layout | |
1477 layout (vm-extent-property extent 'vm-mime-layout) | |
1478 id (vm-mime-get-parameter layout "id")) | |
1479 (if (null id) | |
1480 (vm-mime-error | |
1481 "message/partial message missing id parameter")) | |
1482 (save-excursion | |
1483 (set-buffer (marker-buffer (vm-mm-layout-body-start layout))) | |
1484 (save-excursion | |
1485 (save-restriction | |
1486 (widen) | |
1487 (goto-char (point-min)) | |
1488 (while (and (search-forward id nil t) | |
1489 (setq m (vm-message-at-point))) | |
1490 (setq o (vm-mm-layout m)) | |
1491 (if (not (vectorp o)) | |
1492 nil | |
1493 (setq p-list (vm-mime-find-message/partials o id)) | |
1494 (while p-list | |
1495 (setq p-id (vm-mime-get-parameter (car p-list) "id")) | |
1496 (setq p-total (vm-mime-get-parameter (car p-list) "total")) | |
1497 (if (null p-total) | |
1498 nil | |
1499 (setq p-total (string-to-int p-total)) | |
1500 (if (< p-total 1) | |
1501 (vm-mime-error "message/partial specified part total < 0, %d" p-total)) | |
1502 (if total | |
1503 (if (not (= total p-total)) | |
1504 (vm-mime-error "message/partial speificed total differs between parts, (%d != %d)" p-total total)) | |
1505 (setq total p-total))) | |
1506 (setq p-number (vm-mime-get-parameter (car p-list) "number")) | |
1507 (if (null p-number) | |
1508 (vm-mime-error | |
1509 "message/partial message missing number parameter")) | |
1510 (setq p-number (string-to-int p-number)) | |
1511 (if (< p-number 1) | |
1512 (vm-mime-error "message/partial part number < 0, %d" | |
1513 p-number)) | |
1514 (if (and total (> p-number total)) | |
1515 (vm-mime-error "message/partial part number greater than expected number of parts, (%d > %d)" p-number total)) | |
1516 (setq parts (cons (list p-number (car p-list)) parts) | |
1517 p-list (cdr p-list)))) | |
1518 (goto-char (vm-mm-layout-body-end o)))))) | |
1519 (if (null total) | |
1520 (vm-mime-error "total number of parts not specified in any message/partial part")) | |
1521 (setq parts (sort parts | |
1522 (function | |
1523 (lambda (p q) | |
1524 (< (car p) | |
1525 (car q)))))) | |
1526 (setq i 0 | |
1527 p-list parts) | |
1528 (while p-list | |
1529 (cond ((< i (car (car p-list))) | |
1530 (vm-increment i) | |
1531 (cond ((not (= i (car (car p-list)))) | |
1532 (setq missing (cons i missing))) | |
1533 (t (setq prev p-list | |
1534 p-list (cdr p-list))))) | |
1535 (t | |
1536 ;; remove duplicate part | |
1537 (setcdr prev (cdr p-list)) | |
1538 (setq p-list (cdr p-list))))) | |
1539 (while (< i total) | |
1540 (vm-increment i) | |
1541 (setq missing (cons i missing))) | |
1542 (if missing | |
1543 (vm-mime-error "part%s %s%s missing" | |
1544 (if (cdr missing) "s" "") | |
1545 (mapconcat | |
1546 (function identity) | |
1547 (nreverse (mapcar 'int-to-string | |
1548 (or (cdr missing) missing))) | |
1549 ", ") | |
1550 (if (cdr missing) | |
1551 (concat " and " (car missing)) | |
1552 ""))) | |
1553 (set-buffer (generate-new-buffer "assembled message")) | |
1554 (setq vm-folder-type vm-default-folder-type) | |
1555 (vm-mime-insert-mime-headers (car (cdr (car parts)))) | |
1556 (goto-char (point-min)) | |
1557 (vm-reorder-message-headers | |
1558 nil nil | |
1559 "\\(Encrypted\\|Content-\\|MIME-Version\\|Message-ID\\|Subject\\|X-VM-\\|Status\\)") | |
1560 (goto-char (point-max)) | |
1561 (setq part-header-pos (point)) | |
1562 (while parts | |
1563 (vm-mime-insert-mime-body (car (cdr (car parts)))) | |
1564 (setq parts (cdr parts))) | |
1565 (goto-char part-header-pos) | |
1566 (vm-reorder-message-headers | |
1567 nil '("Subject" "MIME-Version" "Content-" "Message-ID" "Encrypted") nil) | |
1568 (vm-munge-message-separators vm-folder-type (point-min) (point-max)) | |
1569 (goto-char (point-min)) | |
1570 (insert (vm-leading-message-separator)) | |
1571 (goto-char (point-max)) | |
1572 (insert (vm-trailing-message-separator)) | |
1573 (set-buffer-modified-p nil) | |
1574 (vm-unsaved-message "Assembling message... done") | |
1575 (vm-save-buffer-excursion | |
1576 (vm-goto-new-folder-frame-maybe 'folder) | |
1577 (vm-mode)) | |
1578 ;; temp buffer, don't offer to save it. | |
1579 (setq buffer-offer-save nil) | |
1580 (vm-display (or vm-presentation-buffer (current-buffer)) t | |
1581 (list this-command) '(vm-mode startup))) | |
1582 t )) | |
1583 (fset 'vm-mime-display-button-message/partial | |
1584 'vm-mime-display-internal-message/partial) | |
1585 | |
1586 (defun vm-mime-display-internal-image-xxxx (layout feature name) | |
1587 (if (and (vm-xemacs-p) | |
1588 (featurep feature) | |
1589 (eq (device-type) 'x)) | |
1590 (let ((start (point)) end tempfile g e | |
1591 (buffer-read-only nil)) | |
1592 (if (vm-mm-layout-cache layout) | |
1593 (setq g (vm-mm-layout-cache layout)) | |
1594 (vm-mime-insert-mime-body layout) | |
1595 (setq end (point-marker)) | |
1596 (vm-mime-transfer-decode-region layout start end) | |
1597 (setq tempfile (vm-make-tempfile-name)) | |
1598 (write-region start end tempfile nil 0) | |
1599 (vm-unsaved-message "Creating %s glyph..." name) | |
1600 (setq g (make-glyph | |
1601 (list (vector feature ':file tempfile) | |
1602 (vector 'string | |
1603 ':data | |
1604 (format "[Unknown %s image encoding]\n" | |
1605 name))))) | |
1606 (vm-unsaved-message "") | |
1607 (vm-set-mm-layout-cache layout g) | |
1608 (save-excursion | |
1609 (vm-select-folder-buffer) | |
1610 (setq vm-folder-garbage-alist | |
1611 (cons (cons tempfile 'delete-file) | |
1612 vm-folder-garbage-alist))) | |
1613 (delete-region start end)) | |
1614 (if (not (bolp)) | |
1615 (insert-char ?\n 2) | |
1616 (insert-char ?\n 1)) | |
1617 (setq e (vm-make-extent (1- (point)) (point))) | |
1618 (vm-set-extent-property e 'begin-glyph g) | |
1619 t ))) | |
1620 | |
1621 (defun vm-mime-display-internal-image/gif (layout) | |
1622 (vm-mime-display-internal-image-xxxx layout 'gif "GIF")) | |
1623 | |
1624 (defun vm-mime-display-internal-image/jpeg (layout) | |
1625 (vm-mime-display-internal-image-xxxx layout 'jpeg "JPEG")) | |
1626 | |
1627 (defun vm-mime-display-internal-image/png (layout) | |
1628 (vm-mime-display-internal-image-xxxx layout 'png "PNG")) | |
1629 | |
1630 (defun vm-mime-display-internal-image/tiff (layout) | |
1631 (vm-mime-display-internal-image-xxxx layout 'tiff "TIFF")) | |
1632 | |
1633 (defun vm-mime-display-internal-audio/basic (layout) | |
1634 (if (and (vm-xemacs-p) | |
1635 (or (featurep 'native-sound) | |
1636 (featurep 'nas-sound)) | |
1637 (or (device-sound-enabled-p) | |
1638 (and (featurep 'native-sound) | |
1639 (not native-sound-only-on-console) | |
1640 (eq (device-type) 'x)))) | |
1641 (let ((start (point)) end tempfile | |
1642 (buffer-read-only nil)) | |
1643 (if (vm-mm-layout-cache layout) | |
1644 (setq tempfile (vm-mm-layout-cache layout)) | |
1645 (vm-mime-insert-mime-body layout) | |
1646 (setq end (point-marker)) | |
1647 (vm-mime-transfer-decode-region layout start end) | |
1648 (setq tempfile (vm-make-tempfile-name)) | |
1649 (write-region start end tempfile nil 0) | |
1650 (vm-set-mm-layout-cache layout tempfile) | |
1651 (save-excursion | |
1652 (vm-select-folder-buffer) | |
1653 (setq vm-folder-garbage-alist | |
1654 (cons (cons tempfile 'delete-file) | |
1655 vm-folder-garbage-alist))) | |
1656 (delete-region start end)) | |
1657 (start-itimer "audioplayer" | |
1658 (list 'lambda nil (list 'play-sound-file tempfile)) | |
1659 1) | |
1660 t ) | |
1661 nil )) | |
1662 | |
1663 (defun vm-mime-display-button-xxxx (layout disposable) | |
1664 (let ((description (vm-mime-layout-description layout))) | |
1665 (vm-mime-insert-button | |
1666 (format "%-35s [%s to display]" | |
1667 description | |
1668 (if (vm-mouse-support-possible-p) "Click mouse-2" "Press RETURN")) | |
1669 (function | |
1670 (lambda (layout) | |
1671 (save-excursion | |
1672 (let ((vm-auto-displayed-mime-content-types t)) | |
1673 (vm-decode-mime-layout layout t))))) | |
1674 layout disposable) | |
1675 t )) | |
1676 | |
1677 (defun vm-mime-run-display-function-at-point (&optional function) | |
1678 (interactive) | |
1679 ;; save excursion to keep point from moving. its motion would | |
1680 ;; drag window point along, to a place arbitrarily far from | |
1681 ;; where it was when the user triggered the button. | |
1682 (save-excursion | |
1683 (cond ((vm-fsfemacs-19-p) | |
1684 (let (o-list o (found nil)) | |
1685 (setq o-list (overlays-at (point))) | |
1686 (while (and o-list (not found)) | |
1687 (cond ((overlay-get (car o-list) 'vm-mime-layout) | |
1688 (setq found t) | |
1689 (funcall (or function (overlay-get (car o-list) | |
1690 'vm-mime-function)) | |
1691 (car o-list)))) | |
1692 (setq o-list (cdr o-list))))) | |
1693 ((vm-xemacs-p) | |
1694 (let ((e (extent-at (point) nil 'vm-mime-layout))) | |
1695 (funcall (or function (extent-property e 'vm-mime-function)) | |
1696 e)))))) | |
1697 | |
1698 ;; for the karking compiler | |
1699 (defvar vm-menu-mime-dispose-menu) | |
1700 | |
1701 (defun vm-mime-insert-button (caption action layout disposable) | |
1702 (let ((start (point)) e | |
1703 (keymap (make-sparse-keymap)) | |
1704 (buffer-read-only nil)) | |
1705 (if (fboundp 'set-keymap-parents) | |
1706 (set-keymap-parents keymap (list (current-local-map))) | |
1707 (setq keymap (nconc keymap (current-local-map)))) | |
1708 (define-key keymap "\r" 'vm-mime-run-display-function-at-point) | |
1709 (if (and (vm-mouse-xemacs-mouse-p) vm-popup-menu-on-mouse-3) | |
1710 (define-key keymap 'button3 'vm-menu-popup-mime-dispose-menu)) | |
1711 (if (not (bolp)) | |
1712 (insert "\n")) | |
1713 (insert caption "\n") | |
1714 ;; we MUST have the five arg make-overlay. overlays must | |
1715 ;; advance when text is inserted at their start position or | |
1716 ;; inline text and graphics will seep into the button | |
1717 ;; overlay and then be removed when the button is removed. | |
1718 (if (fboundp 'make-overlay) | |
1719 (setq e (make-overlay start (point) nil t nil)) | |
1720 (setq e (make-extent start (point))) | |
1721 (set-extent-property e 'start-open t) | |
1722 (set-extent-property e 'end-open t)) | |
1723 ;; for emacs | |
1724 (vm-set-extent-property e 'mouse-face 'highlight) | |
1725 (vm-set-extent-property e 'local-map keymap) | |
1726 ;; for xemacs | |
1727 (vm-set-extent-property e 'highlight t) | |
1728 (vm-set-extent-property e 'keymap keymap) | |
1729 (vm-set-extent-property e 'balloon-help 'vm-mouse-3-help) | |
1730 ;; for all | |
1731 (vm-set-extent-property e 'vm-mime-disposable disposable) | |
1732 (vm-set-extent-property e 'face vm-mime-button-face) | |
1733 (vm-set-extent-property e 'vm-mime-layout layout) | |
1734 (vm-set-extent-property e 'vm-mime-function action))) | |
1735 | |
1736 (defun vm-mime-send-body-to-file (layout &optional default-filename) | |
1737 (if (not (vectorp layout)) | |
1738 (setq layout (vm-extent-property layout 'vm-mime-layout))) | |
1739 (or default-filename | |
1740 (setq default-filename | |
1741 (vm-mime-get-disposition-parameter layout "filename"))) | |
1742 (and default-filename | |
1743 (setq default-filename (file-name-nondirectory default-filename))) | |
1744 (let ((work-buffer nil) | |
1745 ;; evade the XEmacs dialox box, yeccch. | |
1746 (should-use-dialog-box nil) | |
1747 file) | |
1748 (setq file | |
1749 (read-file-name | |
1750 (if default-filename | |
1751 (format "Write MIME body to file (default %s): " | |
1752 default-filename) | |
1753 "Write MIME body to file: ") | |
1754 vm-mime-attachment-save-directory default-filename) | |
1755 file (expand-file-name file vm-mime-attachment-save-directory)) | |
1756 (save-excursion | |
1757 (unwind-protect | |
1758 (progn | |
1759 (setq work-buffer (generate-new-buffer " *vm-work*")) | |
1760 (buffer-disable-undo work-buffer) | |
1761 (set-buffer work-buffer) | |
1762 ;; Tell DOS/Windows NT whether the file is binary | |
1763 (setq buffer-file-type (not (vm-mime-text-type-p layout))) | |
1764 (vm-mime-insert-mime-body layout) | |
1765 (vm-mime-transfer-decode-region layout (point-min) (point-max)) | |
1766 (or (not (file-exists-p file)) | |
1767 (y-or-n-p "File exists, overwrite? ") | |
1768 (error "Aborted")) | |
1769 (write-region (point-min) (point-max) file nil nil)) | |
1770 (and work-buffer (kill-buffer work-buffer)))))) | |
1771 | |
1772 (defun vm-mime-pipe-body-to-command (layout &optional discard-output) | |
1773 (if (not (vectorp layout)) | |
1774 (setq layout (vm-extent-property layout 'vm-mime-layout))) | |
1775 (let ((command-line (read-string "Pipe to command: ")) | |
1776 (output-buffer (if discard-output | |
1777 0 | |
1778 (get-buffer-create "*Shell Command Output*"))) | |
1779 (work-buffer nil)) | |
1780 (save-excursion | |
1781 (if (bufferp output-buffer) | |
1782 (progn | |
1783 (set-buffer output-buffer) | |
1784 (erase-buffer))) | |
1785 (unwind-protect | |
1786 (progn | |
1787 (setq work-buffer (generate-new-buffer " *vm-work*")) | |
1788 (buffer-disable-undo work-buffer) | |
1789 (set-buffer work-buffer) | |
1790 (vm-mime-insert-mime-body layout) | |
1791 (vm-mime-transfer-decode-region layout (point-min) (point-max)) | |
1792 (let ((pop-up-windows (and pop-up-windows | |
1793 (eq vm-mutable-windows t))) | |
1794 ;; Tell DOS/Windows NT whether the input is binary | |
1795 (binary-process-input (not (vm-mime-text-type-p layout)))) | |
1796 (call-process-region (point-min) (point-max) | |
1797 (or shell-file-name "sh") | |
1798 nil output-buffer nil | |
1799 shell-command-switch command-line))) | |
1800 (and work-buffer (kill-buffer work-buffer))) | |
1801 (if (bufferp output-buffer) | |
1802 (progn | |
1803 (set-buffer output-buffer) | |
1804 (if (not (zerop (buffer-size))) | |
1805 (vm-display output-buffer t (list this-command) | |
1806 '(vm-pipe-message-to-command)) | |
1807 (vm-display nil nil (list this-command) | |
1808 '(vm-pipe-message-to-command))))))) | |
1809 t ) | |
1810 | |
1811 (defun vm-mime-pipe-body-to-command-discard-output (layout) | |
1812 (vm-mime-pipe-body-to-command layout t)) | |
1813 | |
1814 (defun vm-mime-scrub-description (string) | |
1815 (let ((work-buffer nil)) | |
1816 (save-excursion | |
1817 (unwind-protect | |
1818 (progn | |
1819 (setq work-buffer (generate-new-buffer " *vm-work*")) | |
1820 (buffer-disable-undo work-buffer) | |
1821 (set-buffer work-buffer) | |
1822 (insert string) | |
1823 (while (re-search-forward "[ \t\n]+" nil t) | |
1824 (replace-match " ")) | |
1825 (buffer-string)) | |
1826 (and work-buffer (kill-buffer work-buffer)))))) | |
1827 | |
1828 (defun vm-mime-layout-description (layout) | |
1829 (if (vm-mm-layout-description layout) | |
1830 (vm-mime-scrub-description (vm-mm-layout-description layout)) | |
1831 (let ((type (car (vm-mm-layout-type layout))) | |
1832 name) | |
1833 (cond ((vm-mime-types-match "multipart/digest" type) | |
1834 (let ((n (length (vm-mm-layout-parts layout)))) | |
1835 (format "digest (%d message%s)" n (if (= n 1) "" "s")))) | |
1836 ((vm-mime-types-match "multipart/alternative" type) | |
1837 "multipart alternative") | |
1838 ((vm-mime-types-match "multipart" type) | |
1839 (let ((n (length (vm-mm-layout-parts layout)))) | |
1840 (format "multipart message (%d part%s)" n (if (= n 1) "" "s")))) | |
1841 ((vm-mime-types-match "text/plain" type) | |
1842 (format "plain text%s" | |
1843 (let ((charset (vm-mime-get-parameter layout "charset"))) | |
1844 (if charset | |
1845 (concat ", " charset) | |
1846 "")))) | |
1847 ((vm-mime-types-match "text/enriched" type) | |
1848 "enriched text") | |
1849 ((vm-mime-types-match "text/html" type) | |
1850 "HTML") | |
1851 ((vm-mime-types-match "image/gif" type) | |
1852 "GIF image") | |
1853 ((vm-mime-types-match "image/jpeg" type) | |
1854 "JPEG image") | |
1855 ((and (vm-mime-types-match "application/octet-stream" type) | |
1856 (setq name (vm-mime-get-parameter layout "name")) | |
1857 (save-match-data (not (string-match "^[ \t]*$" name)))) | |
1858 name) | |
1859 (t type))))) | |
1860 | |
1861 (defun vm-mime-layout-contains-type (layout type) | |
1862 (if (vm-mime-types-match type (car (vm-mm-layout-type layout))) | |
1863 layout | |
1864 (let ((p (vm-mm-layout-parts layout)) | |
1865 (result nil) | |
1866 (done nil)) | |
1867 (while (and p (not done)) | |
1868 (if (setq result (vm-mime-layout-contains-type (car p) type)) | |
1869 (setq done t) | |
1870 (setq p (cdr p)))) | |
1871 result ))) | |
1872 | |
1873 (defun vm-mime-plain-message-p (m) | |
1874 (save-match-data | |
1875 (let ((o (vm-mm-layout m)) | |
1876 (case-fold-search t)) | |
1877 (and (eq (vm-mm-encoded-header m) 'none) | |
1878 (or (not (vectorp o)) | |
1879 (and (vm-mime-types-match "text/plain" | |
1880 (car (vm-mm-layout-type o))) | |
1881 (string-match "^\\(us-ascii\\|iso-8859-1\\)$" | |
1882 (or (vm-mime-get-parameter o "charset") | |
1883 "us-ascii")) | |
1884 (string-match "^\\(7bit\\|8bit\\|binary\\)$" | |
1885 (vm-mm-layout-encoding o)))))))) | |
1886 | |
1887 (defun vm-mime-text-type-p (layout) | |
1888 (or (vm-mime-types-match "text" (car (vm-mm-layout-type layout))) | |
1889 (vm-mime-types-match "message" (car (vm-mm-layout-type layout))))) | |
1890 | |
1891 (defun vm-mime-charset-internally-displayable-p (name) | |
1892 (cond ((and (vm-xemacs-mule-p) (eq (device-type) 'x)) | |
1893 (cdr (assoc (downcase name) vm-mime-xemacs-mule-charset-alist))) | |
1894 ((vm-xemacs-p) | |
1895 (vm-member (downcase name) '("us-ascii" "iso-8859-1"))) | |
1896 ((vm-fsfemacs-19-p) | |
1897 (vm-member (downcase name) '("us-ascii" "iso-8859-1"))))) | |
1898 | |
1899 (defun vm-mime-find-message/partials (layout id) | |
1900 (let ((list nil) | |
1901 (type (vm-mm-layout-type layout))) | |
1902 (cond ((vm-mime-types-match "multipart" (car type)) | |
1903 (let ((parts (vm-mm-layout-parts layout)) o) | |
1904 (while parts | |
1905 (setq o (vm-mime-find-message/partials (car parts) id)) | |
1906 (if o | |
1907 (setq list (nconc o list))) | |
1908 (setq parts (cdr parts))))) | |
1909 ((vm-mime-types-match "message/partial" (car type)) | |
1910 (if (equal (vm-mime-get-parameter layout "id") id) | |
1911 (setq list (cons layout list))))) | |
1912 list )) | |
1913 | |
1914 (defun vm-message-at-point () | |
1915 (let ((mp vm-message-list) | |
1916 (point (point)) | |
1917 (done nil)) | |
1918 (while (and mp (not done)) | |
1919 (if (and (>= point (vm-start-of (car mp))) | |
1920 (<= point (vm-end-of (car mp)))) | |
1921 (setq done t) | |
1922 (setq mp (cdr mp)))) | |
1923 (car mp))) | |
1924 | |
1925 (defun vm-mime-make-multipart-boundary () | |
1926 (let ((boundary (make-string 40 ?a)) | |
1927 (i 0)) | |
1928 (random t) | |
1929 (while (< i (length boundary)) | |
1930 (aset boundary i (aref vm-mime-base64-alphabet | |
1931 (% (vm-abs (lsh (random) -8)) | |
1932 (length vm-mime-base64-alphabet)))) | |
1933 (vm-increment i)) | |
1934 boundary )) | |
1935 | |
1936 (defun vm-mime-attach-file (file type &optional charset) | |
1937 "Attach a file to a VM composition buffer to be sent along with the message. | |
1938 The file is not inserted into the buffer and MIME encoded until | |
1939 you execute vm-mail-send or vm-mail-send-and-exit. A visible tag | |
1940 indicating the existence of the attachment is placed in the | |
1941 composition buffer. You can move the attachment around or remove | |
1942 it entirely with normal text editing commands. If you remove the | |
1943 attachment tag, the attachment will not be sent. | |
1944 | |
1945 First argument, FILE, is the name of the file to attach. Second | |
1946 argument, TYPE, is the MIME Content-Type of the file. Optional | |
1947 third argument CHARSET is the character set of the attached | |
1948 document. This argument is only used for text types, and it | |
1949 is ignored for other types. | |
1950 | |
1951 When called interactively all arguments are read from the | |
1952 minibuffer. | |
1953 | |
1954 This command is for attaching files that do not have a MIME | |
1955 header section at the top. For files with MIME headers, you | |
1956 should use vm-mime-attach-mime-file to attach such a file. VM | |
1957 will extract the content type information from the headers in | |
1958 this case and not prompt you for it in the minibuffer." | |
1959 (interactive | |
1960 ;; protect value of last-command and this-command | |
1961 (let ((last-command last-command) | |
1962 (this-command this-command) | |
1963 (charset nil) | |
1964 file default-type type) | |
1965 (if (null vm-send-using-mime) | |
1966 (error "MIME attachments disabled, set vm-send-using-mime non-nil to enable.")) | |
1967 (setq file (vm-read-file-name "Attach file: " nil nil t) | |
1968 default-type (or (vm-mime-default-type-from-filename file) | |
1969 "application/octet-stream") | |
1970 type (completing-read | |
1971 (format "Content type (default %s): " | |
1972 default-type) | |
1973 vm-mime-type-completion-alist) | |
1974 type (if (> (length type) 0) type default-type)) | |
1975 (if (vm-mime-types-match "text" type) | |
1976 (setq charset (completing-read "Character set (default US-ASCII): " | |
1977 vm-mime-charset-completion-alist) | |
1978 charset (if (> (length charset) 0) charset))) | |
1979 (list file type charset))) | |
1980 (if (null vm-send-using-mime) | |
1981 (error "MIME attachments disabled, set vm-send-using-mime non-nil to enable.")) | |
1982 (if (file-directory-p file) | |
1983 (error "%s is a directory, cannot attach" file)) | |
1984 (if (not (file-exists-p file)) | |
1985 (error "No such file: %s" file)) | |
1986 (if (not (file-readable-p file)) | |
1987 (error "You don't have permission to read %s" file)) | |
1988 (and charset (setq charset (list (concat "charset=" charset)))) | |
1989 (vm-mime-attach-object file type charset nil)) | |
1990 | |
1991 (defun vm-mime-attach-mime-file (file) | |
1992 "Attach a MIME encoded file to a VM composition buffer to be sent | |
1993 along with the message. | |
1994 | |
1995 The file is not inserted into the buffer until you execute | |
1996 vm-mail-send or vm-mail-send-and-exit. A visible tag indicating | |
1997 the existence of the attachment is placed in the composition | |
1998 buffer. You can move the attachment around or remove it entirely | |
1999 with normal text editing commands. If you remove the attachment | |
2000 tag, the attachment will not be sent. | |
2001 | |
2002 The sole argument, FILE, is the name of the file to attach. | |
2003 When called interactively the FILE argument is read from the | |
2004 minibuffer. | |
2005 | |
2006 This command is for attaching files that have a MIME | |
2007 header section at the top. For files without MIME headers, you | |
2008 should use vm-mime-attach-file to attach such a file. VM | |
2009 will interactively query you for the file type information." | |
2010 (interactive | |
2011 ;; protect value of last-command and this-command | |
2012 (let ((last-command last-command) | |
2013 (this-command this-command) | |
2014 file) | |
2015 (if (null vm-send-using-mime) | |
2016 (error "MIME attachments disabled, set vm-send-using-mime non-nil to enable.")) | |
2017 (setq file (vm-read-file-name "Attach file: " nil nil t)) | |
2018 (list file))) | |
2019 (if (null vm-send-using-mime) | |
2020 (error "MIME attachments disabled, set vm-send-using-mime non-nil to enable.")) | |
2021 (if (file-directory-p file) | |
2022 (error "%s is a directory, cannot attach" file)) | |
2023 (if (not (file-exists-p file)) | |
2024 (error "No such file: %s" file)) | |
2025 (if (not (file-readable-p file)) | |
2026 (error "You don't have permission to read %s" file)) | |
2027 (vm-mime-attach-object file "MIME file" nil t)) | |
2028 | |
2029 (defun vm-mime-attach-object (object type params mimed) | |
2030 (if (not (eq major-mode 'mail-mode)) | |
2031 (error "Command must be used in a VM Mail mode buffer.")) | |
2032 (let ((start (point)) | |
2033 e tag-string) | |
2034 (setq tag-string (format "[ATTACHMENT %s, %s]" object type)) | |
2035 (insert tag-string "\n") | |
2036 (cond ((fboundp 'make-overlay) | |
2037 (setq e (make-overlay start (point) nil t nil)) | |
2038 (overlay-put e 'face vm-mime-button-face)) | |
2039 ((fboundp 'make-extent) | |
2040 (setq e (make-extent start (1- (point)))) | |
2041 (set-extent-property e 'start-open t) | |
2042 (set-extent-property e 'face vm-mime-button-face))) | |
2043 (vm-set-extent-property e 'duplicable t) | |
2044 ;; crashes XEmacs | |
2045 ;; (vm-set-extent-property e 'replicating t) | |
2046 (vm-set-extent-property e 'vm-mime-type type) | |
2047 (vm-set-extent-property e 'vm-mime-object object) | |
2048 (vm-set-extent-property e 'vm-mime-params params) | |
2049 (vm-set-extent-property e 'vm-mime-encoded mimed))) | |
2050 | |
2051 (defun vm-mime-default-type-from-filename (file) | |
2052 (let ((alist vm-mime-attachment-auto-type-alist) | |
2053 (case-fold-search t) | |
2054 (done nil)) | |
2055 (while (and alist (not done)) | |
2056 (if (string-match (car (car alist)) file) | |
2057 (setq done t) | |
2058 (setq alist (cdr alist)))) | |
2059 (and alist (cdr (car alist))))) | |
2060 | |
2061 (defun vm-remove-mail-mode-header-separator () | |
2062 (save-excursion | |
2063 (goto-char (point-min)) | |
2064 (if (re-search-forward (concat "^" mail-header-separator "$") nil t) | |
2065 (progn | |
2066 (delete-region (match-beginning 0) (match-end 0)) | |
2067 t ) | |
2068 nil ))) | |
2069 | |
2070 (defun vm-add-mail-mode-header-separator () | |
2071 (save-excursion | |
2072 (goto-char (point-min)) | |
2073 (if (re-search-forward "^$" nil t) | |
2074 (replace-match mail-header-separator t t)))) | |
2075 | |
2076 (defun vm-mime-transfer-encode-region (encoding beg end crlf) | |
2077 (let ((case-fold-search t)) | |
2078 (cond ((string-match "^binary$" encoding) | |
2079 (vm-mime-base64-encode-region beg end crlf) | |
2080 (setq encoding "base64")) | |
2081 ((string-match "^7bit$" encoding) t) | |
2082 ((string-match "^base64$" encoding) t) | |
2083 ((string-match "^quoted-printable$" encoding) t) | |
2084 ;; must be 8bit | |
2085 ((eq vm-mime-8bit-text-transfer-encoding 'quoted-printable) | |
2086 (vm-mime-qp-encode-region beg end) | |
2087 (setq encoding "quoted-printable")) | |
2088 ((eq vm-mime-8bit-text-transfer-encoding 'base64) | |
2089 (vm-mime-base64-encode-region beg end crlf) | |
2090 (setq encoding "base64")) | |
2091 ((eq vm-mime-8bit-text-transfer-encoding 'send) t)) | |
2092 encoding )) | |
2093 | |
2094 (defun vm-mime-transfer-encode-layout (layout) | |
2095 (if (vm-mime-text-type-p layout) | |
2096 (vm-mime-transfer-encode-region (vm-mm-layout-encoding layout) | |
2097 (vm-mm-layout-body-start layout) | |
2098 (vm-mm-layout-body-end layout) | |
2099 t) | |
2100 (vm-mime-transfer-encode-region (vm-mm-layout-encoding layout) | |
2101 (vm-mm-layout-body-start layout) | |
2102 (vm-mm-layout-body-end layout) | |
2103 nil))) | |
2104 (defun vm-mime-encode-composition () | |
2105 "MIME encode the current buffer. | |
2106 Attachment tags added to the buffer with vm-mime-attach-file are expanded | |
2107 and the approriate content-type and boundary markup information is added." | |
2108 (interactive) | |
2109 (save-restriction | |
2110 (widen) | |
2111 (if (not (eq major-mode 'mail-mode)) | |
2112 (error "Command must be used in a VM Mail mode buffer.")) | |
2113 (or (null (vm-mail-mode-get-header-contents "MIME-Version:")) | |
2114 (error "Message is already MIME encoded.")) | |
2115 (let ((8bit nil) | |
2116 (just-one nil) | |
2117 (boundary-positions nil) | |
2118 already-mimed layout e e-list boundary | |
2119 type encoding charset params object opoint-min) | |
2120 (mail-text) | |
2121 (setq e-list (if (fboundp 'extent-list) | |
2122 (extent-list nil (point) (point-max)) | |
2123 (overlays-in (point) (point-max))) | |
2124 e-list (vm-delete (function | |
2125 (lambda (e) | |
2126 (vm-extent-property e 'vm-mime-object))) | |
2127 e-list t) | |
2128 e-list (sort e-list (function | |
2129 (lambda (e1 e2) | |
2130 (< (vm-extent-end-position e1) | |
2131 (vm-extent-end-position e2)))))) | |
2132 ;; If there's just one attachment and no other readable | |
2133 ;; text in the buffer then make the message type just be | |
2134 ;; the attachment type rather than sending a multipart | |
2135 ;; message with one attachment | |
2136 (setq just-one (and (= (length e-list) 1) | |
2137 (looking-at "[ \t\n]*") | |
2138 (= (match-end 0) | |
2139 (vm-extent-start-position (car e-list))) | |
2140 (save-excursion | |
2141 (goto-char (vm-extent-end-position (car e-list))) | |
2142 (looking-at "[ \t\n]*\\'")))) | |
2143 (if (null e-list) | |
2144 (progn | |
2145 (narrow-to-region (point) (point-max)) | |
2146 (setq charset (vm-determine-proper-charset (point-min) | |
2147 (point-max))) | |
2148 (setq encoding (vm-determine-proper-content-transfer-encoding | |
2149 (point-min) | |
2150 (point-max)) | |
2151 encoding (vm-mime-transfer-encode-region encoding | |
2152 (point-min) | |
2153 (point-max) | |
2154 t)) | |
2155 (widen) | |
2156 (vm-remove-mail-mode-header-separator) | |
2157 (goto-char (point-min)) | |
2158 (vm-reorder-message-headers | |
2159 nil nil "\\(Content-Type:\\|Content-Transfer-Encoding\\|MIME-Version:\\)") | |
2160 (insert "MIME-Version: 1.0\n") | |
2161 (insert "Content-Type: text/plain; charset=" charset "\n") | |
2162 (insert "Content-Transfer-Encoding: " encoding "\n") | |
2163 (vm-add-mail-mode-header-separator)) | |
2164 (while e-list | |
2165 (setq e (car e-list)) | |
2166 (if (or just-one (= (point) (vm-extent-start-position e))) | |
2167 nil | |
2168 (narrow-to-region (point) (vm-extent-start-position e)) | |
2169 (setq charset (vm-determine-proper-charset (point-min) | |
2170 (point-max))) | |
2171 (setq encoding (vm-determine-proper-content-transfer-encoding | |
2172 (point-min) | |
2173 (point-max)) | |
2174 encoding (vm-mime-transfer-encode-region encoding | |
2175 (point-min) | |
2176 (point-max) | |
2177 t)) | |
2178 (setq boundary-positions (cons (point-marker) boundary-positions)) | |
2179 (insert "Content-Type: text/plain; charset=" charset "\n") | |
2180 (insert "Content-Transfer-Encoding: " encoding "\n\n") | |
2181 (widen)) | |
2182 (goto-char (vm-extent-end-position e)) | |
2183 (narrow-to-region (point) (point)) | |
2184 (setq object (vm-extent-property e 'vm-mime-object)) | |
2185 (cond ((bufferp object) | |
2186 (insert-buffer-substring object)) | |
2187 ((stringp object) | |
2188 (insert-file-contents-literally object))) | |
2189 (if (setq already-mimed (vm-extent-property e 'vm-mime-encoded)) | |
2190 (setq layout (vm-mime-parse-entity | |
2191 nil (list "text/plain" "charset=us-ascii") | |
2192 "7bit") | |
2193 type (car (vm-mm-layout-type layout)) | |
2194 params (cdr (vm-mm-layout-type layout))) | |
2195 (setq type (vm-extent-property e 'vm-mime-type) | |
2196 params (vm-extent-property e 'vm-mime-parameters))) | |
2197 (cond ((vm-mime-types-match "text" type) | |
2198 (setq encoding | |
2199 (vm-determine-proper-content-transfer-encoding | |
2200 (if already-mimed | |
2201 (vm-mm-layout-body-start layout) | |
2202 (point-min)) | |
2203 (point-max)) | |
2204 encoding (vm-mime-transfer-encode-region | |
2205 encoding | |
2206 (if already-mimed | |
2207 (vm-mm-layout-body-start layout) | |
2208 (point-min)) | |
2209 (point-max) | |
2210 t)) | |
2211 (setq 8bit (or 8bit (equal encoding "8bit")))) | |
2212 ((or (vm-mime-types-match "message/rfc822" type) | |
2213 (vm-mime-types-match "multipart" type)) | |
2214 (setq opoint-min (point-min)) | |
2215 (if (not already-mimed) | |
2216 (setq layout (vm-mime-parse-entity | |
2217 nil (list "text/plain" "charset=us-ascii") | |
2218 "7bit"))) | |
2219 ;; MIME messages of type "message" and | |
2220 ;; "multipart" are required to have a non-opaque | |
2221 ;; content transfer encoding. This means that | |
2222 ;; if the user only wants to send out 7bit data, | |
2223 ;; then any subpart that contains 8bit data must | |
2224 ;; have an opaque (qp or base64) 8->7bit | |
2225 ;; conversion performed on it so that the | |
2226 ;; enclosing entity can use an non-opqaue | |
2227 ;; encoding. | |
2228 ;; | |
2229 ;; message/partial requires a "7bit" encoding so | |
2230 ;; force 8->7 conversion in that case. | |
2231 (let ((vm-mime-8bit-text-transfer-encoding | |
2232 (if (vm-mime-types-match "message/partial" type) | |
2233 'quoted-printable | |
2234 vm-mime-8bit-text-transfer-encoding))) | |
2235 (vm-mime-map-atomic-layouts 'vm-mime-transfer-encode-layout | |
2236 (vm-mm-layout-parts layout))) | |
2237 ;; now figure out a proper content trasnfer | |
2238 ;; encoding value for the enclosing entity. | |
2239 (re-search-forward "^\n" nil t) | |
2240 (save-restriction | |
2241 (narrow-to-region (point) (point-max)) | |
2242 (setq encoding | |
2243 (vm-determine-proper-content-transfer-encoding | |
2244 (point-min) | |
2245 (point-max)))) | |
2246 (setq 8bit (or 8bit (equal encoding "8bit"))) | |
2247 (goto-char (point-max)) | |
2248 (widen) | |
2249 (narrow-to-region opoint-min (point))) | |
2250 (t | |
2251 (vm-mime-base64-encode-region | |
2252 (if already-mimed | |
2253 (vm-mm-layout-body-start layout) | |
2254 (point-min)) | |
2255 (point-max)) | |
2256 (setq encoding "base64"))) | |
2257 (if just-one | |
2258 nil | |
2259 (goto-char (point-min)) | |
2260 (setq boundary-positions (cons (point-marker) boundary-positions)) | |
2261 (if (not already-mimed) | |
2262 nil | |
2263 ;; trim headers | |
2264 (vm-reorder-message-headers | |
2265 nil '("Content-Description:" "Content-ID:") nil) | |
2266 ;; remove header/text separator | |
2267 (goto-char (1- (vm-mm-layout-body-start layout))) | |
2268 (if (looking-at "\n") | |
2269 (delete-char 1))) | |
2270 (insert "Content-Type: " type) | |
2271 (if params | |
2272 (if vm-mime-avoid-folding-content-type | |
2273 (insert "; " (mapconcat 'identity params "; ") "\n") | |
2274 (insert ";\n\t" (mapconcat 'identity params ";\n\t") "\n")) | |
2275 (insert "\n")) | |
2276 (insert "Content-Transfer-Encoding: " encoding "\n\n")) | |
2277 (goto-char (point-max)) | |
2278 (widen) | |
2279 (delete-region (vm-extent-start-position e) | |
2280 (vm-extent-end-position e)) | |
2281 (vm-detach-extent e) | |
2282 (setq e-list (cdr e-list))) | |
2283 ;; handle the remaining chunk of text after the last | |
2284 ;; extent, if any. | |
2285 (if (or just-one (= (point) (point-max))) | |
2286 nil | |
2287 (setq charset (vm-determine-proper-charset (point) | |
2288 (point-max))) | |
2289 (setq encoding (vm-determine-proper-content-transfer-encoding | |
2290 (point) | |
2291 (point-max)) | |
2292 encoding (vm-mime-transfer-encode-region encoding | |
2293 (point) | |
2294 (point-max) | |
2295 t)) | |
2296 (setq 8bit (or 8bit (equal encoding "8bit"))) | |
2297 (setq boundary-positions (cons (point-marker) boundary-positions)) | |
2298 (insert "Content-Type: text/plain; charset=" charset "\n") | |
2299 (insert "Content-Transfer-Encoding: " encoding "\n\n") | |
2300 (goto-char (point-max))) | |
2301 (setq boundary (vm-mime-make-multipart-boundary)) | |
2302 (mail-text) | |
2303 (while (re-search-forward (concat "^--" | |
2304 (regexp-quote boundary) | |
2305 "\\(--\\)?$") | |
2306 nil t) | |
2307 (setq boundary (vm-mime-make-multipart-boundary)) | |
2308 (mail-text)) | |
2309 (goto-char (point-max)) | |
2310 (or just-one (insert "\n--" boundary "--\n")) | |
2311 (while boundary-positions | |
2312 (goto-char (car boundary-positions)) | |
2313 (insert "\n--" boundary "\n") | |
2314 (setq boundary-positions (cdr boundary-positions))) | |
2315 (if (and just-one already-mimed) | |
2316 (progn | |
2317 (goto-char (vm-mm-layout-header-start layout)) | |
2318 ;; trim headers | |
2319 (vm-reorder-message-headers | |
2320 nil '("Content-Description:" "Content-ID:") nil) | |
2321 ;; remove header/text separator | |
2322 (goto-char (1- (vm-mm-layout-body-start layout))) | |
2323 (if (looking-at "\n") | |
2324 (delete-char 1)) | |
2325 ;; copy remainder to enclosing entity's header section | |
2326 (insert-buffer-substring (current-buffer) | |
2327 (vm-mm-layout-header-start layout) | |
2328 (vm-mm-layout-body-start layout)) | |
2329 (delete-region (vm-mm-layout-header-start layout) | |
2330 (vm-mm-layout-body-start layout)))) | |
2331 (goto-char (point-min)) | |
2332 (vm-remove-mail-mode-header-separator) | |
2333 (vm-reorder-message-headers | |
2334 nil nil "\\(Content-Type:\\|MIME-Version:\\|Content-Transfer-Encoding\\)") | |
2335 (vm-add-mail-mode-header-separator) | |
2336 (insert "MIME-Version: 1.0\n") | |
2337 (if (not just-one) | |
2338 (insert (if vm-mime-avoid-folding-content-type | |
2339 "Content-Type: multipart/mixed; boundary=\"" | |
2340 "Content-Type: multipart/mixed;\n\tboundary=\"") | |
2341 boundary "\"\n") | |
2342 (insert "Content-Type: " type) | |
2343 (if params | |
2344 (if vm-mime-avoid-folding-content-type | |
2345 (insert "; " (mapconcat 'identity params "; ") "\n") | |
2346 (insert ";\n\t" (mapconcat 'identity params ";\n\t")))) | |
2347 (insert "\n")) | |
2348 (if just-one | |
2349 (insert "Content-Transfer-Encoding: " encoding "\n") | |
2350 (if 8bit | |
2351 (insert "Content-Transfer-Encoding: 8bit\n") | |
2352 (insert "Content-Transfer-Encoding: 7bit\n"))))))) | |
2353 | |
2354 (defun vm-mime-fragment-composition (size) | |
2355 (save-restriction | |
2356 (widen) | |
2357 (vm-unsaved-message "Fragmenting message...") | |
2358 (let ((buffers nil) | |
2359 (id (vm-mime-make-multipart-boundary)) | |
2360 (n 1) | |
2361 (the-end nil) | |
2362 b header-start header-end master-buffer start end) | |
2363 (vm-remove-mail-mode-header-separator) | |
2364 ;; message/partial must have "7bit" content transfer | |
2365 ;; encoding, so verify that everything has been encoded for | |
2366 ;; 7bit transmission. | |
2367 (let ((vm-mime-8bit-text-transfer-encoding | |
2368 (if (eq vm-mime-8bit-text-transfer-encoding 'send) | |
2369 'quoted-printable | |
2370 vm-mime-8bit-text-transfer-encoding))) | |
2371 (vm-mime-map-atomic-layouts | |
2372 'vm-mime-transfer-encode-layout | |
2373 (list (vm-mime-parse-entity nil (list "text/plain" "charset=us-ascii") | |
2374 "7bit")))) | |
2375 (goto-char (point-min)) | |
2376 (setq header-start (point)) | |
2377 (search-forward "\n\n") | |
2378 (setq header-end (1- (point))) | |
2379 (setq master-buffer (current-buffer)) | |
2380 (goto-char (point-min)) | |
2381 (setq start (point)) | |
2382 (while (not (eobp)) | |
2383 (condition-case nil | |
2384 (progn | |
2385 (forward-char (max (- size 150) 2000)) | |
2386 (beginning-of-line)) | |
2387 (end-of-buffer (setq the-end t))) | |
2388 (setq end (point)) | |
2389 (setq b (generate-new-buffer (concat (buffer-name) " part " | |
2390 (int-to-string n)))) | |
2391 (setq buffers (cons b buffers)) | |
2392 (set-buffer b) | |
2393 (make-local-variable 'vm-send-using-mime) | |
2394 (setq vm-send-using-mime nil) | |
2395 (insert-buffer-substring master-buffer header-start header-end) | |
2396 (goto-char (point-min)) | |
2397 (vm-reorder-message-headers nil nil | |
2398 "\\(Content-Type:\\|MIME-Version:\\|Content-Transfer-Encoding\\)") | |
2399 (insert "MIME-Version: 1.0\n") | |
2400 (insert (format | |
2401 (if vm-mime-avoid-folding-content-type | |
2402 "Content-Type: message/partial; id=%s; number=%d" | |
2403 "Content-Type: message/partial;\n\tid=%s;\n\tnumber=%d") | |
2404 id n)) | |
2405 (if the-end | |
2406 (if vm-mime-avoid-folding-content-type | |
2407 (insert (format "; total=%d\n" n)) | |
2408 (insert (format ";\n\ttotal=%d\n" n))) | |
2409 (insert "\n")) | |
2410 (insert "Content-Transfer-Encoding: 7bit\n") | |
2411 (goto-char (point-max)) | |
2412 (insert mail-header-separator "\n") | |
2413 (insert-buffer-substring master-buffer start end) | |
2414 (vm-increment n) | |
2415 (set-buffer master-buffer) | |
2416 (setq start (point))) | |
2417 (vm-unsaved-message "Fragmenting message... done") | |
2418 (nreverse buffers)))) | |
2419 | |
2420 (defun vm-mime-preview-composition () | |
2421 "Show how the current composition buffer might be displayed | |
2422 in a MIME-aware mail reader. VM copies and encodes the current | |
2423 mail composition buffer and displays it as a mail folder. | |
2424 Type `q' to quit this temp folder and return to composing your | |
2425 message." | |
2426 (interactive) | |
2427 (if (not (eq major-mode 'mail-mode)) | |
2428 (error "Command must be used in a VM Mail mode buffer.")) | |
2429 (let ((temp-buffer nil) | |
2430 (mail-buffer (current-buffer)) | |
2431 e-list) | |
2432 (unwind-protect | |
2433 (progn | |
2434 (mail-text) | |
2435 (setq e-list (if (fboundp 'extent-list) | |
2436 (extent-list nil (point) (point-max)) | |
2437 (overlays-in (point) (point-max))) | |
2438 e-list (vm-delete (function | |
2439 (lambda (e) | |
2440 (vm-extent-property e 'vm-mime-object))) | |
2441 e-list t) | |
2442 e-list (sort e-list (function | |
2443 (lambda (e1 e2) | |
2444 (< (vm-extent-end-position e1) | |
2445 (vm-extent-end-position e2)))))) | |
2446 (setq temp-buffer (generate-new-buffer "composition preview")) | |
2447 (set-buffer temp-buffer) | |
2448 ;; so vm-mime-encode-composition won't complain | |
2449 (setq major-mode 'mail-mode) | |
2450 (vm-insert-region-from-buffer mail-buffer) | |
2451 (mapcar 'vm-copy-extent e-list) | |
2452 (goto-char (point-min)) | |
2453 (or (vm-mail-mode-get-header-contents "From") | |
2454 (insert "From: " (or user-mail-address (user-login-name)) "\n")) | |
2455 (or (vm-mail-mode-get-header-contents "Message-ID") | |
2456 (insert "Message-ID: <fake@fake.com>\n")) | |
2457 (or (vm-mail-mode-get-header-contents "Date") | |
2458 (insert "Date: " | |
2459 (format-time-string "%a, %d %b %Y %H%M%S %Z" | |
2460 (current-time)) | |
2461 "\n")) | |
2462 (and vm-send-using-mime | |
2463 (null (vm-mail-mode-get-header-contents "MIME-Version:")) | |
2464 (vm-mime-encode-composition)) | |
2465 (goto-char (point-min)) | |
2466 (insert (vm-leading-message-separator 'From_)) | |
2467 (goto-char (point-max)) | |
2468 (insert (vm-trailing-message-separator 'From_)) | |
2469 (set-buffer-modified-p nil) | |
2470 ;; point of no return, don't kill it if the user quits | |
2471 (setq temp-buffer nil) | |
2472 (let ((vm-auto-decode-mime-messages t) | |
2473 (vm-auto-displayed-mime-content-types t)) | |
2474 (vm-save-buffer-excursion | |
2475 (vm-goto-new-folder-frame-maybe 'folder) | |
2476 (vm-mode))) | |
2477 (message | |
2478 (substitute-command-keys | |
2479 "Type \\[vm-quit] to continue composing your message")) | |
2480 ;; temp buffer, don't offer to save it. | |
2481 (setq buffer-offer-save nil) | |
2482 (vm-display (or vm-presentation-buffer (current-buffer)) t | |
2483 (list this-command) '(vm-mode startup))) | |
2484 (and temp-buffer (kill-buffer temp-buffer))))) | |
2485 | |
2486 (defun vm-mime-composite-type-p (type) | |
2487 (or (vm-mime-types-match "message" type) | |
2488 (vm-mime-types-match "multipart" type))) | |
2489 | |
2490 (defun vm-mime-map-atomic-layouts (function list) | |
2491 (while list | |
2492 (if (vm-mime-composite-type-p (car (vm-mm-layout-type (car list)))) | |
2493 (vm-mime-map-atomic-layouts function (vm-mm-layout-parts (car list))) | |
2494 (funcall function (car list))) | |
2495 (setq list (cdr list)))) |