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))))