20
|
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))
|
24
|
30 (defun vm-mm-layout-qtype (e) (aref e 1))
|
|
31 (defun vm-mm-layout-encoding (e) (aref e 2))
|
|
32 (defun vm-mm-layout-id (e) (aref e 3))
|
|
33 (defun vm-mm-layout-description (e) (aref e 4))
|
|
34 (defun vm-mm-layout-disposition (e) (aref e 5))
|
|
35 (defun vm-mm-layout-qdisposition (e) (aref e 6))
|
|
36 (defun vm-mm-layout-header-start (e) (aref e 7))
|
|
37 (defun vm-mm-layout-body-start (e) (aref e 8))
|
|
38 (defun vm-mm-layout-body-end (e) (aref e 9))
|
|
39 (defun vm-mm-layout-parts (e) (aref e 10))
|
131
|
40 ;; if display of MIME part fails, error string will be here.
|
24
|
41 (defun vm-mm-layout-cache (e) (aref e 11))
|
20
|
42
|
30
|
43 (defun vm-set-mm-layout-type (e type) (aset e 0 type))
|
24
|
44 (defun vm-set-mm-layout-cache (e c) (aset e 11 c))
|
20
|
45
|
|
46 (defun vm-mm-layout (m)
|
|
47 (or (vm-mime-layout-of m)
|
|
48 (progn (vm-set-mime-layout-of
|
|
49 m
|
|
50 (condition-case data
|
|
51 (vm-mime-parse-entity m)
|
114
|
52 (vm-mime-error (message "%s" (car (cdr data))))))
|
20
|
53 (vm-mime-layout-of m))))
|
|
54
|
|
55 (defun vm-mm-encoded-header (m)
|
|
56 (or (vm-mime-encoded-header-flag-of m)
|
|
57 (progn (setq m (vm-real-message-of m))
|
|
58 (vm-set-mime-encoded-header-flag-of
|
|
59 m
|
|
60 (save-excursion
|
|
61 (set-buffer (vm-buffer-of m))
|
|
62 (save-excursion
|
|
63 (save-restriction
|
|
64 (widen)
|
|
65 (goto-char (vm-headers-of m))
|
|
66 (or (re-search-forward vm-mime-encoded-word-regexp
|
|
67 (vm-text-of m) t)
|
|
68 'none)))))
|
|
69 (vm-mime-encoded-header-flag-of m))))
|
|
70
|
|
71 (defun vm-mime-Q-decode-region (start end)
|
|
72 (let ((buffer-read-only nil))
|
|
73 (subst-char-in-region start end ?_ (string-to-char " ") t)
|
|
74 (vm-mime-qp-decode-region start end)))
|
|
75
|
|
76 (fset 'vm-mime-B-decode-region 'vm-mime-base64-decode-region)
|
|
77
|
|
78 (defun vm-mime-Q-encode-region (start end)
|
|
79 (let ((buffer-read-only nil))
|
|
80 (subst-char-in-region start end (string-to-char " ") ?_ t)
|
24
|
81 (vm-mime-qp-encode-region start end t)))
|
20
|
82
|
24
|
83 (defun vm-mime-B-encode-region (start end)
|
|
84 (vm-mime-base64-encode-region start end nil t))
|
20
|
85
|
|
86 (defun vm-mime-crlf-to-lf-region (start end)
|
|
87 (let ((buffer-read-only nil))
|
|
88 (save-excursion
|
|
89 (save-restriction
|
|
90 (narrow-to-region start end)
|
|
91 (goto-char start)
|
|
92 (while (search-forward "\r\n" nil t)
|
|
93 (delete-char -2)
|
|
94 (insert "\n"))))))
|
|
95
|
|
96 (defun vm-mime-lf-to-crlf-region (start end)
|
|
97 (let ((buffer-read-only nil))
|
|
98 (save-excursion
|
|
99 (save-restriction
|
|
100 (narrow-to-region start end)
|
|
101 (goto-char start)
|
|
102 (while (search-forward "\n" nil t)
|
|
103 (delete-char -1)
|
|
104 (insert "\r\n"))))))
|
|
105
|
|
106 (defun vm-mime-charset-decode-region (charset start end)
|
24
|
107 (or (markerp end) (setq end (vm-marker end)))
|
120
|
108 (cond (vm-xemacs-mule-p
|
24
|
109 (if (eq (device-type) 'x)
|
|
110 (let ((buffer-read-only nil)
|
|
111 (cell (cdr (vm-string-assoc
|
|
112 charset
|
|
113 vm-mime-mule-charset-to-coding-alist)))
|
|
114 (oend (marker-position end))
|
|
115 (opoint (point)))
|
|
116 (if cell
|
|
117 (progn
|
|
118 (set-marker end (+ start
|
|
119 (or (decode-coding-region
|
|
120 start end (car cell))
|
|
121 (- oend start))))
|
|
122 (put-text-property start end 'vm-string t)
|
|
123 (put-text-property start end 'vm-charset charset)
|
|
124 (put-text-property start end 'vm-coding (car cell))))
|
|
125 ;; In XEmacs 20.0 beta93 decode-coding-region moves point.
|
|
126 (goto-char opoint))))
|
|
127 ((not (vm-multiple-fonts-possible-p)) nil)
|
|
128 ((vm-string-member charset vm-mime-default-face-charsets) nil)
|
|
129 (t
|
|
130 (let ((font (cdr (vm-string-assoc
|
|
131 charset
|
|
132 vm-mime-charset-font-alist)))
|
|
133 (face (make-face (make-symbol "temp-face")))
|
|
134 (e (vm-make-extent start end)))
|
|
135 (put-text-property start end 'vm-string t)
|
|
136 (put-text-property start end 'vm-charset charset)
|
|
137 (if font
|
|
138 (condition-case data
|
|
139 (progn (set-face-font face font)
|
|
140 (vm-set-extent-property e 'face face))
|
|
141 (error nil)))))))
|
20
|
142
|
|
143 (defun vm-mime-transfer-decode-region (layout start end)
|
|
144 (let ((case-fold-search t) (crlf nil))
|
|
145 (cond ((string-match "^base64$" (vm-mm-layout-encoding layout))
|
|
146 (cond ((vm-mime-types-match "text"
|
|
147 (car (vm-mm-layout-type layout)))
|
|
148 (setq crlf t))
|
|
149 ((vm-mime-types-match "message"
|
|
150 (car (vm-mm-layout-type layout)))
|
|
151 (setq crlf t)))
|
|
152 (vm-mime-base64-decode-region start end crlf))
|
|
153 ((string-match "^quoted-printable$"
|
|
154 (vm-mm-layout-encoding layout))
|
|
155 (vm-mime-qp-decode-region start end)))))
|
|
156
|
|
157 (defun vm-mime-base64-decode-region (start end &optional crlf)
|
26
|
158 (message "Decoding base64...")
|
20
|
159 (let ((work-buffer nil)
|
|
160 (done nil)
|
|
161 (counter 0)
|
|
162 (bits 0)
|
|
163 (lim 0) inputpos
|
|
164 (non-data-chars (concat "^=" vm-mime-base64-alphabet)))
|
|
165 (unwind-protect
|
|
166 (save-excursion
|
|
167 (setq work-buffer (generate-new-buffer " *vm-work*"))
|
|
168 (buffer-disable-undo work-buffer)
|
|
169 (if vm-mime-base64-decoder-program
|
|
170 (let* ((binary-process-output t) ; any text already has CRLFs
|
|
171 (status (apply 'vm-run-command-on-region
|
|
172 start end work-buffer
|
|
173 vm-mime-base64-decoder-program
|
|
174 vm-mime-base64-decoder-switches)))
|
|
175 (if (not (eq status t))
|
|
176 (vm-mime-error "%s" (cdr status))))
|
|
177 (goto-char start)
|
|
178 (skip-chars-forward non-data-chars end)
|
|
179 (while (not done)
|
|
180 (setq inputpos (point))
|
|
181 (cond
|
|
182 ((> (skip-chars-forward vm-mime-base64-alphabet end) 0)
|
|
183 (setq lim (point))
|
|
184 (while (< inputpos lim)
|
|
185 (setq bits (+ bits
|
|
186 (aref vm-mime-base64-alphabet-decoding-vector
|
|
187 (char-after inputpos))))
|
|
188 (vm-increment counter)
|
|
189 (vm-increment inputpos)
|
|
190 (cond ((= counter 4)
|
|
191 (vm-insert-char (lsh bits -16) 1 nil work-buffer)
|
|
192 (vm-insert-char (logand (lsh bits -8) 255) 1 nil
|
|
193 work-buffer)
|
|
194 (vm-insert-char (logand bits 255) 1 nil work-buffer)
|
|
195 (setq bits 0 counter 0))
|
|
196 (t (setq bits (lsh bits 6)))))))
|
|
197 (cond
|
|
198 ((= (point) end)
|
|
199 (if (not (zerop counter))
|
|
200 (vm-mime-error "at least %d bits missing at end of base64 encoding"
|
|
201 (* (- 4 counter) 6)))
|
|
202 (setq done t))
|
|
203 ((= (char-after (point)) 61) ; 61 is ASCII equals
|
|
204 (setq done t)
|
|
205 (cond ((= counter 1)
|
|
206 (vm-mime-error "at least 2 bits missing at end of base64 encoding"))
|
|
207 ((= counter 2)
|
|
208 (vm-insert-char (lsh bits -10) 1 nil work-buffer))
|
|
209 ((= counter 3)
|
|
210 (vm-insert-char (lsh bits -16) 1 nil work-buffer)
|
|
211 (vm-insert-char (logand (lsh bits -8) 255)
|
|
212 1 nil work-buffer))
|
|
213 ((= counter 0) t)))
|
|
214 (t (skip-chars-forward non-data-chars end)))))
|
|
215 (and crlf
|
|
216 (save-excursion
|
|
217 (set-buffer work-buffer)
|
|
218 (vm-mime-crlf-to-lf-region (point-min) (point-max))))
|
|
219 (or (markerp end) (setq end (vm-marker end)))
|
|
220 (goto-char start)
|
|
221 (insert-buffer-substring work-buffer)
|
|
222 (delete-region (point) end))
|
|
223 (and work-buffer (kill-buffer work-buffer))))
|
26
|
224 (message "Decoding base64... done"))
|
20
|
225
|
24
|
226 (defun vm-mime-base64-encode-region (start end &optional crlf B-encoding)
|
|
227 (and (> (- end start) 200)
|
26
|
228 (message "Encoding base64..."))
|
20
|
229 (let ((work-buffer nil)
|
|
230 (counter 0)
|
|
231 (cols 0)
|
|
232 (bits 0)
|
|
233 (alphabet vm-mime-base64-alphabet)
|
|
234 inputpos)
|
|
235 (unwind-protect
|
|
236 (save-excursion
|
|
237 (setq work-buffer (generate-new-buffer " *vm-work*"))
|
|
238 (buffer-disable-undo work-buffer)
|
|
239 (if crlf
|
|
240 (progn
|
|
241 (or (markerp end) (setq end (vm-marker end)))
|
|
242 (vm-mime-lf-to-crlf-region start end)))
|
|
243 (if vm-mime-base64-encoder-program
|
|
244 (let ((status (apply 'vm-run-command-on-region
|
|
245 start end work-buffer
|
|
246 vm-mime-base64-encoder-program
|
|
247 vm-mime-base64-encoder-switches)))
|
|
248 (if (not (eq status t))
|
26
|
249 (vm-mime-error "%s" (cdr status)))
|
|
250 (if B-encoding
|
|
251 (progn
|
|
252 ;; if we're B encoding, strip out the line breaks
|
|
253 (goto-char (point-min))
|
|
254 (while (search-forward "\n" nil t)
|
|
255 (delete-char -1)))))
|
20
|
256 (setq inputpos start)
|
|
257 (while (< inputpos end)
|
|
258 (setq bits (+ bits (char-after inputpos)))
|
|
259 (vm-increment counter)
|
|
260 (cond ((= counter 3)
|
|
261 (vm-insert-char (aref alphabet (lsh bits -18)) 1 nil
|
|
262 work-buffer)
|
|
263 (vm-insert-char (aref alphabet (logand (lsh bits -12) 63))
|
|
264 1 nil work-buffer)
|
|
265 (vm-insert-char (aref alphabet (logand (lsh bits -6) 63))
|
|
266 1 nil work-buffer)
|
|
267 (vm-insert-char (aref alphabet (logand bits 63)) 1 nil
|
|
268 work-buffer)
|
|
269 (setq cols (+ cols 4))
|
|
270 (cond ((= cols 72)
|
24
|
271 (setq cols 0)
|
|
272 (if (not B-encoding)
|
|
273 (vm-insert-char ?\n 1 nil work-buffer))))
|
20
|
274 (setq bits 0 counter 0))
|
|
275 (t (setq bits (lsh bits 8))))
|
|
276 (vm-increment inputpos))
|
|
277 ;; write out any remaining bits with appropriate padding
|
|
278 (if (= counter 0)
|
|
279 nil
|
|
280 (setq bits (lsh bits (- 16 (* 8 counter))))
|
|
281 (vm-insert-char (aref alphabet (lsh bits -18)) 1 nil
|
|
282 work-buffer)
|
|
283 (vm-insert-char (aref alphabet (logand (lsh bits -12) 63))
|
|
284 1 nil work-buffer)
|
|
285 (if (= counter 1)
|
|
286 (vm-insert-char ?= 2 nil work-buffer)
|
|
287 (vm-insert-char (aref alphabet (logand (lsh bits -6) 63))
|
|
288 1 nil work-buffer)
|
|
289 (vm-insert-char ?= 1 nil work-buffer)))
|
|
290 (if (> cols 0)
|
|
291 (vm-insert-char ?\n 1 nil work-buffer)))
|
|
292 (or (markerp end) (setq end (vm-marker end)))
|
|
293 (goto-char start)
|
|
294 (insert-buffer-substring work-buffer)
|
24
|
295 (delete-region (point) end)
|
|
296 (and (> (- end start) 200)
|
26
|
297 (message "Encoding base64... done"))
|
24
|
298 (- end start))
|
|
299 (and work-buffer (kill-buffer work-buffer)))))
|
20
|
300
|
|
301 (defun vm-mime-qp-decode-region (start end)
|
24
|
302 (and (> (- end start) 200)
|
26
|
303 (message "Decoding quoted-printable..."))
|
20
|
304 (let ((work-buffer nil)
|
|
305 (buf (current-buffer))
|
|
306 (case-fold-search nil)
|
|
307 (hex-digit-alist '((?0 . 0) (?1 . 1) (?2 . 2) (?3 . 3)
|
|
308 (?4 . 4) (?5 . 5) (?6 . 6) (?7 . 7)
|
|
309 (?8 . 8) (?9 . 9) (?A . 10) (?B . 11)
|
|
310 (?C . 12) (?D . 13) (?E . 14) (?F . 15)))
|
|
311 inputpos stop-point copy-point)
|
|
312 (unwind-protect
|
|
313 (save-excursion
|
|
314 (setq work-buffer (generate-new-buffer " *vm-work*"))
|
|
315 (buffer-disable-undo work-buffer)
|
|
316 (goto-char start)
|
|
317 (setq inputpos start)
|
|
318 (while (< inputpos end)
|
|
319 (skip-chars-forward "^=\n" end)
|
|
320 (setq stop-point (point))
|
|
321 (cond ((looking-at "\n")
|
|
322 ;; spaces or tabs before a hard line break must be ignored
|
|
323 (skip-chars-backward " \t")
|
|
324 (setq copy-point (point))
|
|
325 (goto-char stop-point))
|
|
326 (t (setq copy-point stop-point)))
|
|
327 (save-excursion
|
|
328 (set-buffer work-buffer)
|
|
329 (insert-buffer-substring buf inputpos copy-point))
|
|
330 (cond ((= (point) end) t)
|
|
331 ((looking-at "\n")
|
|
332 (vm-insert-char ?\n 1 nil work-buffer)
|
|
333 (forward-char))
|
|
334 (t ;; looking at =
|
|
335 (forward-char)
|
|
336 (cond ((looking-at "[0-9A-F][0-9A-F]")
|
|
337 (vm-insert-char (+ (* (cdr (assq (char-after (point))
|
|
338 hex-digit-alist))
|
|
339 16)
|
|
340 (cdr (assq (char-after
|
|
341 (1+ (point)))
|
|
342 hex-digit-alist)))
|
|
343 1 nil work-buffer)
|
|
344 (forward-char 2))
|
|
345 ((looking-at "\n") ; soft line break
|
|
346 (forward-char))
|
|
347 ((looking-at "\r")
|
118
|
348 ;; assume the user's goatloving
|
20
|
349 ;; delivery software didn't convert
|
|
350 ;; from Internet's CRLF newline
|
|
351 ;; convention to the local LF
|
|
352 ;; convention.
|
|
353 (forward-char))
|
|
354 ((looking-at "[ \t]")
|
|
355 ;; garbage added in transit
|
|
356 (skip-chars-forward " \t" end))
|
|
357 (t (vm-mime-error "something other than line break or hex digits after = in quoted-printable encoding")))))
|
|
358 (setq inputpos (point)))
|
|
359 (or (markerp end) (setq end (vm-marker end)))
|
|
360 (goto-char start)
|
|
361 (insert-buffer-substring work-buffer)
|
|
362 (delete-region (point) end))
|
|
363 (and work-buffer (kill-buffer work-buffer))))
|
24
|
364 (and (> (- end start) 200)
|
26
|
365 (message "Decoding quoted-printable... done")))
|
20
|
366
|
30
|
367 (defun vm-mime-qp-encode-region (start end &optional Q-encoding quote-from)
|
24
|
368 (and (> (- end start) 200)
|
26
|
369 (message "Encoding quoted-printable..."))
|
20
|
370 (let ((work-buffer nil)
|
|
371 (buf (current-buffer))
|
|
372 (cols 0)
|
|
373 (hex-digit-alist '((?0 . 0) (?1 . 1) (?2 . 2) (?3 . 3)
|
|
374 (?4 . 4) (?5 . 5) (?6 . 6) (?7 . 7)
|
|
375 (?8 . 8) (?9 . 9) (?A . 10) (?B . 11)
|
|
376 (?C . 12) (?D . 13) (?E . 14) (?F . 15)))
|
|
377 char inputpos)
|
|
378 (unwind-protect
|
|
379 (save-excursion
|
|
380 (setq work-buffer (generate-new-buffer " *vm-work*"))
|
|
381 (buffer-disable-undo work-buffer)
|
|
382 (setq inputpos start)
|
|
383 (while (< inputpos end)
|
|
384 (setq char (char-after inputpos))
|
|
385 (cond ((= char ?\n)
|
|
386 (vm-insert-char char 1 nil work-buffer)
|
|
387 (setq cols 0))
|
114
|
388 ((and (= char 32)
|
|
389 (not (= (1+ inputpos) end))
|
|
390 (not (= ?\n (char-after (1+ inputpos)))))
|
20
|
391 (vm-insert-char char 1 nil work-buffer)
|
|
392 (vm-increment cols))
|
30
|
393 ((or (< char 33) (> char 126) (= char 61)
|
|
394 (and quote-from (= cols 0) (let ((case-fold-search nil))
|
|
395 (looking-at "From "))))
|
20
|
396 (vm-insert-char ?= 1 nil work-buffer)
|
|
397 (vm-insert-char (car (rassq (lsh char -4) hex-digit-alist))
|
|
398 1 nil work-buffer)
|
|
399 (vm-insert-char (car (rassq (logand char 15)
|
|
400 hex-digit-alist))
|
|
401 1 nil work-buffer)
|
|
402 (setq cols (+ cols 3)))
|
|
403 (t (vm-insert-char char 1 nil work-buffer)
|
|
404 (vm-increment cols)))
|
|
405 (cond ((> cols 70)
|
24
|
406 (setq cols 0)
|
|
407 (if Q-encoding
|
|
408 nil
|
|
409 (vm-insert-char ?= 1 nil work-buffer)
|
|
410 (vm-insert-char ?\n 1 nil work-buffer))))
|
20
|
411 (vm-increment inputpos))
|
|
412 (or (markerp end) (setq end (vm-marker end)))
|
|
413 (goto-char start)
|
|
414 (insert-buffer-substring work-buffer)
|
24
|
415 (delete-region (point) end)
|
|
416 (and (> (- end start) 200)
|
26
|
417 (message "Encoding quoted-printable... done"))
|
24
|
418 (- end start))
|
|
419 (and work-buffer (kill-buffer work-buffer)))))
|
20
|
420
|
|
421 (defun vm-decode-mime-message-headers (m)
|
|
422 (let ((case-fold-search t)
|
|
423 (buffer-read-only nil)
|
|
424 charset encoding match-start match-end start end)
|
|
425 (save-excursion
|
|
426 (goto-char (vm-headers-of m))
|
|
427 (while (re-search-forward vm-mime-encoded-word-regexp (vm-text-of m) t)
|
|
428 (setq match-start (match-beginning 0)
|
|
429 match-end (match-end 0)
|
30
|
430 charset (buffer-substring (match-beginning 1) (match-end 1))
|
|
431 encoding (buffer-substring (match-beginning 2) (match-end 2))
|
20
|
432 start (match-beginning 3)
|
|
433 end (vm-marker (match-end 3)))
|
|
434 ;; don't change anything if we can't display the
|
|
435 ;; character set properly.
|
|
436 (if (not (vm-mime-charset-internally-displayable-p charset))
|
|
437 nil
|
|
438 (delete-region end match-end)
|
30
|
439 (condition-case data
|
|
440 (cond ((string-match "B" encoding)
|
|
441 (vm-mime-B-decode-region start end))
|
|
442 ((string-match "Q" encoding)
|
|
443 (vm-mime-Q-decode-region start end))
|
|
444 (t (vm-mime-error "unknown encoded word encoding, %s"
|
|
445 encoding)))
|
|
446 (vm-mime-error (apply 'message (cdr data))
|
|
447 (goto-char start)
|
|
448 (insert "**invalid encoded word**")
|
|
449 (delete-region (point) end)))
|
20
|
450 (vm-mime-charset-decode-region charset start end)
|
114
|
451 (delete-region match-start start))))))
|
20
|
452
|
|
453 (defun vm-decode-mime-encoded-words ()
|
|
454 (let ((case-fold-search t)
|
|
455 (buffer-read-only nil)
|
|
456 charset encoding match-start match-end start end)
|
|
457 (save-excursion
|
|
458 (goto-char (point-min))
|
|
459 (while (re-search-forward vm-mime-encoded-word-regexp nil t)
|
|
460 (setq match-start (match-beginning 0)
|
|
461 match-end (match-end 0)
|
30
|
462 charset (buffer-substring (match-beginning 1) (match-end 1))
|
|
463 encoding (buffer-substring (match-beginning 2) (match-end 2))
|
20
|
464 start (match-beginning 3)
|
|
465 end (vm-marker (match-end 3)))
|
|
466 ;; don't change anything if we can't display the
|
|
467 ;; character set properly.
|
|
468 (if (not (vm-mime-charset-internally-displayable-p charset))
|
|
469 nil
|
|
470 (delete-region end match-end)
|
30
|
471 (condition-case data
|
|
472 (cond ((string-match "B" encoding)
|
|
473 (vm-mime-B-decode-region start end))
|
|
474 ((string-match "Q" encoding)
|
|
475 (vm-mime-Q-decode-region start end))
|
|
476 (t (vm-mime-error "unknown encoded word encoding, %s"
|
|
477 encoding)))
|
|
478 (vm-mime-error (apply 'message (cdr data))
|
|
479 (goto-char start)
|
|
480 (insert "**invalid encoded word**")
|
|
481 (delete-region (point) end)))
|
20
|
482 (vm-mime-charset-decode-region charset start end)
|
|
483 (delete-region match-start start))))))
|
|
484
|
24
|
485 (defun vm-decode-mime-encoded-words-in-string (string)
|
20
|
486 (if (and vm-display-using-mime
|
|
487 (string-match vm-mime-encoded-word-regexp string))
|
|
488 (vm-with-string-as-temp-buffer string 'vm-decode-mime-encoded-words)
|
|
489 string ))
|
|
490
|
24
|
491 (defun vm-reencode-mime-encoded-words ()
|
|
492 (let ((charset nil)
|
|
493 start coding pos q-encoding
|
|
494 old-size
|
|
495 (case-fold-search t)
|
|
496 (done nil))
|
|
497 (save-excursion
|
|
498 (setq start (point-min))
|
|
499 (while (not done)
|
|
500 (setq charset (get-text-property start 'vm-charset))
|
|
501 (setq pos (next-single-property-change start 'vm-charset))
|
|
502 (or pos (setq pos (point-max) done t))
|
|
503 (if charset
|
|
504 (progn
|
|
505 (if (setq coding (get-text-property start 'vm-coding))
|
|
506 (progn
|
|
507 (setq old-size (buffer-size))
|
|
508 (encode-coding-region start pos coding)
|
|
509 (setq pos (+ pos (- (buffer-size) old-size)))))
|
|
510 (setq pos
|
|
511 (+ start
|
|
512 (if (setq q-encoding
|
|
513 (string-match "^iso-8859-\\|^us-ascii"
|
|
514 charset))
|
|
515 (vm-mime-Q-encode-region start pos)
|
|
516 (vm-mime-B-encode-region start pos))))
|
|
517 (goto-char pos)
|
|
518 (insert "?=")
|
|
519 (setq pos (point))
|
|
520 (goto-char start)
|
|
521 (insert "=?" charset "?" (if q-encoding "Q" "B") "?")))
|
|
522 (setq start pos)))))
|
|
523
|
|
524 (defun vm-reencode-mime-encoded-words-in-string (string)
|
|
525 (if (and vm-display-using-mime
|
|
526 (text-property-any 0 (length string) 'vm-string t string))
|
|
527 (vm-with-string-as-temp-buffer string 'vm-reencode-mime-encoded-words)
|
|
528 string ))
|
|
529
|
118
|
530 (fset 'vm-mime-parse-content-header 'vm-parse-structured-header)
|
20
|
531
|
|
532 (defun vm-mime-get-header-contents (header-name-regexp)
|
|
533 (let ((contents nil)
|
|
534 regexp)
|
|
535 (setq regexp (concat "^\\(" header-name-regexp "\\)\\|\\(^$\\)"))
|
|
536 (save-excursion
|
|
537 (let ((case-fold-search t))
|
|
538 (if (and (re-search-forward regexp nil t)
|
|
539 (match-beginning 1)
|
|
540 (progn (goto-char (match-beginning 0))
|
|
541 (vm-match-header)))
|
|
542 (vm-matched-header-contents)
|
|
543 nil )))))
|
|
544
|
|
545 (defun vm-mime-parse-entity (&optional m default-type default-encoding)
|
24
|
546 (let ((case-fold-search t) version type qtype encoding id description
|
|
547 disposition qdisposition boundary boundary-regexp start
|
20
|
548 multipart-list c-t c-t-e done p returnval)
|
|
549 (catch 'return-value
|
|
550 (save-excursion
|
|
551 (if m
|
|
552 (progn
|
|
553 (setq m (vm-real-message-of m))
|
|
554 (set-buffer (vm-buffer-of m))))
|
|
555 (save-excursion
|
|
556 (save-restriction
|
|
557 (if m
|
|
558 (progn
|
|
559 (setq version (vm-get-header-contents m "MIME-Version:")
|
|
560 version (car (vm-mime-parse-content-header version))
|
|
561 type (vm-get-header-contents m "Content-Type:")
|
24
|
562 qtype (vm-mime-parse-content-header type ?\; t)
|
20
|
563 type (vm-mime-parse-content-header type ?\;)
|
|
564 encoding (or (vm-get-header-contents
|
|
565 m "Content-Transfer-Encoding:")
|
|
566 "7bit")
|
|
567 encoding (car (vm-mime-parse-content-header encoding))
|
|
568 id (vm-get-header-contents m "Content-ID:")
|
|
569 id (car (vm-mime-parse-content-header id))
|
|
570 description (vm-get-header-contents
|
|
571 m "Content-Description:")
|
|
572 description (and description
|
|
573 (if (string-match "^[ \t\n]$"
|
|
574 description)
|
|
575 nil
|
|
576 description))
|
|
577 disposition (vm-get-header-contents
|
|
578 m "Content-Disposition:")
|
24
|
579 qdisposition (and disposition
|
|
580 (vm-mime-parse-content-header
|
|
581 disposition ?\; t))
|
20
|
582 disposition (and disposition
|
|
583 (vm-mime-parse-content-header
|
|
584 disposition ?\;)))
|
|
585 (widen)
|
|
586 (narrow-to-region (vm-headers-of m) (vm-text-end-of m)))
|
|
587 (goto-char (point-min))
|
|
588 (setq type (vm-mime-get-header-contents "Content-Type:")
|
24
|
589 qtype (or (vm-mime-parse-content-header type ?\; t)
|
|
590 default-type)
|
20
|
591 type (or (vm-mime-parse-content-header type ?\;)
|
|
592 default-type)
|
|
593 encoding (or (vm-mime-get-header-contents
|
|
594 "Content-Transfer-Encoding:")
|
|
595 default-encoding)
|
|
596 encoding (car (vm-mime-parse-content-header encoding))
|
|
597 id (vm-mime-get-header-contents "Content-ID:")
|
|
598 id (car (vm-mime-parse-content-header id))
|
|
599 description (vm-mime-get-header-contents
|
|
600 "Content-Description:")
|
|
601 description (and description (if (string-match "^[ \t\n]+$"
|
|
602 description)
|
|
603 nil
|
|
604 description))
|
|
605 disposition (vm-mime-get-header-contents
|
|
606 "Content-Disposition:")
|
24
|
607 qdisposition (and disposition
|
|
608 (vm-mime-parse-content-header
|
|
609 disposition ?\; t))
|
20
|
610 disposition (and disposition
|
|
611 (vm-mime-parse-content-header
|
|
612 disposition ?\;))))
|
|
613 (cond ((null m) t)
|
|
614 ((null version)
|
|
615 (throw 'return-value 'none))
|
114
|
616 ((or vm-mime-ignore-mime-version (string= version "1.0")) t)
|
20
|
617 (t (vm-mime-error "Unsupported MIME version: %s" version)))
|
|
618 (cond ((and m (null type))
|
|
619 (throw 'return-value
|
|
620 (vector '("text/plain" "charset=us-ascii")
|
24
|
621 '("text/plain" "charset=us-ascii")
|
|
622 encoding id description
|
|
623 disposition qdisposition
|
20
|
624 (vm-headers-of m)
|
|
625 (vm-text-of m)
|
|
626 (vm-text-end-of m)
|
136
|
627 nil nil )))
|
20
|
628 ((null type)
|
|
629 (goto-char (point-min))
|
|
630 (or (re-search-forward "^\n\\|\n\\'" nil t)
|
|
631 (vm-mime-error "MIME part missing header/body separator line"))
|
24
|
632 (vector default-type default-type
|
|
633 encoding id description
|
|
634 disposition qdisposition
|
20
|
635 (vm-marker (point-min))
|
|
636 (vm-marker (point))
|
|
637 (vm-marker (point-max))
|
136
|
638 nil nil ))
|
20
|
639 ((null (string-match "[^/ ]+/[^/ ]+" (car type)))
|
|
640 (vm-mime-error "Malformed MIME content type: %s" (car type)))
|
|
641 ((and (string-match "^multipart/\\|^message/" (car type))
|
|
642 (null (string-match "^\\(7bit\\|8bit\\|binary\\)$"
|
|
643 encoding)))
|
|
644 (vm-mime-error "Opaque transfer encoding used with multipart or message type: %s, %s" (car type) encoding))
|
|
645 ((and (string-match "^message/partial$" (car type))
|
|
646 (null (string-match "^7bit$" encoding)))
|
|
647 (vm-mime-error "Non-7BIT transfer encoding used with message/partial message: %s" encoding))
|
|
648 ((string-match "^multipart/digest" (car type))
|
|
649 (setq c-t '("message/rfc822")
|
|
650 c-t-e "7bit"))
|
|
651 ((string-match "^multipart/" (car type))
|
|
652 (setq c-t '("text/plain" "charset=us-ascii")
|
|
653 c-t-e "7bit")) ; below
|
30
|
654 ((string-match "^message/\\(rfc822\\|news\\)" (car type))
|
20
|
655 (setq c-t '("text/plain" "charset=us-ascii")
|
|
656 c-t-e "7bit")
|
|
657 (goto-char (point-min))
|
|
658 (or (re-search-forward "^\n\\|\n\\'" nil t)
|
|
659 (vm-mime-error "MIME part missing header/body separator line"))
|
|
660 (throw 'return-value
|
24
|
661 (vector type qtype encoding id description
|
|
662 disposition qdisposition
|
20
|
663 (vm-marker (point-min))
|
|
664 (vm-marker (point))
|
|
665 (vm-marker (point-max))
|
|
666 (list
|
|
667 (save-restriction
|
|
668 (narrow-to-region (point) (point-max))
|
24
|
669 (vm-mime-parse-entity-safe nil c-t
|
|
670 c-t-e)))
|
20
|
671 nil )))
|
|
672 (t
|
|
673 (goto-char (point-min))
|
|
674 (or (re-search-forward "^\n\\|\n\\'" nil t)
|
|
675 (vm-mime-error "MIME part missing header/body separator line"))
|
|
676 (throw 'return-value
|
24
|
677 (vector type qtype encoding id description
|
|
678 disposition qdisposition
|
20
|
679 (vm-marker (point-min))
|
|
680 (vm-marker (point))
|
|
681 (vm-marker (point-max))
|
|
682 nil nil ))))
|
|
683 (setq p (cdr type)
|
|
684 boundary nil)
|
|
685 (while p
|
|
686 (if (string-match "^boundary=" (car p))
|
|
687 (setq boundary (car (vm-parse (car p) "=\\(.+\\)"))
|
|
688 p nil)
|
|
689 (setq p (cdr p))))
|
|
690 (or boundary
|
|
691 (vm-mime-error
|
|
692 "Boundary parameter missing in %s type specification"
|
|
693 (car type)))
|
24
|
694 ;; the \' in the regexp is to "be liberal" in the
|
|
695 ;; face of broken software that does not add a line
|
|
696 ;; break after the final boundary of a nested
|
|
697 ;; multipart entity.
|
|
698 (setq boundary-regexp
|
|
699 (concat "^--" (regexp-quote boundary)
|
|
700 "\\(--\\)?[ \t]*\\(\n\\|\\'\\)"))
|
20
|
701 (goto-char (point-min))
|
|
702 (setq start nil
|
|
703 multipart-list nil
|
|
704 done nil)
|
|
705 (while (and (not done) (re-search-forward boundary-regexp nil t))
|
|
706 (cond ((null start)
|
|
707 (setq start (match-end 0)))
|
|
708 (t
|
|
709 (and (match-beginning 1)
|
|
710 (setq done t))
|
|
711 (save-excursion
|
|
712 (save-restriction
|
|
713 (narrow-to-region start (1- (match-beginning 0)))
|
|
714 (setq start (match-end 0))
|
|
715 (setq multipart-list
|
|
716 (cons (vm-mime-parse-entity-safe nil c-t c-t-e)
|
|
717 multipart-list)))))))
|
|
718 (if (not done)
|
|
719 (vm-mime-error "final %s boundary missing" boundary))
|
|
720 (goto-char (point-min))
|
|
721 (or (re-search-forward "^\n\\|\n\\'" nil t)
|
|
722 (vm-mime-error "MIME part missing header/body separator line"))
|
24
|
723 (vector type qtype encoding id description
|
|
724 disposition qdisposition
|
20
|
725 (vm-marker (point-min))
|
|
726 (vm-marker (point))
|
|
727 (vm-marker (point-max))
|
|
728 (nreverse multipart-list)
|
114
|
729 nil )))))))
|
20
|
730
|
|
731 (defun vm-mime-parse-entity-safe (&optional m c-t c-t-e)
|
|
732 (or c-t (setq c-t '("text/plain" "charset=us-ascii")))
|
|
733 ;; don't let subpart parse errors make the whole parse fail. use default
|
|
734 ;; type if the parse fails.
|
|
735 (condition-case error-data
|
|
736 (vm-mime-parse-entity nil c-t c-t-e)
|
|
737 (vm-mime-error
|
|
738 (let ((header (if m
|
|
739 (vm-headers-of m)
|
|
740 (vm-marker (point-min))))
|
|
741 (text (if m
|
|
742 (vm-text-of m)
|
|
743 (save-excursion
|
|
744 (re-search-forward "^\n\\|\n\\'"
|
|
745 nil 0)
|
|
746 (vm-marker (point)))))
|
|
747 (text-end (if m
|
|
748 (vm-text-end-of m)
|
|
749 (vm-marker (point-max)))))
|
24
|
750 (vector c-t c-t
|
20
|
751 (vm-determine-proper-content-transfer-encoding text text-end)
|
|
752 nil
|
|
753 ;; cram the error message into the description slot
|
24
|
754 (car (cdr error-data))
|
20
|
755 ;; mark as an attachment to improve the chance that the user
|
|
756 ;; will see the description.
|
24
|
757 '("attachment") '("attachment")
|
20
|
758 header
|
|
759 text
|
136
|
760 text-end
|
|
761 nil nil)))))
|
20
|
762
|
|
763 (defun vm-mime-get-xxx-parameter (layout name param-list)
|
|
764 (let ((match-end (1+ (length name)))
|
|
765 (name-regexp (concat (regexp-quote name) "="))
|
|
766 (case-fold-search t)
|
|
767 (done nil))
|
|
768 (while (and param-list (not done))
|
|
769 (if (and (string-match name-regexp (car param-list))
|
|
770 (= (match-end 0) match-end))
|
|
771 (setq done t)
|
|
772 (setq param-list (cdr param-list))))
|
|
773 (and (car param-list) (car (vm-parse (car param-list) "=\\(.*\\)")))))
|
|
774
|
|
775 (defun vm-mime-get-parameter (layout name)
|
|
776 (vm-mime-get-xxx-parameter layout name (cdr (vm-mm-layout-type layout))))
|
|
777
|
|
778 (defun vm-mime-get-disposition-parameter (layout name)
|
|
779 (vm-mime-get-xxx-parameter layout name
|
|
780 (cdr (vm-mm-layout-disposition layout))))
|
|
781
|
|
782 (defun vm-mime-insert-mime-body (layout)
|
|
783 (vm-insert-region-from-buffer (marker-buffer (vm-mm-layout-body-start layout))
|
|
784 (vm-mm-layout-body-start layout)
|
|
785 (vm-mm-layout-body-end layout)))
|
|
786
|
|
787 (defun vm-mime-insert-mime-headers (layout)
|
|
788 (vm-insert-region-from-buffer (marker-buffer (vm-mm-layout-body-start layout))
|
|
789 (vm-mm-layout-header-start layout)
|
|
790 (vm-mm-layout-body-start layout))
|
|
791 (if (and (not (bobp)) (char-equal (char-after (1- (point))) ?\n))
|
|
792 (delete-char -1)))
|
|
793
|
|
794 (defun vm-make-presentation-copy (m)
|
|
795 (let ((mail-buffer (current-buffer))
|
|
796 b mm
|
|
797 (real-m (vm-real-message-of m))
|
|
798 (modified (buffer-modified-p)))
|
|
799 (cond ((or (null vm-presentation-buffer-handle)
|
|
800 (null (buffer-name vm-presentation-buffer-handle)))
|
|
801 (setq b (generate-new-buffer (concat (buffer-name)
|
|
802 " Presentation")))
|
|
803 (save-excursion
|
|
804 (set-buffer b)
|
|
805 (if (fboundp 'buffer-disable-undo)
|
|
806 (buffer-disable-undo (current-buffer))
|
|
807 ;; obfuscation to make the v19 compiler not whine
|
|
808 ;; about obsolete functions.
|
|
809 (let ((x 'buffer-flush-undo))
|
|
810 (funcall x (current-buffer))))
|
|
811 (setq mode-name "VM Presentation"
|
|
812 major-mode 'vm-presentation-mode
|
|
813 vm-message-pointer (list nil)
|
|
814 vm-mail-buffer mail-buffer
|
|
815 mode-popup-menu (and vm-use-menus vm-popup-menu-on-mouse-3
|
|
816 (vm-menu-support-possible-p)
|
|
817 (vm-menu-mode-menu))
|
24
|
818 ;; Default to binary file type for DOS/NT.
|
|
819 buffer-file-type t
|
|
820 ;; Tell XEmacs/MULE not to mess with the text on writes.
|
20
|
821 buffer-read-only t
|
|
822 mode-line-format vm-mode-line-format)
|
30
|
823 ;; scroll in place messes with scroll-up and this loses
|
|
824 (defvar scroll-in-place)
|
|
825 (make-local-variable 'scroll-in-place)
|
|
826 (setq scroll-in-place nil)
|
120
|
827 (and vm-xemacs-mule-p
|
136
|
828 (set-file-coding-system 'binary t))
|
120
|
829 (cond (vm-fsfemacs-19-p
|
20
|
830 ;; need to do this outside the let because
|
|
831 ;; loading disp-table initializes
|
|
832 ;; standard-display-table.
|
|
833 (require 'disp-table)
|
|
834 (let* ((standard-display-table
|
|
835 (copy-sequence standard-display-table)))
|
|
836 (standard-display-european t)
|
|
837 (setq buffer-display-table standard-display-table))))
|
30
|
838 (if (and vm-frame-per-folder (vm-multiple-frames-possible-p))
|
20
|
839 (vm-set-hooks-for-frame-deletion))
|
|
840 (use-local-map vm-mode-map)
|
|
841 (and (vm-toolbar-support-possible-p) vm-use-toolbar
|
|
842 (vm-toolbar-install-toolbar))
|
|
843 (and (vm-menu-support-possible-p)
|
114
|
844 (vm-menu-install-menus))
|
|
845 (run-hooks 'vm-presentation-mode-hook))
|
20
|
846 (setq vm-presentation-buffer-handle b)))
|
|
847 (setq b vm-presentation-buffer-handle
|
|
848 vm-presentation-buffer vm-presentation-buffer-handle
|
|
849 vm-mime-decoded nil)
|
|
850 (save-excursion
|
|
851 (set-buffer (vm-buffer-of real-m))
|
|
852 (save-restriction
|
|
853 (widen)
|
|
854 ;; must reference this now so that headers will be in
|
|
855 ;; their final position before the message is copied.
|
|
856 ;; otherwise the vheader offset computed below will be
|
|
857 ;; wrong.
|
|
858 (vm-vheaders-of real-m)
|
|
859 (set-buffer b)
|
|
860 (widen)
|
|
861 (let ((buffer-read-only nil)
|
|
862 (modified (buffer-modified-p)))
|
|
863 (unwind-protect
|
|
864 (progn
|
|
865 (erase-buffer)
|
|
866 (insert-buffer-substring (vm-buffer-of real-m)
|
|
867 (vm-start-of real-m)
|
|
868 (vm-end-of real-m)))
|
|
869 (set-buffer-modified-p modified)))
|
|
870 (setq mm (copy-sequence m))
|
|
871 (vm-set-location-data-of mm (vm-copy (vm-location-data-of m)))
|
|
872 (set-marker (vm-start-of mm) (point-min))
|
|
873 (set-marker (vm-headers-of mm) (+ (vm-start-of mm)
|
|
874 (- (vm-headers-of real-m)
|
|
875 (vm-start-of real-m))))
|
|
876 (set-marker (vm-vheaders-of mm) (+ (vm-start-of mm)
|
|
877 (- (vm-vheaders-of real-m)
|
|
878 (vm-start-of real-m))))
|
|
879 (set-marker (vm-text-of mm) (+ (vm-start-of mm)
|
|
880 (- (vm-text-of real-m)
|
|
881 (vm-start-of real-m))))
|
|
882 (set-marker (vm-text-end-of mm) (+ (vm-start-of mm)
|
|
883 (- (vm-text-end-of real-m)
|
|
884 (vm-start-of real-m))))
|
|
885 (set-marker (vm-end-of mm) (+ (vm-start-of mm)
|
|
886 (- (vm-end-of real-m)
|
|
887 (vm-start-of real-m))))
|
|
888 (setcar vm-message-pointer mm)))))
|
|
889
|
|
890 (fset 'vm-presentation-mode 'vm-mode)
|
|
891 (put 'vm-presentation-mode 'mode-class 'special)
|
|
892
|
136
|
893 (defvar file-coding-system)
|
24
|
894
|
20
|
895 (defun vm-determine-proper-charset (beg end)
|
|
896 (save-excursion
|
|
897 (save-restriction
|
|
898 (narrow-to-region beg end)
|
|
899 (catch 'done
|
|
900 (goto-char (point-min))
|
120
|
901 (if vm-xemacs-mule-p
|
24
|
902 (let ((charsets (delq 'ascii (charsets-in-region beg end))))
|
|
903 (cond ((null charsets)
|
|
904 "us-ascii")
|
|
905 ((cdr charsets)
|
|
906 (or (car (cdr
|
136
|
907 (assoc (coding-system-name file-coding-system)
|
|
908 vm-mime-mule-coding-to-charset-alist)))
|
24
|
909 "iso-2022-jp"))
|
|
910 (t
|
|
911 (or (car (cdr
|
30
|
912 (assoc
|
24
|
913 (car charsets)
|
|
914 vm-mime-mule-charset-to-charset-alist)))
|
|
915 "unknown"))))
|
|
916 (and (re-search-forward "[^\000-\177]" nil t)
|
|
917 (throw 'done (or vm-mime-8bit-composition-charset
|
|
918 "iso-8859-1")))
|
|
919 (throw 'done "us-ascii"))))))
|
20
|
920
|
|
921 (defun vm-determine-proper-content-transfer-encoding (beg end)
|
|
922 (save-excursion
|
|
923 (save-restriction
|
|
924 (narrow-to-region beg end)
|
|
925 (catch 'done
|
|
926 (goto-char (point-min))
|
|
927 (and (re-search-forward "[\000\015]" nil t)
|
|
928 (throw 'done "binary"))
|
|
929
|
|
930 (let ((toolong nil) bol)
|
|
931 (goto-char (point-min))
|
|
932 (setq bol (point))
|
|
933 (while (and (not (eobp)) (not toolong))
|
|
934 (forward-line)
|
|
935 (setq toolong (> (- (point) bol) 998)
|
|
936 bol (point)))
|
|
937 (and toolong (throw 'done "binary")))
|
|
938
|
|
939 (goto-char (point-min))
|
|
940 (and (re-search-forward "[\200-\377]" nil t)
|
|
941 (throw 'done "8bit"))
|
|
942
|
|
943 "7bit"))))
|
|
944
|
|
945 (defun vm-mime-types-match (type type/subtype)
|
|
946 (let ((case-fold-search t))
|
|
947 (cond ((string-match "/" type)
|
|
948 (if (and (string-match (regexp-quote type) type/subtype)
|
|
949 (equal 0 (match-beginning 0))
|
|
950 (equal (length type/subtype) (match-end 0)))
|
|
951 t
|
|
952 nil ))
|
|
953 ((and (string-match (regexp-quote type) type/subtype)
|
|
954 (equal 0 (match-beginning 0))
|
|
955 (equal (save-match-data
|
|
956 (string-match "/" type/subtype (match-end 0)))
|
|
957 (match-end 0)))))))
|
|
958
|
|
959 (defvar native-sound-only-on-console)
|
|
960
|
|
961 (defun vm-mime-can-display-internal (layout)
|
|
962 (let ((type (car (vm-mm-layout-type layout))))
|
|
963 (cond ((vm-mime-types-match "image/jpeg" type)
|
120
|
964 (and vm-xemacs-p
|
20
|
965 (featurep 'jpeg)
|
|
966 (eq (device-type) 'x)))
|
|
967 ((vm-mime-types-match "image/gif" type)
|
120
|
968 (and vm-xemacs-p
|
20
|
969 (featurep 'gif)
|
|
970 (eq (device-type) 'x)))
|
|
971 ((vm-mime-types-match "image/png" type)
|
120
|
972 (and vm-xemacs-p
|
20
|
973 (featurep 'png)
|
|
974 (eq (device-type) 'x)))
|
|
975 ((vm-mime-types-match "image/tiff" type)
|
120
|
976 (and vm-xemacs-p
|
20
|
977 (featurep 'tiff)
|
|
978 (eq (device-type) 'x)))
|
|
979 ((vm-mime-types-match "audio/basic" type)
|
120
|
980 (and vm-xemacs-p
|
20
|
981 (or (featurep 'native-sound)
|
|
982 (featurep 'nas-sound))
|
|
983 (or (device-sound-enabled-p)
|
|
984 (and (featurep 'native-sound)
|
|
985 (not native-sound-only-on-console)
|
|
986 (eq (device-type) 'x)))))
|
|
987 ((vm-mime-types-match "multipart" type) t)
|
|
988 ((vm-mime-types-match "message/external-body" type) nil)
|
|
989 ((vm-mime-types-match "message" type) t)
|
|
990 ((or (vm-mime-types-match "text/plain" type)
|
|
991 (vm-mime-types-match "text/enriched" type))
|
|
992 (let ((charset (or (vm-mime-get-parameter layout "charset")
|
|
993 "us-ascii")))
|
|
994 (vm-mime-charset-internally-displayable-p charset)))
|
120
|
995 ((vm-mime-types-match "text/html" type)
|
|
996 (condition-case ()
|
|
997 (progn (require 'w3)
|
|
998 (fboundp 'w3-region))
|
|
999 (error nil)))
|
20
|
1000 (t nil))))
|
|
1001
|
|
1002 (defun vm-mime-can-convert (type)
|
|
1003 (let ((alist vm-mime-type-converter-alist)
|
|
1004 ;; fake layout. make it the wrong length so an error will
|
|
1005 ;; be signaled if vm-mime-can-display-internal ever asks
|
|
1006 ;; for one of the other fields
|
|
1007 (fake-layout (make-vector 1 (list nil)))
|
|
1008 (done nil))
|
|
1009 (while (and alist (not done))
|
|
1010 (cond ((and (vm-mime-types-match (car (car alist)) type)
|
|
1011 (or (progn
|
|
1012 (setcar (aref fake-layout 0) (nth 1 (car alist)))
|
|
1013 (vm-mime-can-display-internal fake-layout))
|
|
1014 (vm-mime-find-external-viewer (nth 1 (car alist)))))
|
|
1015 (setq done t))
|
|
1016 (t (setq alist (cdr alist)))))
|
|
1017 (and alist (car alist))))
|
|
1018
|
|
1019 (defun vm-mime-convert-undisplayable-layout (layout)
|
|
1020 (let ((ooo (vm-mime-can-convert (car (vm-mm-layout-type layout)))))
|
26
|
1021 (message "Converting %s to %s..."
|
20
|
1022 (car (vm-mm-layout-type layout))
|
|
1023 (nth 1 ooo))
|
|
1024 (save-excursion
|
|
1025 (set-buffer (generate-new-buffer " *mime object*"))
|
|
1026 (setq vm-message-garbage-alist
|
|
1027 (cons (cons (current-buffer) 'kill-buffer)
|
|
1028 vm-message-garbage-alist))
|
|
1029 (vm-mime-insert-mime-body layout)
|
|
1030 (vm-mime-transfer-decode-region layout (point-min) (point-max))
|
|
1031 (call-process-region (point-min) (point-max) shell-file-name
|
|
1032 t t nil shell-command-switch (nth 2 ooo))
|
|
1033 (goto-char (point-min))
|
|
1034 (insert "Content-Type: " (nth 1 ooo) "\n")
|
|
1035 (insert "Content-Transfer-Encoding: binary\n\n")
|
|
1036 (set-buffer-modified-p nil)
|
26
|
1037 (message "Converting %s to %s... done"
|
20
|
1038 (car (vm-mm-layout-type layout))
|
|
1039 (nth 1 ooo))
|
|
1040 (vector (list (nth 1 ooo))
|
24
|
1041 (list (nth 1 ooo))
|
20
|
1042 "binary"
|
|
1043 (vm-mm-layout-id layout)
|
|
1044 (vm-mm-layout-description layout)
|
|
1045 (vm-mm-layout-disposition layout)
|
24
|
1046 (vm-mm-layout-qdisposition layout)
|
20
|
1047 (vm-marker (point-min))
|
|
1048 (vm-marker (point))
|
|
1049 (vm-marker (point-max))
|
|
1050 nil
|
|
1051 nil ))))
|
|
1052
|
|
1053 (defun vm-mime-should-display-button (layout dont-honor-content-disposition)
|
|
1054 (if (and vm-honor-mime-content-disposition
|
|
1055 (not dont-honor-content-disposition)
|
|
1056 (vm-mm-layout-disposition layout))
|
|
1057 (let ((case-fold-search t))
|
|
1058 (string-match "^attachment$" (car (vm-mm-layout-disposition layout))))
|
|
1059 (let ((i-list vm-auto-displayed-mime-content-types)
|
|
1060 (type (car (vm-mm-layout-type layout)))
|
|
1061 (matched nil))
|
|
1062 (if (eq i-list t)
|
|
1063 nil
|
|
1064 (while (and i-list (not matched))
|
|
1065 (if (vm-mime-types-match (car i-list) type)
|
|
1066 (setq matched t)
|
|
1067 (setq i-list (cdr i-list))))
|
|
1068 (not matched) ))))
|
|
1069
|
|
1070 (defun vm-mime-should-display-internal (layout dont-honor-content-disposition)
|
|
1071 (if (and vm-honor-mime-content-disposition
|
|
1072 (not dont-honor-content-disposition)
|
|
1073 (vm-mm-layout-disposition layout))
|
|
1074 (let ((case-fold-search t))
|
|
1075 (string-match "^inline$" (car (vm-mm-layout-disposition layout))))
|
|
1076 (let ((i-list vm-mime-internal-content-types)
|
|
1077 (type (car (vm-mm-layout-type layout)))
|
|
1078 (matched nil))
|
|
1079 (if (eq i-list t)
|
|
1080 t
|
|
1081 (while (and i-list (not matched))
|
|
1082 (if (vm-mime-types-match (car i-list) type)
|
|
1083 (setq matched t)
|
|
1084 (setq i-list (cdr i-list))))
|
|
1085 matched ))))
|
|
1086
|
|
1087 (defun vm-mime-find-external-viewer (type)
|
|
1088 (let ((e-alist vm-mime-external-content-types-alist)
|
|
1089 (matched nil))
|
|
1090 (while (and e-alist (not matched))
|
|
1091 (if (and (vm-mime-types-match (car (car e-alist)) type)
|
|
1092 (cdr (car e-alist)))
|
|
1093 (setq matched (cdr (car e-alist)))
|
|
1094 (setq e-alist (cdr e-alist))))
|
|
1095 matched ))
|
|
1096 (fset 'vm-mime-should-display-external 'vm-mime-find-external-viewer)
|
|
1097
|
|
1098 (defun vm-mime-delete-button-maybe (extent)
|
|
1099 (let ((buffer-read-only))
|
|
1100 ;; if displayed MIME object should replace the button
|
|
1101 ;; remove the button now.
|
|
1102 (cond ((vm-extent-property extent 'vm-mime-disposable)
|
|
1103 (delete-region (vm-extent-start-position extent)
|
|
1104 (vm-extent-end-position extent))
|
|
1105 (vm-detach-extent extent)))))
|
|
1106
|
|
1107 (defun vm-decode-mime-message ()
|
|
1108 "Decode the MIME objects in the current message.
|
|
1109
|
|
1110 The first time this command is run on a message, decoding is done.
|
|
1111 The second time, buttons for all the objects are displayed instead.
|
|
1112 The third time, the raw, undecoded data is displayed.
|
|
1113
|
|
1114 If decoding, the decoded objects might be displayed immediately, or
|
|
1115 buttons might be displayed that you need to activate to view the
|
|
1116 object. See the documentation for the variables
|
|
1117
|
|
1118 vm-auto-displayed-mime-content-types
|
|
1119 vm-mime-internal-content-types
|
|
1120 vm-mime-external-content-types-alist
|
|
1121
|
|
1122 to see how to control whether you see buttons or objects.
|
|
1123
|
|
1124 If the variable vm-mime-display-function is set, then its value
|
|
1125 is called as a function with no arguments, and none of the
|
|
1126 actions mentioned in the preceding paragraphs are done. At the
|
|
1127 time of the call, the current buffer will be the presentation
|
|
1128 buffer for the folder and a copy of the current message will be
|
|
1129 in the buffer. The function is expected to make the message
|
|
1130 `MIME presentable' to the user in whatever manner it sees fit."
|
|
1131 (interactive)
|
|
1132 (vm-follow-summary-cursor)
|
|
1133 (vm-select-folder-buffer)
|
|
1134 (vm-check-for-killed-summary)
|
|
1135 (vm-check-for-killed-presentation)
|
|
1136 (vm-error-if-folder-empty)
|
|
1137 (if (and (not vm-display-using-mime)
|
|
1138 (null vm-mime-display-function))
|
|
1139 (error "MIME display disabled, set vm-display-using-mime non-nil to enable."))
|
|
1140 (if vm-mime-display-function
|
|
1141 (progn
|
|
1142 (vm-make-presentation-copy (car vm-message-pointer))
|
|
1143 (set-buffer vm-presentation-buffer)
|
|
1144 (funcall vm-mime-display-function))
|
|
1145 (if vm-mime-decoded
|
|
1146 (if (eq vm-mime-decoded 'decoded)
|
|
1147 (let ((vm-preview-read-messages nil)
|
|
1148 (vm-auto-decode-mime-messages t)
|
|
1149 (vm-honor-mime-content-disposition nil)
|
|
1150 (vm-auto-displayed-mime-content-types '("multipart")))
|
|
1151 (setq vm-mime-decoded nil)
|
|
1152 (intern (buffer-name) vm-buffers-needing-display-update)
|
|
1153 (save-excursion
|
|
1154 (vm-preview-current-message))
|
|
1155 (setq vm-mime-decoded 'buttons))
|
|
1156 (let ((vm-preview-read-messages nil)
|
|
1157 (vm-auto-decode-mime-messages nil))
|
|
1158 (intern (buffer-name) vm-buffers-needing-display-update)
|
|
1159 (vm-preview-current-message)))
|
|
1160 (let ((layout (vm-mm-layout (car vm-message-pointer)))
|
|
1161 (m (car vm-message-pointer)))
|
26
|
1162 (message "Decoding MIME message...")
|
20
|
1163 (cond ((stringp layout)
|
|
1164 (error "Invalid MIME message: %s" layout)))
|
|
1165 (if (vm-mime-plain-message-p m)
|
|
1166 (error "Message needs no decoding."))
|
|
1167 (or vm-presentation-buffer
|
|
1168 ;; maybe user killed it
|
|
1169 (error "No presentation buffer."))
|
|
1170 (set-buffer vm-presentation-buffer)
|
24
|
1171 (if (and (interactive-p) (eq vm-system-state 'previewing))
|
|
1172 (let ((vm-display-using-mime nil))
|
|
1173 (vm-show-current-message)))
|
20
|
1174 (setq m (car vm-message-pointer))
|
|
1175 (vm-save-restriction
|
|
1176 (widen)
|
|
1177 (goto-char (vm-text-of m))
|
|
1178 (let ((buffer-read-only nil)
|
|
1179 (modified (buffer-modified-p)))
|
|
1180 (unwind-protect
|
|
1181 (save-excursion
|
|
1182 (and (not (eq (vm-mm-encoded-header m) 'none))
|
|
1183 (vm-decode-mime-message-headers m))
|
|
1184 (if (vectorp layout)
|
|
1185 (progn
|
|
1186 (vm-decode-mime-layout layout)
|
114
|
1187 (delete-region (point) (point-max))))
|
|
1188 (vm-energize-urls)
|
|
1189 (vm-highlight-headers-maybe)
|
|
1190 (vm-energize-headers-and-xfaces))
|
20
|
1191 (set-buffer-modified-p modified))))
|
|
1192 (save-excursion (set-buffer vm-mail-buffer)
|
|
1193 (setq vm-mime-decoded 'decoded))
|
|
1194 (intern (buffer-name vm-mail-buffer) vm-buffers-needing-display-update)
|
|
1195 (vm-update-summary-and-mode-line)
|
26
|
1196 (message "Decoding MIME message... done"))))
|
20
|
1197 (vm-display nil nil '(vm-decode-mime-message)
|
|
1198 '(vm-decode-mime-message reading-message)))
|
|
1199
|
|
1200 (defun vm-decode-mime-layout (layout &optional dont-honor-c-d)
|
|
1201 (let ((modified (buffer-modified-p)) type type-no-subtype (extent nil))
|
|
1202 (unwind-protect
|
|
1203 (progn
|
|
1204 (if (not (vectorp layout))
|
|
1205 (progn
|
|
1206 (setq extent layout
|
|
1207 layout (vm-extent-property extent 'vm-mime-layout))
|
|
1208 (goto-char (vm-extent-start-position extent))))
|
|
1209 (setq type (downcase (car (vm-mm-layout-type layout)))
|
|
1210 type-no-subtype (car (vm-parse type "\\([^/]+\\)")))
|
|
1211 (cond ((and (vm-mime-should-display-button layout dont-honor-c-d)
|
|
1212 (or (condition-case nil
|
|
1213 (funcall (intern
|
|
1214 (concat "vm-mime-display-button-"
|
|
1215 type))
|
|
1216 layout)
|
|
1217 (void-function nil))
|
|
1218 (condition-case nil
|
|
1219 (funcall (intern
|
|
1220 (concat "vm-mime-display-button-"
|
|
1221 type-no-subtype))
|
|
1222 layout)
|
|
1223 (void-function nil)))))
|
|
1224 ((and (vm-mime-should-display-internal layout dont-honor-c-d)
|
|
1225 (condition-case nil
|
|
1226 (funcall (intern
|
|
1227 (concat "vm-mime-display-internal-"
|
|
1228 type))
|
|
1229 layout)
|
|
1230 (void-function nil))))
|
|
1231 ((vm-mime-types-match "multipart" type)
|
|
1232 (or (condition-case nil
|
|
1233 (funcall (intern
|
|
1234 (concat "vm-mime-display-internal-"
|
|
1235 type))
|
|
1236 layout)
|
|
1237 (void-function nil))
|
|
1238 (vm-mime-display-internal-multipart/mixed layout)))
|
|
1239 ((and (vm-mime-should-display-external type)
|
|
1240 (vm-mime-display-external-generic layout))
|
|
1241 (and extent (vm-set-extent-property
|
|
1242 extent 'vm-mime-disposable nil)))
|
|
1243 ((vm-mime-can-convert type)
|
|
1244 (vm-decode-mime-layout
|
|
1245 (vm-mime-convert-undisplayable-layout layout)))
|
|
1246 ((and (or (vm-mime-types-match "message" type)
|
|
1247 (vm-mime-types-match "text" type))
|
|
1248 ;; display unmatched message and text types as
|
|
1249 ;; text/plain.
|
|
1250 (vm-mime-display-internal-text/plain layout)))
|
30
|
1251 (t (and extent (vm-mime-rewrite-failed-button
|
131
|
1252 extent
|
|
1253 (or (vm-mm-layout-cache layout)
|
|
1254 "no external viewer defined for type")))
|
30
|
1255 (vm-mime-display-internal-application/octet-stream
|
20
|
1256 (or extent layout))))
|
|
1257 (and extent (vm-mime-delete-button-maybe extent)))
|
|
1258 (set-buffer-modified-p modified)))
|
|
1259 t )
|
|
1260
|
|
1261 (defun vm-mime-display-button-text (layout)
|
|
1262 (vm-mime-display-button-xxxx layout t))
|
|
1263
|
120
|
1264 (defun vm-mime-display-internal-text/html (layout)
|
131
|
1265 (if (fboundp 'w3-region)
|
|
1266 (let ((buffer-read-only nil)
|
|
1267 (work-buffer nil))
|
|
1268 (message "Inlining text/html, be patient...")
|
|
1269 ;; w3-region is not as tame as we would like.
|
|
1270 ;; make sure the yoke is firmly attached.
|
|
1271 (unwind-protect
|
|
1272 (progn
|
|
1273 (save-excursion
|
|
1274 (set-buffer (setq work-buffer
|
|
1275 (generate-new-buffer " *workbuf*")))
|
|
1276 (vm-mime-insert-mime-body layout)
|
|
1277 (vm-mime-transfer-decode-region layout (point-min) (point-max))
|
|
1278 (save-excursion
|
|
1279 (save-window-excursion
|
|
1280 (w3-region (point-min) (point-max)))))
|
|
1281 (insert-buffer-substring work-buffer))
|
|
1282 (and work-buffer (kill-buffer work-buffer)))
|
|
1283 (message "Inlining text/html... done")
|
|
1284 t )
|
|
1285 (vm-set-mm-layout-cache layout "Need W3 to inline HTML")
|
|
1286 nil ))
|
20
|
1287
|
114
|
1288 (defun vm-mime-display-internal-text/plain (layout &optional no-highlighting)
|
24
|
1289 (let ((start (point)) end old-size
|
20
|
1290 (buffer-read-only nil)
|
|
1291 (charset (or (vm-mime-get-parameter layout "charset") "us-ascii")))
|
|
1292 (if (not (vm-mime-charset-internally-displayable-p charset))
|
30
|
1293 (progn
|
|
1294 (vm-set-mm-layout-cache
|
|
1295 layout (concat "Undisplayable charset: " charset))
|
|
1296 nil)
|
20
|
1297 (vm-mime-insert-mime-body layout)
|
|
1298 (setq end (point-marker))
|
|
1299 (vm-mime-transfer-decode-region layout start end)
|
24
|
1300 (setq old-size (buffer-size))
|
20
|
1301 (vm-mime-charset-decode-region charset start end)
|
24
|
1302 (set-marker end (+ end (- (buffer-size) old-size)))
|
114
|
1303 (or no-highlighting (vm-energize-urls-in-message-region start end))
|
24
|
1304 (goto-char end)
|
20
|
1305 t )))
|
|
1306
|
|
1307 (defun vm-mime-display-internal-text/enriched (layout)
|
|
1308 (require 'enriched)
|
|
1309 (let ((start (point)) end
|
|
1310 (buffer-read-only nil)
|
|
1311 (enriched-verbose t))
|
26
|
1312 (message "Decoding text/enriched, be patient...")
|
20
|
1313 (vm-mime-insert-mime-body layout)
|
|
1314 (setq end (point-marker))
|
|
1315 (vm-mime-transfer-decode-region layout start end)
|
|
1316 ;; enriched-decode expects a couple of headers at the top of
|
|
1317 ;; the region and will remove anything that looks like a
|
|
1318 ;; header. Put a header section here for it to eat so it
|
|
1319 ;; won't eat message text instead.
|
|
1320 (goto-char start)
|
|
1321 (insert "Comment: You should not see this header\n\n")
|
|
1322 (enriched-decode start end)
|
|
1323 (vm-energize-urls-in-message-region start end)
|
|
1324 (goto-char end)
|
26
|
1325 (message "Decoding text/enriched... done")
|
20
|
1326 t ))
|
|
1327
|
|
1328 (defun vm-mime-display-external-generic (layout)
|
|
1329 (let ((program-list (vm-mime-find-external-viewer
|
|
1330 (car (vm-mm-layout-type layout))))
|
|
1331 (process (nth 0 (vm-mm-layout-cache layout)))
|
|
1332 (tempfile (nth 1 (vm-mm-layout-cache layout)))
|
|
1333 (buffer-read-only nil)
|
|
1334 (start (point))
|
|
1335 end)
|
|
1336 (if (and (processp process) (eq (process-status process) 'run))
|
30
|
1337 t
|
20
|
1338 (cond ((or (null tempfile) (null (file-exists-p tempfile)))
|
|
1339 (vm-mime-insert-mime-body layout)
|
|
1340 (setq end (point-marker))
|
|
1341 (vm-mime-transfer-decode-region layout start end)
|
|
1342 (setq tempfile (vm-make-tempfile-name))
|
24
|
1343 (let ((buffer-file-type buffer-file-type)
|
136
|
1344 file-coding-system)
|
24
|
1345 ;; Tell DOS/Windows NT whether the file is binary
|
|
1346 (setq buffer-file-type (not (vm-mime-text-type-p layout)))
|
|
1347 ;; Tell XEmacs/MULE not to mess with the bits unless
|
|
1348 ;; this is a text type.
|
120
|
1349 (if vm-xemacs-mule-p
|
24
|
1350 (if (vm-mime-text-type-p layout)
|
136
|
1351 (set-file-coding-system 'no-conversion nil)
|
|
1352 (set-file-coding-system 'binary t)))
|
24
|
1353 (write-region start end tempfile nil 0))
|
20
|
1354 (delete-region start end)
|
|
1355 (save-excursion
|
|
1356 (vm-select-folder-buffer)
|
|
1357 (setq vm-folder-garbage-alist
|
|
1358 (cons (cons tempfile 'delete-file)
|
|
1359 vm-folder-garbage-alist)))))
|
30
|
1360 (message "Launching %s..." (mapconcat 'identity program-list " "))
|
20
|
1361 (setq process
|
|
1362 (apply 'start-process
|
|
1363 (format "view %25s" (vm-mime-layout-description layout))
|
|
1364 nil (append program-list (list tempfile))))
|
|
1365 (process-kill-without-query process t)
|
26
|
1366 (message "Launching %s... done" (mapconcat 'identity
|
20
|
1367 program-list
|
|
1368 " "))
|
|
1369 (save-excursion
|
|
1370 (vm-select-folder-buffer)
|
|
1371 (setq vm-message-garbage-alist
|
|
1372 (cons (cons process 'delete-process)
|
|
1373 vm-message-garbage-alist)))
|
|
1374 (vm-set-mm-layout-cache layout (list process tempfile))))
|
|
1375 t )
|
|
1376
|
|
1377 (defun vm-mime-display-internal-application/octet-stream (layout)
|
|
1378 (if (vectorp layout)
|
|
1379 (let ((buffer-read-only nil)
|
|
1380 (description (vm-mm-layout-description layout)))
|
|
1381 (vm-mime-insert-button
|
24
|
1382 (format "%-35.35s [%s to save to a file]"
|
20
|
1383 (vm-mime-layout-description layout)
|
|
1384 (if (vm-mouse-support-possible-p)
|
|
1385 "Click mouse-2"
|
|
1386 "Press RETURN"))
|
|
1387 (function
|
|
1388 (lambda (layout)
|
|
1389 (save-excursion
|
|
1390 (vm-mime-display-internal-application/octet-stream layout))))
|
|
1391 layout nil))
|
|
1392 (goto-char (vm-extent-start-position layout))
|
|
1393 (setq layout (vm-extent-property layout 'vm-mime-layout))
|
|
1394 ;; support old "name" paramater for application/octet-stream
|
|
1395 ;; but don't override the "filename" parameter extracted from
|
|
1396 ;; Content-Disposition, if any.
|
|
1397 (let ((default-filename
|
|
1398 (if (vm-mime-get-disposition-parameter layout "filename")
|
|
1399 nil
|
|
1400 (vm-mime-get-parameter layout "name"))))
|
|
1401 (vm-mime-send-body-to-file layout default-filename)))
|
|
1402 t )
|
126
|
1403 (fset 'vm-mime-display-button-application/octet-stream
|
20
|
1404 'vm-mime-display-internal-application/octet-stream)
|
|
1405
|
126
|
1406 (defun vm-mime-display-button-application (layout)
|
|
1407 (vm-mime-display-button-xxxx layout nil))
|
|
1408
|
20
|
1409 (defun vm-mime-display-button-image (layout)
|
|
1410 (vm-mime-display-button-xxxx layout t))
|
|
1411
|
|
1412 (defun vm-mime-display-button-audio (layout)
|
|
1413 (vm-mime-display-button-xxxx layout nil))
|
|
1414
|
|
1415 (defun vm-mime-display-button-video (layout)
|
|
1416 (vm-mime-display-button-xxxx layout t))
|
|
1417
|
|
1418 (defun vm-mime-display-button-message (layout)
|
|
1419 (vm-mime-display-button-xxxx layout t))
|
|
1420
|
|
1421 (defun vm-mime-display-button-multipart (layout)
|
|
1422 (vm-mime-display-button-xxxx layout t))
|
|
1423
|
|
1424 (defun vm-mime-display-internal-multipart/mixed (layout)
|
|
1425 (let ((part-list (vm-mm-layout-parts layout)))
|
|
1426 (while part-list
|
|
1427 (vm-decode-mime-layout (car part-list))
|
|
1428 (setq part-list (cdr part-list)))
|
|
1429 t ))
|
|
1430
|
|
1431 (defun vm-mime-display-internal-multipart/alternative (layout)
|
|
1432 (let (best-layout)
|
|
1433 (cond ((eq vm-mime-alternative-select-method 'best)
|
|
1434 (let ((done nil)
|
|
1435 (best nil)
|
|
1436 part-list type)
|
|
1437 (setq part-list (vm-mm-layout-parts layout)
|
|
1438 part-list (nreverse (copy-sequence part-list)))
|
|
1439 (while (and part-list (not done))
|
|
1440 (setq type (car (vm-mm-layout-type (car part-list))))
|
|
1441 (if (or (vm-mime-can-display-internal (car part-list))
|
|
1442 (vm-mime-find-external-viewer type))
|
|
1443 (setq best (car part-list)
|
|
1444 done t)
|
|
1445 (setq part-list (cdr part-list))))
|
|
1446 (setq best-layout (or best (car (vm-mm-layout-parts layout))))))
|
|
1447 ((eq vm-mime-alternative-select-method 'best-internal)
|
|
1448 (let ((done nil)
|
|
1449 (best nil)
|
|
1450 (second-best nil)
|
|
1451 part-list type)
|
|
1452 (setq part-list (vm-mm-layout-parts layout)
|
|
1453 part-list (nreverse (copy-sequence part-list)))
|
|
1454 (while (and part-list (not done))
|
|
1455 (setq type (car (vm-mm-layout-type (car part-list))))
|
|
1456 (cond ((vm-mime-can-display-internal (car part-list))
|
|
1457 (setq best (car part-list)
|
|
1458 done t))
|
|
1459 ((and (null second-best)
|
|
1460 (vm-mime-find-external-viewer type))
|
|
1461 (setq second-best (car part-list))))
|
|
1462 (setq part-list (cdr part-list)))
|
|
1463 (setq best-layout (or best second-best
|
|
1464 (car (vm-mm-layout-parts layout)))))))
|
|
1465 (vm-decode-mime-layout best-layout)))
|
|
1466
|
|
1467 (defun vm-mime-display-button-multipart/parallel (layout)
|
|
1468 (vm-mime-insert-button
|
24
|
1469 (format "%-35.35s [%s to display in parallel]"
|
20
|
1470 (vm-mime-layout-description layout)
|
|
1471 (if (vm-mouse-support-possible-p)
|
|
1472 "Click mouse-2"
|
|
1473 "Press RETURN"))
|
|
1474 (function
|
|
1475 (lambda (layout)
|
|
1476 (save-excursion
|
|
1477 (let ((vm-auto-displayed-mime-content-types t))
|
|
1478 (vm-decode-mime-layout layout t)))))
|
|
1479 layout t))
|
|
1480
|
|
1481 (fset 'vm-mime-display-internal-multipart/parallel
|
|
1482 'vm-mime-display-internal-multipart/mixed)
|
|
1483
|
|
1484 (defun vm-mime-display-internal-multipart/digest (layout)
|
|
1485 (if (vectorp layout)
|
|
1486 (let ((buffer-read-only nil))
|
|
1487 (vm-mime-insert-button
|
24
|
1488 (format "%-35.35s [%s to display]"
|
20
|
1489 (vm-mime-layout-description layout)
|
|
1490 (if (vm-mouse-support-possible-p)
|
|
1491 "Click mouse-2"
|
|
1492 "Press RETURN"))
|
|
1493 (function
|
|
1494 (lambda (layout)
|
|
1495 (save-excursion
|
|
1496 (vm-mime-display-internal-multipart/digest layout))))
|
|
1497 layout nil))
|
|
1498 (goto-char (vm-extent-start-position layout))
|
|
1499 (setq layout (vm-extent-property layout 'vm-mime-layout))
|
|
1500 (set-buffer (generate-new-buffer (format "digest from %s/%s"
|
|
1501 (buffer-name vm-mail-buffer)
|
|
1502 (vm-number-of
|
|
1503 (car vm-message-pointer)))))
|
|
1504 (setq vm-folder-type vm-default-folder-type)
|
|
1505 (vm-mime-burst-layout layout nil)
|
|
1506 (vm-save-buffer-excursion
|
|
1507 (vm-goto-new-folder-frame-maybe 'folder)
|
|
1508 (vm-mode))
|
|
1509 ;; temp buffer, don't offer to save it.
|
|
1510 (setq buffer-offer-save nil)
|
114
|
1511 (vm-display (or vm-presentation-buffer (current-buffer)) t
|
|
1512 (list this-command) '(vm-mode startup)))
|
20
|
1513 t )
|
|
1514 (fset 'vm-mime-display-button-multipart/digest
|
|
1515 'vm-mime-display-internal-multipart/digest)
|
|
1516
|
118
|
1517 (defun vm-mime-display-button-message/rfc822 (layout)
|
|
1518 (let ((buffer-read-only nil))
|
|
1519 (vm-mime-insert-button
|
|
1520 (format "%-35.35s [%s to display]"
|
|
1521 (vm-mime-layout-description layout)
|
|
1522 (if (vm-mouse-support-possible-p)
|
|
1523 "Click mouse-2"
|
|
1524 "Press RETURN"))
|
|
1525 (function
|
|
1526 (lambda (layout)
|
|
1527 (save-excursion
|
|
1528 (vm-mime-display-internal-message/rfc822 layout))))
|
|
1529 layout nil)))
|
|
1530 (fset 'vm-mime-display-button-message/news
|
|
1531 'vm-mime-display-button-message/rfc822)
|
|
1532
|
20
|
1533 (defun vm-mime-display-internal-message/rfc822 (layout)
|
|
1534 (if (vectorp layout)
|
118
|
1535 (vm-mime-display-internal-text/plain layout)
|
20
|
1536 (goto-char (vm-extent-start-position layout))
|
|
1537 (setq layout (vm-extent-property layout 'vm-mime-layout))
|
|
1538 (set-buffer (generate-new-buffer
|
|
1539 (format "message from %s/%s"
|
|
1540 (buffer-name vm-mail-buffer)
|
|
1541 (vm-number-of
|
|
1542 (car vm-message-pointer)))))
|
|
1543 (setq vm-folder-type vm-default-folder-type)
|
|
1544 (vm-mime-burst-layout layout nil)
|
|
1545 (set-buffer-modified-p nil)
|
|
1546 (vm-save-buffer-excursion
|
|
1547 (vm-goto-new-folder-frame-maybe 'folder)
|
|
1548 (vm-mode))
|
|
1549 ;; temp buffer, don't offer to save it.
|
|
1550 (setq buffer-offer-save nil)
|
|
1551 (vm-display (or vm-presentation-buffer (current-buffer)) t
|
|
1552 (list this-command) '(vm-mode startup)))
|
|
1553 t )
|
30
|
1554 (fset 'vm-mime-display-internal-message/news
|
|
1555 'vm-mime-display-internal-message/rfc822)
|
20
|
1556
|
|
1557 (defun vm-mime-display-internal-message/partial (layout)
|
|
1558 (if (vectorp layout)
|
|
1559 (let ((buffer-read-only nil)
|
|
1560 (number (vm-mime-get-parameter layout "number"))
|
|
1561 (total (vm-mime-get-parameter layout "total")))
|
|
1562 (vm-mime-insert-button
|
24
|
1563 (format "%-35.35s [%s to attempt assembly]"
|
20
|
1564 (concat (vm-mime-layout-description layout)
|
|
1565 (and number (concat ", part " number))
|
|
1566 (and number total (concat " of " total)))
|
|
1567 (if (vm-mouse-support-possible-p)
|
|
1568 "Click mouse-2"
|
|
1569 "Press RETURN"))
|
|
1570 (function
|
|
1571 (lambda (layout)
|
|
1572 (save-excursion
|
|
1573 (vm-mime-display-internal-message/partial layout))))
|
|
1574 layout nil))
|
26
|
1575 (message "Assembling message...")
|
20
|
1576 (let ((parts nil)
|
|
1577 (missing nil)
|
|
1578 (work-buffer nil)
|
|
1579 extent id o number total m i prev part-header-pos
|
|
1580 p-id p-number p-total p-list)
|
|
1581 (setq extent layout
|
|
1582 layout (vm-extent-property extent 'vm-mime-layout)
|
|
1583 id (vm-mime-get-parameter layout "id"))
|
|
1584 (if (null id)
|
|
1585 (vm-mime-error
|
|
1586 "message/partial message missing id parameter"))
|
|
1587 (save-excursion
|
|
1588 (set-buffer (marker-buffer (vm-mm-layout-body-start layout)))
|
|
1589 (save-excursion
|
|
1590 (save-restriction
|
|
1591 (widen)
|
|
1592 (goto-char (point-min))
|
|
1593 (while (and (search-forward id nil t)
|
|
1594 (setq m (vm-message-at-point)))
|
|
1595 (setq o (vm-mm-layout m))
|
|
1596 (if (not (vectorp o))
|
|
1597 nil
|
|
1598 (setq p-list (vm-mime-find-message/partials o id))
|
|
1599 (while p-list
|
|
1600 (setq p-id (vm-mime-get-parameter (car p-list) "id"))
|
|
1601 (setq p-total (vm-mime-get-parameter (car p-list) "total"))
|
|
1602 (if (null p-total)
|
|
1603 nil
|
|
1604 (setq p-total (string-to-int p-total))
|
|
1605 (if (< p-total 1)
|
|
1606 (vm-mime-error "message/partial specified part total < 0, %d" p-total))
|
|
1607 (if total
|
|
1608 (if (not (= total p-total))
|
|
1609 (vm-mime-error "message/partial speificed total differs between parts, (%d != %d)" p-total total))
|
|
1610 (setq total p-total)))
|
|
1611 (setq p-number (vm-mime-get-parameter (car p-list) "number"))
|
|
1612 (if (null p-number)
|
|
1613 (vm-mime-error
|
|
1614 "message/partial message missing number parameter"))
|
|
1615 (setq p-number (string-to-int p-number))
|
|
1616 (if (< p-number 1)
|
|
1617 (vm-mime-error "message/partial part number < 0, %d"
|
|
1618 p-number))
|
|
1619 (if (and total (> p-number total))
|
|
1620 (vm-mime-error "message/partial part number greater than expected number of parts, (%d > %d)" p-number total))
|
|
1621 (setq parts (cons (list p-number (car p-list)) parts)
|
|
1622 p-list (cdr p-list))))
|
|
1623 (goto-char (vm-mm-layout-body-end o))))))
|
|
1624 (if (null total)
|
|
1625 (vm-mime-error "total number of parts not specified in any message/partial part"))
|
|
1626 (setq parts (sort parts
|
|
1627 (function
|
|
1628 (lambda (p q)
|
|
1629 (< (car p)
|
|
1630 (car q))))))
|
|
1631 (setq i 0
|
|
1632 p-list parts)
|
|
1633 (while p-list
|
|
1634 (cond ((< i (car (car p-list)))
|
|
1635 (vm-increment i)
|
|
1636 (cond ((not (= i (car (car p-list))))
|
|
1637 (setq missing (cons i missing)))
|
|
1638 (t (setq prev p-list
|
|
1639 p-list (cdr p-list)))))
|
|
1640 (t
|
|
1641 ;; remove duplicate part
|
|
1642 (setcdr prev (cdr p-list))
|
|
1643 (setq p-list (cdr p-list)))))
|
|
1644 (while (< i total)
|
|
1645 (vm-increment i)
|
|
1646 (setq missing (cons i missing)))
|
|
1647 (if missing
|
|
1648 (vm-mime-error "part%s %s%s missing"
|
|
1649 (if (cdr missing) "s" "")
|
|
1650 (mapconcat
|
|
1651 (function identity)
|
|
1652 (nreverse (mapcar 'int-to-string
|
|
1653 (or (cdr missing) missing)))
|
|
1654 ", ")
|
|
1655 (if (cdr missing)
|
|
1656 (concat " and " (car missing))
|
|
1657 "")))
|
|
1658 (set-buffer (generate-new-buffer "assembled message"))
|
|
1659 (setq vm-folder-type vm-default-folder-type)
|
|
1660 (vm-mime-insert-mime-headers (car (cdr (car parts))))
|
|
1661 (goto-char (point-min))
|
|
1662 (vm-reorder-message-headers
|
|
1663 nil nil
|
|
1664 "\\(Encrypted\\|Content-\\|MIME-Version\\|Message-ID\\|Subject\\|X-VM-\\|Status\\)")
|
|
1665 (goto-char (point-max))
|
|
1666 (setq part-header-pos (point))
|
|
1667 (while parts
|
|
1668 (vm-mime-insert-mime-body (car (cdr (car parts))))
|
|
1669 (setq parts (cdr parts)))
|
|
1670 (goto-char part-header-pos)
|
|
1671 (vm-reorder-message-headers
|
|
1672 nil '("Subject" "MIME-Version" "Content-" "Message-ID" "Encrypted") nil)
|
|
1673 (vm-munge-message-separators vm-folder-type (point-min) (point-max))
|
|
1674 (goto-char (point-min))
|
|
1675 (insert (vm-leading-message-separator))
|
|
1676 (goto-char (point-max))
|
|
1677 (insert (vm-trailing-message-separator))
|
|
1678 (set-buffer-modified-p nil)
|
26
|
1679 (message "Assembling message... done")
|
20
|
1680 (vm-save-buffer-excursion
|
|
1681 (vm-goto-new-folder-frame-maybe 'folder)
|
|
1682 (vm-mode))
|
|
1683 ;; temp buffer, don't offer to save it.
|
|
1684 (setq buffer-offer-save nil)
|
|
1685 (vm-display (or vm-presentation-buffer (current-buffer)) t
|
|
1686 (list this-command) '(vm-mode startup)))
|
|
1687 t ))
|
|
1688 (fset 'vm-mime-display-button-message/partial
|
|
1689 'vm-mime-display-internal-message/partial)
|
|
1690
|
|
1691 (defun vm-mime-display-internal-image-xxxx (layout feature name)
|
120
|
1692 (if (and vm-xemacs-p
|
20
|
1693 (featurep feature)
|
|
1694 (eq (device-type) 'x))
|
|
1695 (let ((start (point)) end tempfile g e
|
|
1696 (buffer-read-only nil))
|
|
1697 (if (vm-mm-layout-cache layout)
|
|
1698 (setq g (vm-mm-layout-cache layout))
|
|
1699 (vm-mime-insert-mime-body layout)
|
|
1700 (setq end (point-marker))
|
|
1701 (vm-mime-transfer-decode-region layout start end)
|
|
1702 (setq tempfile (vm-make-tempfile-name))
|
24
|
1703 ;; coding system for presentation buffer is binary
|
20
|
1704 (write-region start end tempfile nil 0)
|
26
|
1705 (message "Creating %s glyph..." name)
|
20
|
1706 (setq g (make-glyph
|
|
1707 (list (vector feature ':file tempfile)
|
|
1708 (vector 'string
|
|
1709 ':data
|
|
1710 (format "[Unknown %s image encoding]\n"
|
|
1711 name)))))
|
26
|
1712 (message "")
|
20
|
1713 (vm-set-mm-layout-cache layout g)
|
|
1714 (save-excursion
|
|
1715 (vm-select-folder-buffer)
|
|
1716 (setq vm-folder-garbage-alist
|
|
1717 (cons (cons tempfile 'delete-file)
|
|
1718 vm-folder-garbage-alist)))
|
|
1719 (delete-region start end))
|
|
1720 (if (not (bolp))
|
|
1721 (insert-char ?\n 2)
|
|
1722 (insert-char ?\n 1))
|
|
1723 (setq e (vm-make-extent (1- (point)) (point)))
|
|
1724 (vm-set-extent-property e 'begin-glyph g)
|
|
1725 t )))
|
|
1726
|
|
1727 (defun vm-mime-display-internal-image/gif (layout)
|
|
1728 (vm-mime-display-internal-image-xxxx layout 'gif "GIF"))
|
|
1729
|
|
1730 (defun vm-mime-display-internal-image/jpeg (layout)
|
|
1731 (vm-mime-display-internal-image-xxxx layout 'jpeg "JPEG"))
|
|
1732
|
|
1733 (defun vm-mime-display-internal-image/png (layout)
|
|
1734 (vm-mime-display-internal-image-xxxx layout 'png "PNG"))
|
|
1735
|
|
1736 (defun vm-mime-display-internal-image/tiff (layout)
|
|
1737 (vm-mime-display-internal-image-xxxx layout 'tiff "TIFF"))
|
|
1738
|
|
1739 (defun vm-mime-display-internal-audio/basic (layout)
|
120
|
1740 (if (and vm-xemacs-p
|
20
|
1741 (or (featurep 'native-sound)
|
|
1742 (featurep 'nas-sound))
|
|
1743 (or (device-sound-enabled-p)
|
|
1744 (and (featurep 'native-sound)
|
|
1745 (not native-sound-only-on-console)
|
|
1746 (eq (device-type) 'x))))
|
|
1747 (let ((start (point)) end tempfile
|
|
1748 (buffer-read-only nil))
|
|
1749 (if (vm-mm-layout-cache layout)
|
|
1750 (setq tempfile (vm-mm-layout-cache layout))
|
|
1751 (vm-mime-insert-mime-body layout)
|
|
1752 (setq end (point-marker))
|
|
1753 (vm-mime-transfer-decode-region layout start end)
|
|
1754 (setq tempfile (vm-make-tempfile-name))
|
24
|
1755 ;; coding system for presentation buffer is binary
|
20
|
1756 (write-region start end tempfile nil 0)
|
|
1757 (vm-set-mm-layout-cache layout tempfile)
|
|
1758 (save-excursion
|
|
1759 (vm-select-folder-buffer)
|
|
1760 (setq vm-folder-garbage-alist
|
|
1761 (cons (cons tempfile 'delete-file)
|
|
1762 vm-folder-garbage-alist)))
|
|
1763 (delete-region start end))
|
|
1764 (start-itimer "audioplayer"
|
|
1765 (list 'lambda nil (list 'play-sound-file tempfile))
|
|
1766 1)
|
|
1767 t )
|
|
1768 nil ))
|
|
1769
|
|
1770 (defun vm-mime-display-button-xxxx (layout disposable)
|
|
1771 (let ((description (vm-mime-layout-description layout)))
|
|
1772 (vm-mime-insert-button
|
131
|
1773 (format "%-35.35s [%s to attempt display]"
|
20
|
1774 description
|
|
1775 (if (vm-mouse-support-possible-p) "Click mouse-2" "Press RETURN"))
|
|
1776 (function
|
|
1777 (lambda (layout)
|
|
1778 (save-excursion
|
|
1779 (let ((vm-auto-displayed-mime-content-types t))
|
|
1780 (vm-decode-mime-layout layout t)))))
|
|
1781 layout disposable)
|
|
1782 t ))
|
|
1783
|
30
|
1784 (defun vm-mime-run-display-function-at-point (&optional function dispose)
|
20
|
1785 (interactive)
|
|
1786 ;; save excursion to keep point from moving. its motion would
|
|
1787 ;; drag window point along, to a place arbitrarily far from
|
|
1788 ;; where it was when the user triggered the button.
|
|
1789 (save-excursion
|
120
|
1790 (cond (vm-fsfemacs-19-p
|
20
|
1791 (let (o-list o (found nil))
|
|
1792 (setq o-list (overlays-at (point)))
|
|
1793 (while (and o-list (not found))
|
|
1794 (cond ((overlay-get (car o-list) 'vm-mime-layout)
|
|
1795 (setq found t)
|
|
1796 (funcall (or function (overlay-get (car o-list)
|
|
1797 'vm-mime-function))
|
|
1798 (car o-list))))
|
|
1799 (setq o-list (cdr o-list)))))
|
120
|
1800 (vm-xemacs-p
|
20
|
1801 (let ((e (extent-at (point) nil 'vm-mime-layout)))
|
|
1802 (funcall (or function (extent-property e 'vm-mime-function))
|
|
1803 e))))))
|
|
1804
|
|
1805 ;; for the karking compiler
|
|
1806 (defvar vm-menu-mime-dispose-menu)
|
|
1807
|
120
|
1808 (defun vm-mime-set-extent-glyph-for-type (e type)
|
126
|
1809 (if (and vm-xemacs-p
|
|
1810 (featurep 'xpm)
|
|
1811 (eq (device-type) 'x)
|
|
1812 (> (device-bitplanes) 7))
|
120
|
1813 (let ((dir vm-image-directory)
|
118
|
1814 (colorful (> (device-bitplanes) 15))
|
|
1815 (tuples
|
|
1816 '(("text" "document-simple.xpm" "document-colorful.xpm")
|
126
|
1817 ("image" "mona_stamp-simple.xpm" "mona_stamp-colorful.xpm")
|
118
|
1818 ("audio" "audio_stamp-simple.xpm" "audio_stamp-colorful.xpm")
|
|
1819 ("video" "film-simple.xpm" "film-colorful.xpm")
|
|
1820 ("message" "message-simple.xpm" "message-colorful.xpm")
|
|
1821 ("application" "gear-simple.xpm" "gear-colorful.xpm")
|
|
1822 ("multipart" "stuffed_box-simple.xpm"
|
|
1823 "stuffed_box-colorful.xpm")))
|
|
1824 glyph file sym p)
|
|
1825 (setq file (catch 'done
|
|
1826 (while tuples
|
|
1827 (if (vm-mime-types-match (car (car tuples)) type)
|
|
1828 (throw 'done (car tuples))
|
|
1829 (setq tuples (cdr tuples))))
|
|
1830 nil)
|
120
|
1831 file (and file (if colorful (nth 2 file) (nth 1 file)))
|
118
|
1832 sym (and file (intern file vm-image-obarray))
|
|
1833 glyph (and sym (boundp sym) (symbol-value sym))
|
136
|
1834 glyph (or glyph
|
|
1835 (and file
|
|
1836 (make-glyph
|
|
1837 (vector 'autodetect
|
|
1838 ':data (expand-file-name file dir))))))
|
118
|
1839 (and sym (not (boundp sym)) (set sym glyph))
|
24
|
1840 (and glyph (set-extent-begin-glyph e glyph)))))
|
|
1841
|
20
|
1842 (defun vm-mime-insert-button (caption action layout disposable)
|
|
1843 (let ((start (point)) e
|
|
1844 (keymap (make-sparse-keymap))
|
|
1845 (buffer-read-only nil))
|
|
1846 (if (fboundp 'set-keymap-parents)
|
30
|
1847 (if (current-local-map)
|
|
1848 (set-keymap-parents keymap (list (current-local-map))))
|
20
|
1849 (setq keymap (nconc keymap (current-local-map))))
|
|
1850 (define-key keymap "\r" 'vm-mime-run-display-function-at-point)
|
|
1851 (if (and (vm-mouse-xemacs-mouse-p) vm-popup-menu-on-mouse-3)
|
|
1852 (define-key keymap 'button3 'vm-menu-popup-mime-dispose-menu))
|
|
1853 (if (not (bolp))
|
|
1854 (insert "\n"))
|
|
1855 (insert caption "\n")
|
120
|
1856 ;; we must use the same interface that the vm-extent functions
|
|
1857 ;; use. if they use overlays, then we call make-overlay.
|
|
1858 (if (eq (symbol-function 'vm-make-extent) 'make-overlay)
|
|
1859 ;; we MUST have the five arg make-overlay. overlays must
|
|
1860 ;; advance when text is inserted at their start position or
|
|
1861 ;; inline text and graphics will seep into the button
|
|
1862 ;; overlay and then be removed when the button is removed.
|
20
|
1863 (setq e (make-overlay start (point) nil t nil))
|
|
1864 (setq e (make-extent start (point)))
|
|
1865 (set-extent-property e 'start-open t)
|
|
1866 (set-extent-property e 'end-open t))
|
120
|
1867 (vm-mime-set-extent-glyph-for-type e (car (vm-mm-layout-type layout)))
|
20
|
1868 ;; for emacs
|
|
1869 (vm-set-extent-property e 'mouse-face 'highlight)
|
|
1870 (vm-set-extent-property e 'local-map keymap)
|
|
1871 ;; for xemacs
|
|
1872 (vm-set-extent-property e 'highlight t)
|
|
1873 (vm-set-extent-property e 'keymap keymap)
|
|
1874 (vm-set-extent-property e 'balloon-help 'vm-mouse-3-help)
|
|
1875 ;; for all
|
|
1876 (vm-set-extent-property e 'vm-mime-disposable disposable)
|
|
1877 (vm-set-extent-property e 'face vm-mime-button-face)
|
|
1878 (vm-set-extent-property e 'vm-mime-layout layout)
|
|
1879 (vm-set-extent-property e 'vm-mime-function action)))
|
|
1880
|
30
|
1881 (defun vm-mime-rewrite-failed-button (button error-string)
|
|
1882 (let* ((buffer-read-only nil)
|
|
1883 (start (point)))
|
|
1884 (goto-char (vm-extent-start-position button))
|
131
|
1885 (insert (format "DISPLAY FAILED -- %s\n" error-string))
|
30
|
1886 (vm-set-extent-endpoints button start (vm-extent-end-position button))
|
|
1887 (delete-region (point) (vm-extent-end-position button))))
|
|
1888
|
20
|
1889 (defun vm-mime-send-body-to-file (layout &optional default-filename)
|
|
1890 (if (not (vectorp layout))
|
|
1891 (setq layout (vm-extent-property layout 'vm-mime-layout)))
|
|
1892 (or default-filename
|
|
1893 (setq default-filename
|
|
1894 (vm-mime-get-disposition-parameter layout "filename")))
|
|
1895 (and default-filename
|
|
1896 (setq default-filename (file-name-nondirectory default-filename)))
|
|
1897 (let ((work-buffer nil)
|
24
|
1898 ;; evade the XEmacs dialog box, yeccch.
|
|
1899 (use-dialog-box nil)
|
|
1900 (dir vm-mime-attachment-save-directory)
|
|
1901 (done nil)
|
20
|
1902 file)
|
24
|
1903 (while (not done)
|
|
1904 (setq file
|
|
1905 (read-file-name
|
|
1906 (if default-filename
|
|
1907 (format "Write MIME body to file (default %s): "
|
|
1908 default-filename)
|
|
1909 "Write MIME body to file: ")
|
|
1910 dir default-filename)
|
|
1911 file (expand-file-name file dir))
|
|
1912 (if (not (file-directory-p file))
|
|
1913 (setq done t)
|
|
1914 (if default-filename
|
|
1915 (message "%s is a directory" file)
|
|
1916 (error "%s is a directory" file))
|
|
1917 (sit-for 2)
|
|
1918 (setq dir file
|
|
1919 default-filename (if (string-match "/$" file)
|
|
1920 (concat file default-filename)
|
|
1921 (concat file "/" default-filename)))))
|
20
|
1922 (save-excursion
|
|
1923 (unwind-protect
|
|
1924 (progn
|
|
1925 (setq work-buffer (generate-new-buffer " *vm-work*"))
|
|
1926 (buffer-disable-undo work-buffer)
|
|
1927 (set-buffer work-buffer)
|
|
1928 ;; Tell DOS/Windows NT whether the file is binary
|
|
1929 (setq buffer-file-type (not (vm-mime-text-type-p layout)))
|
24
|
1930 ;; Tell XEmacs/MULE not to mess with the bits unless
|
|
1931 ;; this is a text type.
|
120
|
1932 (if vm-xemacs-mule-p
|
24
|
1933 (if (vm-mime-text-type-p layout)
|
136
|
1934 (set-file-coding-system 'no-conversion nil)
|
|
1935 (set-file-coding-system 'binary t)))
|
20
|
1936 (vm-mime-insert-mime-body layout)
|
|
1937 (vm-mime-transfer-decode-region layout (point-min) (point-max))
|
|
1938 (or (not (file-exists-p file))
|
|
1939 (y-or-n-p "File exists, overwrite? ")
|
|
1940 (error "Aborted"))
|
|
1941 (write-region (point-min) (point-max) file nil nil))
|
|
1942 (and work-buffer (kill-buffer work-buffer))))))
|
|
1943
|
30
|
1944 (defun vm-mime-pipe-body-to-command (command layout &optional discard-output)
|
20
|
1945 (if (not (vectorp layout))
|
|
1946 (setq layout (vm-extent-property layout 'vm-mime-layout)))
|
30
|
1947 (let ((output-buffer (if discard-output
|
20
|
1948 0
|
|
1949 (get-buffer-create "*Shell Command Output*")))
|
|
1950 (work-buffer nil))
|
|
1951 (save-excursion
|
|
1952 (if (bufferp output-buffer)
|
|
1953 (progn
|
|
1954 (set-buffer output-buffer)
|
|
1955 (erase-buffer)))
|
|
1956 (unwind-protect
|
|
1957 (progn
|
|
1958 (setq work-buffer (generate-new-buffer " *vm-work*"))
|
|
1959 (buffer-disable-undo work-buffer)
|
|
1960 (set-buffer work-buffer)
|
|
1961 (vm-mime-insert-mime-body layout)
|
|
1962 (vm-mime-transfer-decode-region layout (point-min) (point-max))
|
|
1963 (let ((pop-up-windows (and pop-up-windows
|
|
1964 (eq vm-mutable-windows t)))
|
|
1965 ;; Tell DOS/Windows NT whether the input is binary
|
|
1966 (binary-process-input (not (vm-mime-text-type-p layout))))
|
|
1967 (call-process-region (point-min) (point-max)
|
|
1968 (or shell-file-name "sh")
|
|
1969 nil output-buffer nil
|
30
|
1970 shell-command-switch command)))
|
20
|
1971 (and work-buffer (kill-buffer work-buffer)))
|
|
1972 (if (bufferp output-buffer)
|
|
1973 (progn
|
|
1974 (set-buffer output-buffer)
|
|
1975 (if (not (zerop (buffer-size)))
|
|
1976 (vm-display output-buffer t (list this-command)
|
|
1977 '(vm-pipe-message-to-command))
|
|
1978 (vm-display nil nil (list this-command)
|
|
1979 '(vm-pipe-message-to-command)))))))
|
|
1980 t )
|
|
1981
|
30
|
1982 (defun vm-mime-pipe-body-to-queried-command (layout &optional discard-output)
|
|
1983 (let ((command (read-string "Pipe to command: ")))
|
|
1984 (vm-mime-pipe-body-to-command command layout discard-output)))
|
|
1985
|
|
1986 (defun vm-mime-pipe-body-to-queried-command-discard-output (layout)
|
|
1987 (vm-mime-pipe-body-to-queried-command layout t))
|
|
1988
|
|
1989 (defun vm-mime-send-body-to-printer (layout)
|
|
1990 (vm-mime-pipe-body-to-command (mapconcat (function identity)
|
|
1991 (nconc (list vm-print-command)
|
|
1992 vm-print-command-switches)
|
|
1993 " ")
|
|
1994 layout))
|
|
1995
|
|
1996 (defun vm-mime-display-body-as-text (button)
|
|
1997 (let ((vm-auto-displayed-mime-content-types '("text/plain"))
|
|
1998 (layout (copy-sequence (vm-extent-property button 'vm-mime-layout))))
|
|
1999 (vm-set-extent-property button 'vm-mime-disposable t)
|
|
2000 (vm-set-extent-property button 'vm-mime-layout layout)
|
|
2001 ;; not universally correct, but close enough.
|
|
2002 (vm-set-mm-layout-type layout '("text/plain" "charset=us-ascii"))
|
|
2003 (goto-char (vm-extent-start-position button))
|
|
2004 (vm-decode-mime-layout button t)))
|
|
2005
|
|
2006 (defun vm-mime-display-body-using-external-viewer (button)
|
|
2007 (let ((layout (vm-extent-property button 'vm-mime-layout)))
|
|
2008 (goto-char (vm-extent-start-position button))
|
|
2009 (if (not (vm-mime-find-external-viewer (car (vm-mm-layout-type layout))))
|
|
2010 (error "No viewer defined for type %s"
|
|
2011 (car (vm-mm-layout-type layout)))
|
|
2012 (vm-mime-display-external-generic layout))))
|
20
|
2013
|
|
2014 (defun vm-mime-scrub-description (string)
|
|
2015 (let ((work-buffer nil))
|
|
2016 (save-excursion
|
|
2017 (unwind-protect
|
|
2018 (progn
|
|
2019 (setq work-buffer (generate-new-buffer " *vm-work*"))
|
|
2020 (buffer-disable-undo work-buffer)
|
|
2021 (set-buffer work-buffer)
|
|
2022 (insert string)
|
|
2023 (while (re-search-forward "[ \t\n]+" nil t)
|
|
2024 (replace-match " "))
|
|
2025 (buffer-string))
|
|
2026 (and work-buffer (kill-buffer work-buffer))))))
|
|
2027
|
|
2028 (defun vm-mime-layout-description (layout)
|
26
|
2029 (let ((type (car (vm-mm-layout-type layout)))
|
|
2030 description name)
|
|
2031 (setq description
|
|
2032 (if (vm-mm-layout-description layout)
|
|
2033 (vm-mime-scrub-description (vm-mm-layout-description layout))))
|
|
2034 (concat
|
|
2035 (if description description "")
|
|
2036 (if description ", " "")
|
|
2037 (cond ((vm-mime-types-match "multipart/digest" type)
|
|
2038 (let ((n (length (vm-mm-layout-parts layout))))
|
|
2039 (format "digest (%d message%s)" n (if (= n 1) "" "s"))))
|
|
2040 ((vm-mime-types-match "multipart/alternative" type)
|
|
2041 "multipart alternative")
|
|
2042 ((vm-mime-types-match "multipart" type)
|
|
2043 (let ((n (length (vm-mm-layout-parts layout))))
|
|
2044 (format "multipart message (%d part%s)" n (if (= n 1) "" "s"))))
|
|
2045 ((vm-mime-types-match "text/plain" type)
|
|
2046 (format "plain text%s"
|
|
2047 (let ((charset (vm-mime-get-parameter layout "charset")))
|
|
2048 (if charset
|
|
2049 (concat ", " charset)
|
|
2050 ""))))
|
|
2051 ((vm-mime-types-match "text/enriched" type)
|
|
2052 "enriched text")
|
|
2053 ((vm-mime-types-match "text/html" type)
|
|
2054 "HTML")
|
|
2055 ((vm-mime-types-match "image/gif" type)
|
|
2056 "GIF image")
|
|
2057 ((vm-mime-types-match "image/jpeg" type)
|
|
2058 "JPEG image")
|
|
2059 ((and (vm-mime-types-match "application/octet-stream" type)
|
|
2060 (setq name (vm-mime-get-parameter layout "name"))
|
|
2061 (save-match-data (not (string-match "^[ \t]*$" name))))
|
|
2062 name)
|
|
2063 (t type)))))
|
20
|
2064
|
|
2065 (defun vm-mime-layout-contains-type (layout type)
|
|
2066 (if (vm-mime-types-match type (car (vm-mm-layout-type layout)))
|
|
2067 layout
|
|
2068 (let ((p (vm-mm-layout-parts layout))
|
|
2069 (result nil)
|
|
2070 (done nil))
|
|
2071 (while (and p (not done))
|
|
2072 (if (setq result (vm-mime-layout-contains-type (car p) type))
|
|
2073 (setq done t)
|
|
2074 (setq p (cdr p))))
|
|
2075 result )))
|
|
2076
|
|
2077 (defun vm-mime-plain-message-p (m)
|
|
2078 (save-match-data
|
|
2079 (let ((o (vm-mm-layout m))
|
|
2080 (case-fold-search t))
|
|
2081 (and (eq (vm-mm-encoded-header m) 'none)
|
|
2082 (or (not (vectorp o))
|
|
2083 (and (vm-mime-types-match "text/plain"
|
|
2084 (car (vm-mm-layout-type o)))
|
24
|
2085 (let* ((charset (or (vm-mime-get-parameter o "charset")
|
|
2086 "us-ascii")))
|
|
2087 (vm-string-member charset vm-mime-default-face-charsets))
|
20
|
2088 (string-match "^\\(7bit\\|8bit\\|binary\\)$"
|
|
2089 (vm-mm-layout-encoding o))))))))
|
|
2090
|
|
2091 (defun vm-mime-text-type-p (layout)
|
|
2092 (or (vm-mime-types-match "text" (car (vm-mm-layout-type layout)))
|
|
2093 (vm-mime-types-match "message" (car (vm-mm-layout-type layout)))))
|
|
2094
|
|
2095 (defun vm-mime-charset-internally-displayable-p (name)
|
120
|
2096 (cond ((and vm-xemacs-mule-p (eq (device-type) 'x))
|
24
|
2097 (vm-string-assoc name vm-mime-mule-charset-to-coding-alist))
|
|
2098 ((vm-multiple-fonts-possible-p)
|
|
2099 (or (vm-string-member name vm-mime-default-face-charsets)
|
|
2100 (vm-string-assoc name vm-mime-charset-font-alist)))
|
|
2101 (t
|
|
2102 (vm-string-member name vm-mime-default-face-charsets))))
|
20
|
2103
|
|
2104 (defun vm-mime-find-message/partials (layout id)
|
|
2105 (let ((list nil)
|
|
2106 (type (vm-mm-layout-type layout)))
|
|
2107 (cond ((vm-mime-types-match "multipart" (car type))
|
|
2108 (let ((parts (vm-mm-layout-parts layout)) o)
|
|
2109 (while parts
|
|
2110 (setq o (vm-mime-find-message/partials (car parts) id))
|
|
2111 (if o
|
|
2112 (setq list (nconc o list)))
|
|
2113 (setq parts (cdr parts)))))
|
|
2114 ((vm-mime-types-match "message/partial" (car type))
|
|
2115 (if (equal (vm-mime-get-parameter layout "id") id)
|
|
2116 (setq list (cons layout list)))))
|
|
2117 list ))
|
|
2118
|
|
2119 (defun vm-message-at-point ()
|
|
2120 (let ((mp vm-message-list)
|
|
2121 (point (point))
|
|
2122 (done nil))
|
|
2123 (while (and mp (not done))
|
|
2124 (if (and (>= point (vm-start-of (car mp)))
|
|
2125 (<= point (vm-end-of (car mp))))
|
|
2126 (setq done t)
|
|
2127 (setq mp (cdr mp))))
|
|
2128 (car mp)))
|
|
2129
|
|
2130 (defun vm-mime-make-multipart-boundary ()
|
120
|
2131 (let ((boundary (make-string 10 ?a))
|
20
|
2132 (i 0))
|
|
2133 (random t)
|
|
2134 (while (< i (length boundary))
|
|
2135 (aset boundary i (aref vm-mime-base64-alphabet
|
|
2136 (% (vm-abs (lsh (random) -8))
|
|
2137 (length vm-mime-base64-alphabet))))
|
|
2138 (vm-increment i))
|
|
2139 boundary ))
|
|
2140
|
24
|
2141 (defun vm-mime-attach-file (file type &optional charset description)
|
20
|
2142 "Attach a file to a VM composition buffer to be sent along with the message.
|
|
2143 The file is not inserted into the buffer and MIME encoded until
|
|
2144 you execute vm-mail-send or vm-mail-send-and-exit. A visible tag
|
|
2145 indicating the existence of the attachment is placed in the
|
|
2146 composition buffer. You can move the attachment around or remove
|
|
2147 it entirely with normal text editing commands. If you remove the
|
|
2148 attachment tag, the attachment will not be sent.
|
|
2149
|
|
2150 First argument, FILE, is the name of the file to attach. Second
|
|
2151 argument, TYPE, is the MIME Content-Type of the file. Optional
|
|
2152 third argument CHARSET is the character set of the attached
|
24
|
2153 document. This argument is only used for text types, and it is
|
|
2154 ignored for other types. Optional fourth argument DESCRIPTION
|
|
2155 should be a one line description of the file.
|
20
|
2156
|
|
2157 When called interactively all arguments are read from the
|
|
2158 minibuffer.
|
|
2159
|
|
2160 This command is for attaching files that do not have a MIME
|
|
2161 header section at the top. For files with MIME headers, you
|
|
2162 should use vm-mime-attach-mime-file to attach such a file. VM
|
|
2163 will extract the content type information from the headers in
|
|
2164 this case and not prompt you for it in the minibuffer."
|
|
2165 (interactive
|
|
2166 ;; protect value of last-command and this-command
|
|
2167 (let ((last-command last-command)
|
|
2168 (this-command this-command)
|
|
2169 (charset nil)
|
24
|
2170 description file default-type type)
|
20
|
2171 (if (null vm-send-using-mime)
|
|
2172 (error "MIME attachments disabled, set vm-send-using-mime non-nil to enable."))
|
|
2173 (setq file (vm-read-file-name "Attach file: " nil nil t)
|
|
2174 default-type (or (vm-mime-default-type-from-filename file)
|
|
2175 "application/octet-stream")
|
|
2176 type (completing-read
|
|
2177 (format "Content type (default %s): "
|
|
2178 default-type)
|
|
2179 vm-mime-type-completion-alist)
|
|
2180 type (if (> (length type) 0) type default-type))
|
|
2181 (if (vm-mime-types-match "text" type)
|
|
2182 (setq charset (completing-read "Character set (default US-ASCII): "
|
|
2183 vm-mime-charset-completion-alist)
|
|
2184 charset (if (> (length charset) 0) charset)))
|
24
|
2185 (setq description (read-string "One line description: "))
|
|
2186 (if (string-match "^[ \t]*$" description)
|
|
2187 (setq description nil))
|
|
2188 (list file type charset description)))
|
20
|
2189 (if (null vm-send-using-mime)
|
|
2190 (error "MIME attachments disabled, set vm-send-using-mime non-nil to enable."))
|
|
2191 (if (file-directory-p file)
|
|
2192 (error "%s is a directory, cannot attach" file))
|
|
2193 (if (not (file-exists-p file))
|
|
2194 (error "No such file: %s" file))
|
|
2195 (if (not (file-readable-p file))
|
|
2196 (error "You don't have permission to read %s" file))
|
|
2197 (and charset (setq charset (list (concat "charset=" charset))))
|
24
|
2198 (and description (setq description (vm-mime-scrub-description description)))
|
|
2199 (vm-mime-attach-object file type charset description nil))
|
20
|
2200
|
|
2201 (defun vm-mime-attach-mime-file (file)
|
|
2202 "Attach a MIME encoded file to a VM composition buffer to be sent
|
|
2203 along with the message.
|
|
2204
|
|
2205 The file is not inserted into the buffer until you execute
|
|
2206 vm-mail-send or vm-mail-send-and-exit. A visible tag indicating
|
|
2207 the existence of the attachment is placed in the composition
|
|
2208 buffer. You can move the attachment around or remove it entirely
|
|
2209 with normal text editing commands. If you remove the attachment
|
|
2210 tag, the attachment will not be sent.
|
|
2211
|
|
2212 The sole argument, FILE, is the name of the file to attach.
|
|
2213 When called interactively the FILE argument is read from the
|
|
2214 minibuffer.
|
|
2215
|
|
2216 This command is for attaching files that have a MIME
|
|
2217 header section at the top. For files without MIME headers, you
|
|
2218 should use vm-mime-attach-file to attach such a file. VM
|
|
2219 will interactively query you for the file type information."
|
|
2220 (interactive
|
|
2221 ;; protect value of last-command and this-command
|
|
2222 (let ((last-command last-command)
|
|
2223 (this-command this-command)
|
|
2224 file)
|
|
2225 (if (null vm-send-using-mime)
|
|
2226 (error "MIME attachments disabled, set vm-send-using-mime non-nil to enable."))
|
|
2227 (setq file (vm-read-file-name "Attach file: " nil nil t))
|
|
2228 (list file)))
|
|
2229 (if (null vm-send-using-mime)
|
|
2230 (error "MIME attachments disabled, set vm-send-using-mime non-nil to enable."))
|
|
2231 (if (file-directory-p file)
|
|
2232 (error "%s is a directory, cannot attach" file))
|
|
2233 (if (not (file-exists-p file))
|
|
2234 (error "No such file: %s" file))
|
|
2235 (if (not (file-readable-p file))
|
|
2236 (error "You don't have permission to read %s" file))
|
24
|
2237 (vm-mime-attach-object file nil nil nil t))
|
20
|
2238
|
24
|
2239 (defun vm-mime-attach-object (object type params description mimed)
|
20
|
2240 (if (not (eq major-mode 'mail-mode))
|
|
2241 (error "Command must be used in a VM Mail mode buffer."))
|
24
|
2242 (let (start end e tag-string disposition)
|
|
2243 (if (< (point) (save-excursion (mail-text) (point)))
|
|
2244 (mail-text))
|
|
2245 (setq start (point)
|
|
2246 tag-string (format "[ATTACHMENT %s, %s]" object
|
|
2247 (or type "MIME file")))
|
20
|
2248 (insert tag-string "\n")
|
24
|
2249 (setq end (1- (point)))
|
|
2250 (if (and (stringp object) (not mimed))
|
|
2251 (progn
|
|
2252 (if (or (vm-mime-types-match "application" type)
|
|
2253 (vm-mime-types-match "model" type))
|
|
2254 (setq disposition (list "attachment"))
|
|
2255 (setq disposition (list "inline")))
|
|
2256 (setq disposition (nconc disposition
|
|
2257 (list
|
|
2258 (concat "filename=\""
|
|
2259 (file-name-nondirectory object)
|
30
|
2260 "\"")))))
|
|
2261 (setq disposition (list "unspecified")))
|
120
|
2262 (cond (vm-fsfemacs-19-p
|
24
|
2263 (put-text-property start end 'front-sticky nil)
|
|
2264 (put-text-property start end 'rear-nonsticky t)
|
30
|
2265 ;; can't be intangible because menu clicking at a position needs
|
|
2266 ;; to set point inside the tag so that a command can access the
|
|
2267 ;; text properties there.
|
|
2268 ;; (put-text-property start end 'intangible object)
|
24
|
2269 (put-text-property start end 'face vm-mime-button-face)
|
|
2270 (put-text-property start end 'vm-mime-type type)
|
|
2271 (put-text-property start end 'vm-mime-object object)
|
|
2272 (put-text-property start end 'vm-mime-parameters params)
|
|
2273 (put-text-property start end 'vm-mime-description description)
|
|
2274 (put-text-property start end 'vm-mime-disposition disposition)
|
|
2275 (put-text-property start end 'vm-mime-encoded mimed)
|
|
2276 (put-text-property start end 'vm-mime-object object))
|
120
|
2277 (vm-xemacs-p
|
24
|
2278 (setq e (make-extent start end))
|
126
|
2279 (vm-mime-set-extent-glyph-for-type e (or type "text/plain"))
|
20
|
2280 (set-extent-property e 'start-open t)
|
24
|
2281 (set-extent-property e 'face vm-mime-button-face)
|
118
|
2282 (set-extent-property e 'duplicable t)
|
30
|
2283 (let ((keymap (make-sparse-keymap)))
|
|
2284 (if vm-popup-menu-on-mouse-3
|
|
2285 (define-key keymap 'button3
|
|
2286 'vm-menu-popup-content-disposition-menu))
|
118
|
2287 (set-extent-property e 'keymap keymap)
|
30
|
2288 (set-extent-property e 'balloon-help 'vm-mouse-3-help))
|
118
|
2289 (set-extent-property e 'vm-mime-type type)
|
|
2290 (set-extent-property e 'vm-mime-object object)
|
|
2291 (set-extent-property e 'vm-mime-parameters params)
|
|
2292 (set-extent-property e 'vm-mime-description description)
|
|
2293 (set-extent-property e 'vm-mime-disposition disposition)
|
|
2294 (set-extent-property e 'vm-mime-encoded mimed)))))
|
24
|
2295
|
30
|
2296 (defun vm-mime-attachment-disposition-at-point ()
|
120
|
2297 (cond (vm-fsfemacs-19-p
|
30
|
2298 (let ((disp (get-text-property (point) 'vm-mime-disposition)))
|
|
2299 (intern (car disp))))
|
120
|
2300 (vm-xemacs-p
|
30
|
2301 (let* ((e (extent-at (point) nil 'vm-mime-disposition))
|
|
2302 (disp (extent-property e 'vm-mime-disposition)))
|
|
2303 (intern (car disp))))))
|
|
2304
|
|
2305 (defun vm-mime-set-attachment-disposition-at-point (sym)
|
120
|
2306 (cond (vm-fsfemacs-19-p
|
30
|
2307 (let ((disp (get-text-property (point) 'vm-mime-disposition)))
|
|
2308 (setcar disp (symbol-name sym))))
|
120
|
2309 (vm-xemacs-p
|
30
|
2310 (let* ((e (extent-at (point) nil 'vm-mime-disposition))
|
|
2311 (disp (extent-property e 'vm-mime-disposition)))
|
|
2312 (setcar disp (symbol-name sym))))))
|
|
2313
|
24
|
2314 (defun vm-disallow-overlay-endpoint-insertion (overlay after start end
|
|
2315 &optional old-size)
|
|
2316 (cond ((null after) nil)
|
|
2317 ((= start (overlay-start overlay))
|
|
2318 (move-overlay overlay end (overlay-end overlay)))
|
|
2319 ((= start (overlay-end overlay))
|
|
2320 (move-overlay overlay (overlay-start overlay) start))))
|
|
2321
|
|
2322 (defun vm-mime-fake-attachment-overlays (start end)
|
|
2323 (let ((o-list nil)
|
|
2324 (done nil)
|
|
2325 (pos start)
|
26
|
2326 object props o)
|
24
|
2327 (save-excursion
|
|
2328 (save-restriction
|
|
2329 (narrow-to-region start end)
|
|
2330 (while (not done)
|
|
2331 (setq object (get-text-property pos 'vm-mime-object))
|
|
2332 (setq pos (next-single-property-change pos 'vm-mime-object))
|
|
2333 (or pos (setq pos (point-max) done t))
|
|
2334 (if object
|
|
2335 (progn
|
|
2336 (setq o (make-overlay start pos))
|
|
2337 (overlay-put o 'insert-in-front-hooks
|
|
2338 '(vm-disallow-overlay-endpoint-insertion))
|
|
2339 (overlay-put o 'insert-behind-hooks
|
|
2340 '(vm-disallow-overlay-endpoint-insertion))
|
|
2341 (setq props (text-properties-at start))
|
|
2342 (while props
|
|
2343 (overlay-put o (car props) (car (cdr props)))
|
|
2344 (setq props (cdr (cdr props))))
|
|
2345 (setq o-list (cons o o-list))))
|
|
2346 (setq start pos))
|
|
2347 o-list ))))
|
20
|
2348
|
|
2349 (defun vm-mime-default-type-from-filename (file)
|
|
2350 (let ((alist vm-mime-attachment-auto-type-alist)
|
|
2351 (case-fold-search t)
|
|
2352 (done nil))
|
|
2353 (while (and alist (not done))
|
|
2354 (if (string-match (car (car alist)) file)
|
|
2355 (setq done t)
|
|
2356 (setq alist (cdr alist))))
|
|
2357 (and alist (cdr (car alist)))))
|
|
2358
|
|
2359 (defun vm-remove-mail-mode-header-separator ()
|
|
2360 (save-excursion
|
|
2361 (goto-char (point-min))
|
|
2362 (if (re-search-forward (concat "^" mail-header-separator "$") nil t)
|
|
2363 (progn
|
|
2364 (delete-region (match-beginning 0) (match-end 0))
|
|
2365 t )
|
|
2366 nil )))
|
|
2367
|
|
2368 (defun vm-add-mail-mode-header-separator ()
|
|
2369 (save-excursion
|
|
2370 (goto-char (point-min))
|
|
2371 (if (re-search-forward "^$" nil t)
|
|
2372 (replace-match mail-header-separator t t))))
|
|
2373
|
|
2374 (defun vm-mime-transfer-encode-region (encoding beg end crlf)
|
30
|
2375 (let ((case-fold-search t)
|
|
2376 (armor-from (and vm-mime-composition-armor-from-lines
|
|
2377 (let ((case-fold-search nil))
|
|
2378 (save-excursion
|
|
2379 (goto-char beg)
|
|
2380 (re-search-forward "^From " nil t))))))
|
20
|
2381 (cond ((string-match "^binary$" encoding)
|
|
2382 (vm-mime-base64-encode-region beg end crlf)
|
|
2383 (setq encoding "base64"))
|
30
|
2384 ((and (not armor-from) (string-match "^7bit$" encoding)) t)
|
20
|
2385 ((string-match "^base64$" encoding) t)
|
|
2386 ((string-match "^quoted-printable$" encoding) t)
|
|
2387 ((eq vm-mime-8bit-text-transfer-encoding 'quoted-printable)
|
30
|
2388 (vm-mime-qp-encode-region beg end nil armor-from)
|
20
|
2389 (setq encoding "quoted-printable"))
|
|
2390 ((eq vm-mime-8bit-text-transfer-encoding 'base64)
|
|
2391 (vm-mime-base64-encode-region beg end crlf)
|
|
2392 (setq encoding "base64"))
|
30
|
2393 (armor-from (vm-mime-qp-encode-region beg end nil armor-from))
|
|
2394 ((eq vm-mime-8bit-text-transfer-encoding '8bit) t))
|
20
|
2395 encoding ))
|
|
2396
|
|
2397 (defun vm-mime-transfer-encode-layout (layout)
|
136
|
2398 (let ((list (vm-mm-layout-parts layout))
|
|
2399 (type (car (vm-mm-layout-type layout)))
|
|
2400 (encoding "7bit")
|
|
2401 (vm-mime-8bit-text-transfer-encoding
|
|
2402 vm-mime-8bit-text-transfer-encoding))
|
|
2403 (cond ((vm-mime-composite-type-p type)
|
|
2404 ;; MIME messages of type "message" and
|
|
2405 ;; "multipart" are required to have a non-opaque
|
|
2406 ;; content transfer encoding. This means that
|
|
2407 ;; if the user only wants to send out 7bit data,
|
|
2408 ;; then any subpart that contains 8bit data must
|
|
2409 ;; have an opaque (qp or base64) 8->7bit
|
|
2410 ;; conversion performed on it so that the
|
|
2411 ;; enclosing entity can use a non-opaque
|
|
2412 ;; encoding.
|
|
2413 ;;
|
|
2414 ;; message/partial requires a "7bit" encoding so
|
|
2415 ;; force 8->7 conversion in that case.
|
|
2416 (cond ((memq vm-mime-8bit-text-transfer-encoding
|
|
2417 '(quoted-printable base64))
|
|
2418 t)
|
|
2419 ((vm-mime-types-match "message/partial" type)
|
|
2420 (setq vm-mime-8bit-text-transfer-encoding
|
|
2421 'quoted-printable)))
|
|
2422 (while list
|
|
2423 (if (equal (vm-mime-transfer-encode-layout (car list)) "8bit")
|
|
2424 (setq encoding "8bit"))
|
|
2425 (setq list (cdr list))))
|
|
2426 (t
|
|
2427 (if (and (vm-mime-types-match "message/partial" type)
|
|
2428 (not (memq vm-mime-8bit-text-transfer-encoding
|
|
2429 '(quoted-printable base64))))
|
|
2430 (setq vm-mime-8bit-text-transfer-encoding
|
|
2431 'quoted-printable))
|
|
2432 (setq encoding
|
|
2433 (vm-mime-transfer-encode-region (vm-mm-layout-encoding layout)
|
|
2434 (vm-mm-layout-body-start layout)
|
|
2435 (vm-mm-layout-body-end layout)
|
|
2436 (vm-mime-text-type-p layout)))))
|
|
2437 (save-excursion
|
|
2438 (save-restriction
|
|
2439 (goto-char (vm-mm-layout-header-start layout))
|
|
2440 (narrow-to-region (point) (vm-mm-layout-body-start layout))
|
|
2441 (vm-reorder-message-headers nil nil "Content-Transfer-Encoding:")
|
|
2442 (if (not (equal encoding "7bit"))
|
|
2443 (insert "CONTENT-TRANSFER-ENCODING: " encoding "\n"))
|
|
2444 encoding ))))
|
24
|
2445
|
20
|
2446 (defun vm-mime-encode-composition ()
|
118
|
2447 "MIME encode the current mail composition buffer.
|
20
|
2448 Attachment tags added to the buffer with vm-mime-attach-file are expanded
|
|
2449 and the approriate content-type and boundary markup information is added."
|
|
2450 (interactive)
|
120
|
2451 (cond (vm-xemacs-mule-p
|
118
|
2452 (vm-mime-xemacs-encode-composition))
|
120
|
2453 (vm-xemacs-p
|
118
|
2454 (vm-mime-xemacs-encode-composition))
|
120
|
2455 (vm-fsfemacs-19-p
|
118
|
2456 (vm-mime-fsfemacs-encode-composition))
|
|
2457 (t
|
|
2458 (error "don't know how to MIME encode composition for %s"
|
|
2459 (emacs-version)))))
|
|
2460
|
|
2461 (defun vm-mime-xemacs-encode-composition ()
|
20
|
2462 (save-restriction
|
|
2463 (widen)
|
|
2464 (if (not (eq major-mode 'mail-mode))
|
|
2465 (error "Command must be used in a VM Mail mode buffer."))
|
|
2466 (or (null (vm-mail-mode-get-header-contents "MIME-Version:"))
|
|
2467 (error "Message is already MIME encoded."))
|
|
2468 (let ((8bit nil)
|
|
2469 (just-one nil)
|
|
2470 (boundary-positions nil)
|
|
2471 already-mimed layout e e-list boundary
|
24
|
2472 type encoding charset params description disposition object
|
|
2473 opoint-min)
|
20
|
2474 (mail-text)
|
118
|
2475 (setq e-list (extent-list nil (point) (point-max))
|
20
|
2476 e-list (vm-delete (function
|
|
2477 (lambda (e)
|
118
|
2478 (extent-property e 'vm-mime-object)))
|
20
|
2479 e-list t)
|
|
2480 e-list (sort e-list (function
|
|
2481 (lambda (e1 e2)
|
118
|
2482 (< (extent-end-position e1)
|
|
2483 (extent-end-position e2))))))
|
20
|
2484 ;; If there's just one attachment and no other readable
|
|
2485 ;; text in the buffer then make the message type just be
|
|
2486 ;; the attachment type rather than sending a multipart
|
|
2487 ;; message with one attachment
|
|
2488 (setq just-one (and (= (length e-list) 1)
|
|
2489 (looking-at "[ \t\n]*")
|
|
2490 (= (match-end 0)
|
118
|
2491 (extent-start-position (car e-list)))
|
20
|
2492 (save-excursion
|
118
|
2493 (goto-char (extent-end-position (car e-list)))
|
20
|
2494 (looking-at "[ \t\n]*\\'"))))
|
|
2495 (if (null e-list)
|
|
2496 (progn
|
|
2497 (narrow-to-region (point) (point-max))
|
|
2498 (setq charset (vm-determine-proper-charset (point-min)
|
|
2499 (point-max)))
|
|
2500 (setq encoding (vm-determine-proper-content-transfer-encoding
|
|
2501 (point-min)
|
|
2502 (point-max))
|
|
2503 encoding (vm-mime-transfer-encode-region encoding
|
|
2504 (point-min)
|
|
2505 (point-max)
|
|
2506 t))
|
|
2507 (widen)
|
|
2508 (vm-remove-mail-mode-header-separator)
|
|
2509 (goto-char (point-min))
|
|
2510 (vm-reorder-message-headers
|
|
2511 nil nil "\\(Content-Type:\\|Content-Transfer-Encoding\\|MIME-Version:\\)")
|
|
2512 (insert "MIME-Version: 1.0\n")
|
|
2513 (insert "Content-Type: text/plain; charset=" charset "\n")
|
|
2514 (insert "Content-Transfer-Encoding: " encoding "\n")
|
|
2515 (vm-add-mail-mode-header-separator))
|
|
2516 (while e-list
|
|
2517 (setq e (car e-list))
|
118
|
2518 (if (or just-one (= (point) (extent-start-position e)))
|
20
|
2519 nil
|
118
|
2520 (narrow-to-region (point) (extent-start-position e))
|
20
|
2521 (setq charset (vm-determine-proper-charset (point-min)
|
|
2522 (point-max)))
|
|
2523 (setq encoding (vm-determine-proper-content-transfer-encoding
|
|
2524 (point-min)
|
|
2525 (point-max))
|
|
2526 encoding (vm-mime-transfer-encode-region encoding
|
|
2527 (point-min)
|
|
2528 (point-max)
|
|
2529 t))
|
|
2530 (setq boundary-positions (cons (point-marker) boundary-positions))
|
|
2531 (insert "Content-Type: text/plain; charset=" charset "\n")
|
|
2532 (insert "Content-Transfer-Encoding: " encoding "\n\n")
|
|
2533 (widen))
|
118
|
2534 (goto-char (extent-start-position e))
|
20
|
2535 (narrow-to-region (point) (point))
|
118
|
2536 (setq object (extent-property e 'vm-mime-object))
|
24
|
2537 ;; insert the object
|
20
|
2538 (cond ((bufferp object)
|
118
|
2539 (insert-buffer-substring object))
|
20
|
2540 ((stringp object)
|
136
|
2541 (let ((overriding-file-coding-system 'no-conversion)
|
131
|
2542 ;; don't let file-coding-system be changed
|
|
2543 ;; by insert-file-contents-literally. The
|
|
2544 ;; value we bind to it to here isn't important.
|
136
|
2545 (file-coding-system 'no-conversion))
|
118
|
2546 (insert-file-contents-literally object))))
|
24
|
2547 ;; gather information about the object from the extent.
|
118
|
2548 (if (setq already-mimed (extent-property e 'vm-mime-encoded))
|
20
|
2549 (setq layout (vm-mime-parse-entity
|
|
2550 nil (list "text/plain" "charset=us-ascii")
|
|
2551 "7bit")
|
118
|
2552 type (or (extent-property e 'vm-mime-type)
|
24
|
2553 (car (vm-mm-layout-type layout)))
|
118
|
2554 params (or (extent-property e 'vm-mime-parameters)
|
24
|
2555 (cdr (vm-mm-layout-qtype layout)))
|
118
|
2556 description (extent-property e 'vm-mime-description)
|
30
|
2557 disposition
|
|
2558 (if (not
|
|
2559 (equal
|
118
|
2560 (car (extent-property e 'vm-mime-disposition))
|
30
|
2561 "unspecified"))
|
118
|
2562 (extent-property e 'vm-mime-disposition)
|
30
|
2563 (vm-mm-layout-qdisposition layout)))
|
118
|
2564 (setq type (extent-property e 'vm-mime-type)
|
|
2565 params (extent-property e 'vm-mime-parameters)
|
|
2566 description (extent-property e 'vm-mime-description)
|
30
|
2567 disposition
|
|
2568 (if (not (equal
|
118
|
2569 (car (extent-property e 'vm-mime-disposition))
|
30
|
2570 "unspecified"))
|
118
|
2571 (extent-property e 'vm-mime-disposition)
|
30
|
2572 nil)))
|
20
|
2573 (cond ((vm-mime-types-match "text" type)
|
|
2574 (setq encoding
|
|
2575 (vm-determine-proper-content-transfer-encoding
|
|
2576 (if already-mimed
|
|
2577 (vm-mm-layout-body-start layout)
|
|
2578 (point-min))
|
|
2579 (point-max))
|
|
2580 encoding (vm-mime-transfer-encode-region
|
|
2581 encoding
|
|
2582 (if already-mimed
|
|
2583 (vm-mm-layout-body-start layout)
|
|
2584 (point-min))
|
|
2585 (point-max)
|
|
2586 t))
|
|
2587 (setq 8bit (or 8bit (equal encoding "8bit"))))
|
136
|
2588 ((vm-mime-composite-type-p type)
|
20
|
2589 (setq opoint-min (point-min))
|
|
2590 (if (not already-mimed)
|
|
2591 (setq layout (vm-mime-parse-entity
|
|
2592 nil (list "text/plain" "charset=us-ascii")
|
|
2593 "7bit")))
|
136
|
2594 (setq encoding (vm-mime-transfer-encode-layout layout))
|
20
|
2595 (setq 8bit (or 8bit (equal encoding "8bit")))
|
|
2596 (goto-char (point-max))
|
|
2597 (widen)
|
|
2598 (narrow-to-region opoint-min (point)))
|
|
2599 (t
|
|
2600 (vm-mime-base64-encode-region
|
|
2601 (if already-mimed
|
|
2602 (vm-mm-layout-body-start layout)
|
|
2603 (point-min))
|
|
2604 (point-max))
|
|
2605 (setq encoding "base64")))
|
|
2606 (if just-one
|
|
2607 nil
|
|
2608 (goto-char (point-min))
|
|
2609 (setq boundary-positions (cons (point-marker) boundary-positions))
|
|
2610 (if (not already-mimed)
|
|
2611 nil
|
|
2612 ;; trim headers
|
|
2613 (vm-reorder-message-headers
|
24
|
2614 nil (nconc (list "Content-Disposition:" "Content-ID:")
|
|
2615 (if description
|
|
2616 (list "Content-Description:")
|
|
2617 nil))
|
|
2618 nil)
|
20
|
2619 ;; remove header/text separator
|
|
2620 (goto-char (1- (vm-mm-layout-body-start layout)))
|
|
2621 (if (looking-at "\n")
|
|
2622 (delete-char 1)))
|
|
2623 (insert "Content-Type: " type)
|
|
2624 (if params
|
|
2625 (if vm-mime-avoid-folding-content-type
|
|
2626 (insert "; " (mapconcat 'identity params "; ") "\n")
|
|
2627 (insert ";\n\t" (mapconcat 'identity params ";\n\t") "\n"))
|
|
2628 (insert "\n"))
|
24
|
2629 (and description
|
|
2630 (insert "Content-Description: " description "\n"))
|
|
2631 (if disposition
|
|
2632 (progn
|
|
2633 (insert "Content-Disposition: " (car disposition))
|
|
2634 (if (cdr disposition)
|
|
2635 (insert ";\n\t" (mapconcat 'identity
|
|
2636 (cdr disposition)
|
|
2637 ";\n\t")))
|
|
2638 (insert "\n")))
|
20
|
2639 (insert "Content-Transfer-Encoding: " encoding "\n\n"))
|
|
2640 (goto-char (point-max))
|
|
2641 (widen)
|
30
|
2642 (save-excursion
|
118
|
2643 (goto-char (extent-start-position e))
|
30
|
2644 (vm-assert (looking-at "\\[ATTACHMENT")))
|
118
|
2645 (delete-region (extent-start-position e)
|
|
2646 (extent-end-position e))
|
|
2647 (detach-extent e)
|
24
|
2648 (if (looking-at "\n")
|
|
2649 (delete-char 1))
|
20
|
2650 (setq e-list (cdr e-list)))
|
|
2651 ;; handle the remaining chunk of text after the last
|
|
2652 ;; extent, if any.
|
|
2653 (if (or just-one (= (point) (point-max)))
|
|
2654 nil
|
|
2655 (setq charset (vm-determine-proper-charset (point)
|
|
2656 (point-max)))
|
|
2657 (setq encoding (vm-determine-proper-content-transfer-encoding
|
|
2658 (point)
|
|
2659 (point-max))
|
|
2660 encoding (vm-mime-transfer-encode-region encoding
|
|
2661 (point)
|
|
2662 (point-max)
|
|
2663 t))
|
|
2664 (setq 8bit (or 8bit (equal encoding "8bit")))
|
|
2665 (setq boundary-positions (cons (point-marker) boundary-positions))
|
|
2666 (insert "Content-Type: text/plain; charset=" charset "\n")
|
|
2667 (insert "Content-Transfer-Encoding: " encoding "\n\n")
|
|
2668 (goto-char (point-max)))
|
|
2669 (setq boundary (vm-mime-make-multipart-boundary))
|
|
2670 (mail-text)
|
|
2671 (while (re-search-forward (concat "^--"
|
|
2672 (regexp-quote boundary)
|
|
2673 "\\(--\\)?$")
|
|
2674 nil t)
|
|
2675 (setq boundary (vm-mime-make-multipart-boundary))
|
|
2676 (mail-text))
|
|
2677 (goto-char (point-max))
|
|
2678 (or just-one (insert "\n--" boundary "--\n"))
|
|
2679 (while boundary-positions
|
|
2680 (goto-char (car boundary-positions))
|
|
2681 (insert "\n--" boundary "\n")
|
|
2682 (setq boundary-positions (cdr boundary-positions)))
|
|
2683 (if (and just-one already-mimed)
|
|
2684 (progn
|
|
2685 (goto-char (vm-mm-layout-header-start layout))
|
|
2686 ;; trim headers
|
|
2687 (vm-reorder-message-headers
|
|
2688 nil '("Content-Description:" "Content-ID:") nil)
|
|
2689 ;; remove header/text separator
|
|
2690 (goto-char (1- (vm-mm-layout-body-start layout)))
|
|
2691 (if (looking-at "\n")
|
|
2692 (delete-char 1))
|
|
2693 ;; copy remainder to enclosing entity's header section
|
|
2694 (insert-buffer-substring (current-buffer)
|
|
2695 (vm-mm-layout-header-start layout)
|
|
2696 (vm-mm-layout-body-start layout))
|
|
2697 (delete-region (vm-mm-layout-header-start layout)
|
|
2698 (vm-mm-layout-body-start layout))))
|
|
2699 (goto-char (point-min))
|
|
2700 (vm-remove-mail-mode-header-separator)
|
|
2701 (vm-reorder-message-headers
|
|
2702 nil nil "\\(Content-Type:\\|MIME-Version:\\|Content-Transfer-Encoding\\)")
|
|
2703 (vm-add-mail-mode-header-separator)
|
|
2704 (insert "MIME-Version: 1.0\n")
|
|
2705 (if (not just-one)
|
|
2706 (insert (if vm-mime-avoid-folding-content-type
|
|
2707 "Content-Type: multipart/mixed; boundary=\""
|
|
2708 "Content-Type: multipart/mixed;\n\tboundary=\"")
|
|
2709 boundary "\"\n")
|
|
2710 (insert "Content-Type: " type)
|
|
2711 (if params
|
|
2712 (if vm-mime-avoid-folding-content-type
|
|
2713 (insert "; " (mapconcat 'identity params "; ") "\n")
|
126
|
2714 (insert ";\n\t" (mapconcat 'identity params ";\n\t") "\n"))
|
|
2715 (insert "\n")))
|
20
|
2716 (if just-one
|
24
|
2717 (and description
|
|
2718 (insert "Content-Description: " description "\n")))
|
|
2719 (if (and just-one disposition)
|
|
2720 (progn
|
|
2721 (insert "Content-Disposition: " (car disposition))
|
|
2722 (if (cdr disposition)
|
126
|
2723 (if vm-mime-avoid-folding-content-type
|
|
2724 (insert "; " (mapconcat 'identity (cdr disposition) "; ")
|
|
2725 "\n")
|
|
2726 (insert ";\n\t" (mapconcat 'identity (cdr disposition)
|
|
2727 ";\n\t")))
|
|
2728 (insert "\n"))))
|
24
|
2729 (if just-one
|
20
|
2730 (insert "Content-Transfer-Encoding: " encoding "\n")
|
|
2731 (if 8bit
|
|
2732 (insert "Content-Transfer-Encoding: 8bit\n")
|
|
2733 (insert "Content-Transfer-Encoding: 7bit\n")))))))
|
|
2734
|
118
|
2735 (defun vm-mime-fsfemacs-encode-composition ()
|
|
2736 (save-restriction
|
|
2737 (widen)
|
|
2738 (if (not (eq major-mode 'mail-mode))
|
|
2739 (error "Command must be used in a VM Mail mode buffer."))
|
|
2740 (or (null (vm-mail-mode-get-header-contents "MIME-Version:"))
|
|
2741 (error "Message is already MIME encoded."))
|
|
2742 (let ((8bit nil)
|
|
2743 (just-one nil)
|
|
2744 (boundary-positions nil)
|
|
2745 already-mimed layout o o-list boundary
|
|
2746 type encoding charset params description disposition object
|
|
2747 opoint-min)
|
|
2748 (mail-text)
|
|
2749 (setq o-list (vm-mime-fake-attachment-overlays (point) (point-max))
|
|
2750 o-list (vm-delete (function
|
|
2751 (lambda (o)
|
|
2752 (overlay-get o 'vm-mime-object)))
|
|
2753 o-list t)
|
|
2754 o-list (sort o-list (function
|
|
2755 (lambda (e1 e2)
|
|
2756 (< (overlay-end e1)
|
|
2757 (overlay-end e2))))))
|
|
2758 ;; If there's just one attachment and no other readable
|
|
2759 ;; text in the buffer then make the message type just be
|
|
2760 ;; the attachment type rather than sending a multipart
|
|
2761 ;; message with one attachment
|
|
2762 (setq just-one (and (= (length o-list) 1)
|
|
2763 (looking-at "[ \t\n]*")
|
|
2764 (= (match-end 0)
|
|
2765 (overlay-start (car o-list)))
|
|
2766 (save-excursion
|
|
2767 (goto-char (overlay-end (car o-list)))
|
|
2768 (looking-at "[ \t\n]*\\'"))))
|
|
2769 (if (null o-list)
|
|
2770 (progn
|
|
2771 (narrow-to-region (point) (point-max))
|
|
2772 (setq charset (vm-determine-proper-charset (point-min)
|
|
2773 (point-max)))
|
|
2774 (setq encoding (vm-determine-proper-content-transfer-encoding
|
|
2775 (point-min)
|
|
2776 (point-max))
|
|
2777 encoding (vm-mime-transfer-encode-region encoding
|
|
2778 (point-min)
|
|
2779 (point-max)
|
|
2780 t))
|
|
2781 (widen)
|
|
2782 (vm-remove-mail-mode-header-separator)
|
|
2783 (goto-char (point-min))
|
|
2784 (vm-reorder-message-headers
|
|
2785 nil nil "\\(Content-Type:\\|Content-Transfer-Encoding\\|MIME-Version:\\)")
|
|
2786 (insert "MIME-Version: 1.0\n")
|
|
2787 (insert "Content-Type: text/plain; charset=" charset "\n")
|
|
2788 (insert "Content-Transfer-Encoding: " encoding "\n")
|
|
2789 (vm-add-mail-mode-header-separator))
|
|
2790 (while o-list
|
|
2791 (setq o (car o-list))
|
|
2792 (if (or just-one (= (point) (overlay-start o)))
|
|
2793 nil
|
|
2794 (narrow-to-region (point) (overlay-start o))
|
|
2795 (setq charset (vm-determine-proper-charset (point-min)
|
|
2796 (point-max)))
|
|
2797 (setq encoding (vm-determine-proper-content-transfer-encoding
|
|
2798 (point-min)
|
|
2799 (point-max))
|
|
2800 encoding (vm-mime-transfer-encode-region encoding
|
|
2801 (point-min)
|
|
2802 (point-max)
|
|
2803 t))
|
|
2804 (setq boundary-positions (cons (point-marker) boundary-positions))
|
|
2805 (insert "Content-Type: text/plain; charset=" charset "\n")
|
|
2806 (insert "Content-Transfer-Encoding: " encoding "\n\n")
|
|
2807 (widen))
|
|
2808 (goto-char (overlay-start o))
|
|
2809 (narrow-to-region (point) (point))
|
|
2810 (setq object (overlay-get o 'vm-mime-object))
|
|
2811 ;; insert the object
|
|
2812 (cond ((bufferp object)
|
|
2813 ;; as of FSF Emacs 19.34, even with the hooks
|
|
2814 ;; we've attached to the attachment overlays,
|
|
2815 ;; text STILL can be inserted into them when
|
|
2816 ;; font-lock is enabled. Explaining why is
|
|
2817 ;; beyond the scope of this comment and I
|
|
2818 ;; don't know the answer anyway. This works
|
|
2819 ;; to prevent it.
|
|
2820 (insert-before-markers " ")
|
|
2821 (forward-char -1)
|
|
2822 (insert-buffer-substring object)
|
|
2823 (delete-char 1))
|
|
2824 ((stringp object)
|
|
2825 (insert-before-markers " ")
|
|
2826 (forward-char -1)
|
|
2827 (insert-file-contents object)
|
|
2828 (goto-char (point-max))
|
|
2829 (delete-char -1)))
|
|
2830 ;; gather information about the object from the extent.
|
|
2831 (if (setq already-mimed (overlay-get o 'vm-mime-encoded))
|
|
2832 (setq layout (vm-mime-parse-entity
|
|
2833 nil (list "text/plain" "charset=us-ascii")
|
|
2834 "7bit")
|
|
2835 type (or (overlay-get o 'vm-mime-type)
|
|
2836 (car (vm-mm-layout-type layout)))
|
|
2837 params (or (overlay-get o 'vm-mime-parameters)
|
|
2838 (cdr (vm-mm-layout-qtype layout)))
|
|
2839 description (overlay-get o 'vm-mime-description)
|
|
2840 disposition
|
|
2841 (if (not
|
|
2842 (equal
|
|
2843 (car (overlay-get o 'vm-mime-disposition))
|
|
2844 "unspecified"))
|
|
2845 (overlay-get o 'vm-mime-disposition)
|
|
2846 (vm-mm-layout-qdisposition layout)))
|
|
2847 (setq type (overlay-get o 'vm-mime-type)
|
|
2848 params (overlay-get o 'vm-mime-parameters)
|
|
2849 description (overlay-get o 'vm-mime-description)
|
|
2850 disposition
|
|
2851 (if (not (equal
|
|
2852 (car (overlay-get o 'vm-mime-disposition))
|
|
2853 "unspecified"))
|
|
2854 (overlay-get o 'vm-mime-disposition)
|
|
2855 nil)))
|
|
2856 (cond ((vm-mime-types-match "text" type)
|
|
2857 (setq encoding
|
|
2858 (vm-determine-proper-content-transfer-encoding
|
|
2859 (if already-mimed
|
|
2860 (vm-mm-layout-body-start layout)
|
|
2861 (point-min))
|
|
2862 (point-max))
|
|
2863 encoding (vm-mime-transfer-encode-region
|
|
2864 encoding
|
|
2865 (if already-mimed
|
|
2866 (vm-mm-layout-body-start layout)
|
|
2867 (point-min))
|
|
2868 (point-max)
|
|
2869 t))
|
|
2870 (setq 8bit (or 8bit (equal encoding "8bit"))))
|
136
|
2871 ((vm-mime-composite-type-p type)
|
118
|
2872 (setq opoint-min (point-min))
|
|
2873 (if (not already-mimed)
|
|
2874 (setq layout (vm-mime-parse-entity
|
|
2875 nil (list "text/plain" "charset=us-ascii")
|
|
2876 "7bit")))
|
136
|
2877 (setq encoding (vm-mime-transfer-encode-layout layout))
|
118
|
2878 (setq 8bit (or 8bit (equal encoding "8bit")))
|
|
2879 (goto-char (point-max))
|
|
2880 (widen)
|
|
2881 (narrow-to-region opoint-min (point)))
|
|
2882 (t
|
|
2883 (vm-mime-base64-encode-region
|
|
2884 (if already-mimed
|
|
2885 (vm-mm-layout-body-start layout)
|
|
2886 (point-min))
|
|
2887 (point-max))
|
|
2888 (setq encoding "base64")))
|
|
2889 (if just-one
|
|
2890 nil
|
|
2891 (goto-char (point-min))
|
|
2892 (setq boundary-positions (cons (point-marker) boundary-positions))
|
|
2893 (if (not already-mimed)
|
|
2894 nil
|
|
2895 ;; trim headers
|
|
2896 (vm-reorder-message-headers
|
|
2897 nil (nconc (list "Content-Disposition:" "Content-ID:")
|
|
2898 (if description
|
|
2899 (list "Content-Description:")
|
|
2900 nil))
|
|
2901 nil)
|
|
2902 ;; remove header/text separator
|
|
2903 (goto-char (1- (vm-mm-layout-body-start layout)))
|
|
2904 (if (looking-at "\n")
|
|
2905 (delete-char 1)))
|
|
2906 (insert "Content-Type: " type)
|
|
2907 (if params
|
|
2908 (if vm-mime-avoid-folding-content-type
|
|
2909 (insert "; " (mapconcat 'identity params "; ") "\n")
|
|
2910 (insert ";\n\t" (mapconcat 'identity params ";\n\t") "\n"))
|
|
2911 (insert "\n"))
|
|
2912 (and description
|
|
2913 (insert "Content-Description: " description "\n"))
|
|
2914 (if disposition
|
|
2915 (progn
|
|
2916 (insert "Content-Disposition: " (car disposition))
|
|
2917 (if (cdr disposition)
|
|
2918 (insert ";\n\t" (mapconcat 'identity
|
|
2919 (cdr disposition)
|
|
2920 ";\n\t")))
|
|
2921 (insert "\n")))
|
|
2922 (insert "Content-Transfer-Encoding: " encoding "\n\n"))
|
|
2923 (goto-char (point-max))
|
|
2924 (widen)
|
|
2925 (save-excursion
|
|
2926 (goto-char (overlay-start o))
|
|
2927 (vm-assert (looking-at "\\[ATTACHMENT")))
|
|
2928 (delete-region (overlay-start o)
|
|
2929 (overlay-end o))
|
|
2930 (delete-overlay o)
|
|
2931 (if (looking-at "\n")
|
|
2932 (delete-char 1))
|
|
2933 (setq o-list (cdr o-list)))
|
|
2934 ;; handle the remaining chunk of text after the last
|
|
2935 ;; extent, if any.
|
|
2936 (if (or just-one (= (point) (point-max)))
|
|
2937 nil
|
|
2938 (setq charset (vm-determine-proper-charset (point)
|
|
2939 (point-max)))
|
|
2940 (setq encoding (vm-determine-proper-content-transfer-encoding
|
|
2941 (point)
|
|
2942 (point-max))
|
|
2943 encoding (vm-mime-transfer-encode-region encoding
|
|
2944 (point)
|
|
2945 (point-max)
|
|
2946 t))
|
|
2947 (setq 8bit (or 8bit (equal encoding "8bit")))
|
|
2948 (setq boundary-positions (cons (point-marker) boundary-positions))
|
|
2949 (insert "Content-Type: text/plain; charset=" charset "\n")
|
|
2950 (insert "Content-Transfer-Encoding: " encoding "\n\n")
|
|
2951 (goto-char (point-max)))
|
|
2952 (setq boundary (vm-mime-make-multipart-boundary))
|
|
2953 (mail-text)
|
|
2954 (while (re-search-forward (concat "^--"
|
|
2955 (regexp-quote boundary)
|
|
2956 "\\(--\\)?$")
|
|
2957 nil t)
|
|
2958 (setq boundary (vm-mime-make-multipart-boundary))
|
|
2959 (mail-text))
|
|
2960 (goto-char (point-max))
|
|
2961 (or just-one (insert "\n--" boundary "--\n"))
|
|
2962 (while boundary-positions
|
|
2963 (goto-char (car boundary-positions))
|
|
2964 (insert "\n--" boundary "\n")
|
|
2965 (setq boundary-positions (cdr boundary-positions)))
|
|
2966 (if (and just-one already-mimed)
|
|
2967 (progn
|
|
2968 (goto-char (vm-mm-layout-header-start layout))
|
|
2969 ;; trim headers
|
|
2970 (vm-reorder-message-headers
|
|
2971 nil '("Content-Description:" "Content-ID:") nil)
|
|
2972 ;; remove header/text separator
|
|
2973 (goto-char (1- (vm-mm-layout-body-start layout)))
|
|
2974 (if (looking-at "\n")
|
|
2975 (delete-char 1))
|
|
2976 ;; copy remainder to enclosing entity's header section
|
|
2977 (insert-buffer-substring (current-buffer)
|
|
2978 (vm-mm-layout-header-start layout)
|
|
2979 (vm-mm-layout-body-start layout))
|
|
2980 (delete-region (vm-mm-layout-header-start layout)
|
|
2981 (vm-mm-layout-body-start layout))))
|
|
2982 (goto-char (point-min))
|
|
2983 (vm-remove-mail-mode-header-separator)
|
|
2984 (vm-reorder-message-headers
|
|
2985 nil nil "\\(Content-Type:\\|MIME-Version:\\|Content-Transfer-Encoding\\)")
|
|
2986 (vm-add-mail-mode-header-separator)
|
|
2987 (insert "MIME-Version: 1.0\n")
|
|
2988 (if (not just-one)
|
|
2989 (insert (if vm-mime-avoid-folding-content-type
|
|
2990 "Content-Type: multipart/mixed; boundary=\""
|
|
2991 "Content-Type: multipart/mixed;\n\tboundary=\"")
|
|
2992 boundary "\"\n")
|
|
2993 (insert "Content-Type: " type)
|
|
2994 (if params
|
|
2995 (if vm-mime-avoid-folding-content-type
|
|
2996 (insert "; " (mapconcat 'identity params "; ") "\n")
|
126
|
2997 (insert ";\n\t" (mapconcat 'identity params ";\n\t") "\n"))
|
|
2998 (insert "\n")))
|
118
|
2999 (if just-one
|
|
3000 (and description
|
|
3001 (insert "Content-Description: " description "\n")))
|
|
3002 (if (and just-one disposition)
|
|
3003 (progn
|
|
3004 (insert "Content-Disposition: " (car disposition))
|
|
3005 (if (cdr disposition)
|
126
|
3006 (if vm-mime-avoid-folding-content-type
|
|
3007 (insert "; " (mapconcat 'identity (cdr disposition) "; ")
|
|
3008 "\n")
|
|
3009 (insert ";\n\t" (mapconcat 'identity (cdr disposition)
|
|
3010 ";\n\t")))
|
|
3011 (insert "\n"))))
|
118
|
3012 (if just-one
|
|
3013 (insert "Content-Transfer-Encoding: " encoding "\n")
|
|
3014 (if 8bit
|
|
3015 (insert "Content-Transfer-Encoding: 8bit\n")
|
|
3016 (insert "Content-Transfer-Encoding: 7bit\n")))))))
|
|
3017
|
20
|
3018 (defun vm-mime-fragment-composition (size)
|
|
3019 (save-restriction
|
|
3020 (widen)
|
26
|
3021 (message "Fragmenting message...")
|
20
|
3022 (let ((buffers nil)
|
|
3023 (id (vm-mime-make-multipart-boundary))
|
|
3024 (n 1)
|
|
3025 (the-end nil)
|
|
3026 b header-start header-end master-buffer start end)
|
|
3027 (vm-remove-mail-mode-header-separator)
|
|
3028 ;; message/partial must have "7bit" content transfer
|
136
|
3029 ;; encoding, so force everything to be encoded for
|
20
|
3030 ;; 7bit transmission.
|
|
3031 (let ((vm-mime-8bit-text-transfer-encoding
|
118
|
3032 (if (eq vm-mime-8bit-text-transfer-encoding '8bit)
|
20
|
3033 'quoted-printable
|
|
3034 vm-mime-8bit-text-transfer-encoding)))
|
136
|
3035 (vm-mime-transfer-encode-layout
|
|
3036 (vm-mime-parse-entity nil (list "text/plain" "charset=us-ascii")
|
|
3037 "7bit")))
|
20
|
3038 (goto-char (point-min))
|
|
3039 (setq header-start (point))
|
|
3040 (search-forward "\n\n")
|
|
3041 (setq header-end (1- (point)))
|
|
3042 (setq master-buffer (current-buffer))
|
|
3043 (goto-char (point-min))
|
|
3044 (setq start (point))
|
|
3045 (while (not (eobp))
|
|
3046 (condition-case nil
|
|
3047 (progn
|
|
3048 (forward-char (max (- size 150) 2000))
|
|
3049 (beginning-of-line))
|
|
3050 (end-of-buffer (setq the-end t)))
|
|
3051 (setq end (point))
|
|
3052 (setq b (generate-new-buffer (concat (buffer-name) " part "
|
|
3053 (int-to-string n))))
|
|
3054 (setq buffers (cons b buffers))
|
|
3055 (set-buffer b)
|
|
3056 (make-local-variable 'vm-send-using-mime)
|
|
3057 (setq vm-send-using-mime nil)
|
|
3058 (insert-buffer-substring master-buffer header-start header-end)
|
|
3059 (goto-char (point-min))
|
|
3060 (vm-reorder-message-headers nil nil
|
|
3061 "\\(Content-Type:\\|MIME-Version:\\|Content-Transfer-Encoding\\)")
|
|
3062 (insert "MIME-Version: 1.0\n")
|
|
3063 (insert (format
|
|
3064 (if vm-mime-avoid-folding-content-type
|
|
3065 "Content-Type: message/partial; id=%s; number=%d"
|
|
3066 "Content-Type: message/partial;\n\tid=%s;\n\tnumber=%d")
|
|
3067 id n))
|
|
3068 (if the-end
|
|
3069 (if vm-mime-avoid-folding-content-type
|
|
3070 (insert (format "; total=%d\n" n))
|
|
3071 (insert (format ";\n\ttotal=%d\n" n)))
|
|
3072 (insert "\n"))
|
|
3073 (insert "Content-Transfer-Encoding: 7bit\n")
|
|
3074 (goto-char (point-max))
|
|
3075 (insert mail-header-separator "\n")
|
|
3076 (insert-buffer-substring master-buffer start end)
|
|
3077 (vm-increment n)
|
|
3078 (set-buffer master-buffer)
|
|
3079 (setq start (point)))
|
118
|
3080 (vm-add-mail-mode-header-separator)
|
26
|
3081 (message "Fragmenting message... done")
|
20
|
3082 (nreverse buffers))))
|
|
3083
|
|
3084 (defun vm-mime-preview-composition ()
|
|
3085 "Show how the current composition buffer might be displayed
|
|
3086 in a MIME-aware mail reader. VM copies and encodes the current
|
|
3087 mail composition buffer and displays it as a mail folder.
|
|
3088 Type `q' to quit this temp folder and return to composing your
|
|
3089 message."
|
|
3090 (interactive)
|
|
3091 (if (not (eq major-mode 'mail-mode))
|
|
3092 (error "Command must be used in a VM Mail mode buffer."))
|
|
3093 (let ((temp-buffer nil)
|
|
3094 (mail-buffer (current-buffer))
|
|
3095 e-list)
|
|
3096 (unwind-protect
|
|
3097 (progn
|
|
3098 (setq temp-buffer (generate-new-buffer "composition preview"))
|
|
3099 (set-buffer temp-buffer)
|
126
|
3100 ;; so vm-mime-xxxx-encode-composition won't complain
|
20
|
3101 (setq major-mode 'mail-mode)
|
|
3102 (vm-insert-region-from-buffer mail-buffer)
|
|
3103 (goto-char (point-min))
|
|
3104 (or (vm-mail-mode-get-header-contents "From")
|
114
|
3105 (insert "From: " (user-login-name) "\n"))
|
20
|
3106 (or (vm-mail-mode-get-header-contents "Message-ID")
|
24
|
3107 (insert "Message-ID: <fake@fake.fake>\n"))
|
20
|
3108 (or (vm-mail-mode-get-header-contents "Date")
|
|
3109 (insert "Date: "
|
|
3110 (format-time-string "%a, %d %b %Y %H%M%S %Z"
|
|
3111 (current-time))
|
|
3112 "\n"))
|
|
3113 (and vm-send-using-mime
|
|
3114 (null (vm-mail-mode-get-header-contents "MIME-Version:"))
|
|
3115 (vm-mime-encode-composition))
|
126
|
3116 (vm-remove-mail-mode-header-separator)
|
20
|
3117 (goto-char (point-min))
|
|
3118 (insert (vm-leading-message-separator 'From_))
|
|
3119 (goto-char (point-max))
|
|
3120 (insert (vm-trailing-message-separator 'From_))
|
|
3121 (set-buffer-modified-p nil)
|
|
3122 ;; point of no return, don't kill it if the user quits
|
|
3123 (setq temp-buffer nil)
|
|
3124 (let ((vm-auto-decode-mime-messages t)
|
|
3125 (vm-auto-displayed-mime-content-types t))
|
|
3126 (vm-save-buffer-excursion
|
|
3127 (vm-goto-new-folder-frame-maybe 'folder)
|
|
3128 (vm-mode)))
|
|
3129 (message
|
|
3130 (substitute-command-keys
|
|
3131 "Type \\[vm-quit] to continue composing your message"))
|
|
3132 ;; temp buffer, don't offer to save it.
|
|
3133 (setq buffer-offer-save nil)
|
|
3134 (vm-display (or vm-presentation-buffer (current-buffer)) t
|
|
3135 (list this-command) '(vm-mode startup)))
|
|
3136 (and temp-buffer (kill-buffer temp-buffer)))))
|
|
3137
|
|
3138 (defun vm-mime-composite-type-p (type)
|
136
|
3139 (or (and (vm-mime-types-match "message" type)
|
|
3140 (not (vm-mime-types-match "message/partial" type))
|
|
3141 (not (vm-mime-types-match "message/external-body" type)))
|
20
|
3142 (vm-mime-types-match "multipart" type)))
|
|
3143
|
136
|
3144 ;; Unused currrently.
|
|
3145 ;;
|
|
3146 ;;(defun vm-mime-map-atomic-layouts (function list)
|
|
3147 ;; (while list
|
|
3148 ;; (if (vm-mime-composite-type-p (car (vm-mm-layout-type (car list))))
|
|
3149 ;; (vm-mime-map-atomic-layouts function (vm-mm-layout-parts (car list)))
|
|
3150 ;; (funcall function (car list)))
|
|
3151 ;; (setq list (cdr list))))
|