17bc7b1717dd03fe02bd0f78e1caf10ec802c581
[elisp/semi.git] / mime-play.el
1 ;;; mime-play.el --- Playback processing module for mime-view.el
2
3 ;; Copyright (C) 1994,1995,1996,1997,1998 Free Software Foundation, Inc.
4
5 ;; Author: MORIOKA Tomohiko <morioka@jaist.ac.jp>
6 ;; Created: 1995/9/26 (separated from tm-view.el)
7 ;;      Renamed: 1997/2/21 from tm-play.el
8 ;; Keywords: MIME, multimedia, mail, news
9
10 ;; This file is part of SEMI (Secretariat of Emacs MIME Interfaces).
11
12 ;; This program is free software; you can redistribute it and/or
13 ;; modify it under the terms of the GNU General Public License as
14 ;; published by the Free Software Foundation; either version 2, or (at
15 ;; your option) any later version.
16
17 ;; This program is distributed in the hope that it will be useful, but
18 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 ;; General Public License for more details.
21
22 ;; You should have received a copy of the GNU General Public License
23 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
24 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 ;; Boston, MA 02111-1307, USA.
26
27 ;;; Code:
28
29 (require 'mime-view)
30 (require 'alist)
31 (require 'filename)
32
33 (eval-when-compile (require 'mime-text))
34
35
36 (defvar mime-acting-situation-examples nil)
37
38 (defun mime-save-acting-situation-examples ()
39   (let* ((file mime-acting-situation-examples-file)
40          (buffer (get-buffer-create " *mime-example*")))
41     (unwind-protect
42         (save-excursion
43           (set-buffer buffer)
44           (setq buffer-file-name file)
45           (erase-buffer)
46           (insert ";;; " (file-name-nondirectory file) "\n")
47           (insert "\n;; This file is generated automatically by "
48                   mime-view-version-string "\n\n")
49           (insert ";;; Code:\n\n")
50           (pp `(setq mime-acting-situation-examples
51                      ',mime-acting-situation-examples)
52               (current-buffer))
53           (insert "\n;;; "
54                   (file-name-nondirectory file)
55                   " ends here.\n")
56           (save-buffer))
57       (kill-buffer buffer))))
58
59 (add-hook 'kill-emacs-hook 'mime-save-acting-situation-examples)
60
61   
62 ;;; @ content decoder
63 ;;;
64
65 (defvar mime-preview-after-decoded-position nil)
66
67 (defun mime-preview-play-current-entity (&optional mode)
68   "Play current entity.
69 It decodes current entity to call internal or external method.  The
70 method is selected from variable `mime-acting-condition'.
71 If MODE is specified, play as it.  Default MODE is \"play\"."
72   (interactive)
73   (or mode
74       (setq mode "play"))
75   (let ((entity-info (get-text-property (point) 'mime-view-entity)))
76     (if entity-info
77         (let ((the-buf (current-buffer))
78               (raw-buffer (get-text-property (point) 'mime-view-raw-buffer)))
79           (setq mime-preview-after-decoded-position (point))
80           (set-buffer raw-buffer)
81           (mime-raw-play-entity entity-info mode)
82           (when (eq (current-buffer) raw-buffer)
83             (set-buffer the-buf)
84             (goto-char mime-preview-after-decoded-position)
85             )))))
86
87 (defun mime-raw-play-entity (entity-info &optional mode)
88   "Play entity specified by ENTITY-INFO.
89 It decodes the entity to call internal or external method.  The method
90 is selected from variable `mime-acting-condition'.  If MODE is
91 specified, play as it.  Default MODE is \"play\"."
92   (let ((beg (mime-entity-point-min entity-info))
93         (end (mime-entity-point-max entity-info))
94         (c-type (mime-entity-media-type entity-info))
95         (c-subtype (mime-entity-media-subtype entity-info))
96         (params (mime-entity-parameters entity-info))
97         (encoding (mime-entity-encoding entity-info))
98         )
99     (or c-type
100         (setq c-type 'text
101               c-subtype 'plain))
102     ;; Check for VM
103     (if (< beg (point-min))
104         (setq beg (point-min))
105       )
106     (if (< (point-max) end)
107         (setq end (point-max))
108       )
109     (let (method cal ret)
110       (setq cal (list* (cons 'type c-type)
111                        (cons 'subtype c-subtype)
112                        (cons 'encoding encoding)
113                        (cons 'major-mode major-mode)
114                        params))
115       (if mode
116           (setq cal (cons (cons 'mode mode) cal))
117         )
118       (setq ret
119             (or (ctree-match-calist mime-acting-situation-examples cal)
120                 (ctree-match-calist-partially mime-acting-situation-examples
121                                               cal)
122                 cal))
123       (setq ret
124             (or (ctree-find-calist mime-acting-condition ret
125                                    mime-view-find-every-acting-situation)
126                 (ctree-find-calist mime-acting-condition cal
127                                    mime-view-find-every-acting-situation)
128                 ))
129       (cond ((cdr ret)
130              (setq ret (select-menu-alist
131                         "Methods"
132                         (mapcar (function
133                                  (lambda (situation)
134                                    (cons
135                                     (format "%s"
136                                             (cdr (assq 'method situation)))
137                                     situation)))
138                                 ret)))
139              (setq ret
140                    (sort ret
141                          #'(lambda (a b)
142                              (let ((a-t (car a))
143                                    (b-t (car b))
144                                    (order '((type . 1)
145                                             (subtype . 2)
146                                             (mode . 3)
147                                             (major-mode . 4)))
148                                    a-order b-order)
149                                (if (symbolp a-t)
150                                    (let ((ret (assq a-t order)))
151                                      (if ret
152                                          (setq a-order (cdr ret))
153                                        (setq a-order 5)
154                                        ))
155                                  (setq a-order 6)
156                                  )
157                                (if (symbolp b-t)
158                                    (let ((ret (assq b-t order)))
159                                      (if ret
160                                          (setq b-order (cdr ret))
161                                        (setq b-order 5)
162                                        ))
163                                  (setq b-order 6)
164                                  )
165                                (if (= a-order b-order)
166                                    (string< (format "%s" a-t)(format "%s" b-t))
167                                  (< a-order b-order))
168                                ))))
169              (ctree-set-calist-strictly 'mime-acting-situation-examples ret)
170              )
171             (t
172              (setq ret (car ret))
173              ))
174       (setq method (cdr (assq 'method ret)))
175       (cond ((and (symbolp method)
176                   (fboundp method))
177              (funcall method beg end ret)
178              )
179             ((and (listp method)(stringp (car method)))
180              (mime-activate-external-method beg end ret)
181              )
182             (t
183              (mime-show-echo-buffer
184               "No method are specified for %s\n"
185               (mime-type/subtype-string c-type c-subtype))
186              ))
187       )
188     ))
189
190
191 ;;; @ external decoder
192 ;;;
193
194 (defun mime-activate-external-method (beg end cal)
195   (save-excursion
196     (save-restriction
197       (narrow-to-region beg end)
198       (goto-char beg)
199       (let ((method (cdr (assoc 'method cal)))
200             (name (mime-raw-get-filename cal))
201             )
202         (if method
203             (let ((file (make-temp-name
204                          (expand-file-name "TM" mime-temp-directory)))
205                   b args)
206               (if (nth 1 method)
207                   (setq b beg)
208                 (setq b
209                       (if (re-search-forward "^$" nil t)
210                           (1+ (match-end 0))
211                         (point-min)
212                         ))
213                 )
214               (goto-char b)
215               (write-region b end file)
216               (message "External method is starting...")
217               (setq cal (put-alist
218                          'name (replace-as-filename name) cal))
219               (setq cal (put-alist 'file file cal))
220               (setq args (nconc
221                           (list (car method)
222                                 mime-echo-buffer-name (car method)
223                                 )
224                           (mime-make-external-method-args
225                            cal (cdr (cdr method)))
226                           ))
227               (apply (function start-process) args)
228               (mime-show-echo-buffer)
229               ))
230         ))))
231
232 (defun mime-make-external-method-args (cal format)
233   (mapcar (function
234            (lambda (arg)
235              (if (stringp arg)
236                  arg
237                (let* ((item (eval arg))
238                       (ret (cdr (assoc item cal)))
239                       )
240                  (if ret
241                      ret
242                    (if (eq item 'encoding)
243                        "7bit"
244                      ""))
245                  ))
246              ))
247           format))
248
249 (defvar mime-echo-window-is-shared-with-bbdb t
250   "*If non-nil, mime-echo window is shared with BBDB window.")
251
252 (defvar mime-echo-window-height
253   (function
254    (lambda ()
255      (/ (window-height) 5)
256      ))
257   "*Size of mime-echo window.
258 It allows function or integer.  If it is function,
259 `mime-show-echo-buffer' calls it to get height of mime-echo window.
260 Otherwise `mime-show-echo-buffer' uses it as height of mime-echo
261 window.")
262
263 (defun mime-show-echo-buffer (&rest forms)
264   "Show mime-echo buffer to display MIME-playing information."
265   (get-buffer-create mime-echo-buffer-name)
266   (let ((the-win (selected-window))
267         (win (get-buffer-window mime-echo-buffer-name))
268         )
269     (or win
270         (if (and mime-echo-window-is-shared-with-bbdb
271                  (boundp 'bbdb-buffer-name)
272                  (setq win (get-buffer-window bbdb-buffer-name))
273                  )
274             (set-window-buffer win mime-echo-buffer-name)
275           (select-window (get-buffer-window mime-preview-buffer))
276           (setq win (split-window-vertically
277                      (- (window-height)
278                         (if (functionp mime-echo-window-height)
279                             (funcall mime-echo-window-height)
280                           mime-echo-window-height)
281                         )))
282           (set-window-buffer win mime-echo-buffer-name)
283           ))
284     (select-window win)
285     (goto-char (point-max))
286     (if forms
287         (insert (apply (function format) forms))
288       )
289     (select-window the-win)
290     ))
291
292
293 ;;; @ file name
294 ;;;
295
296 (defvar mime-view-file-name-char-regexp "[A-Za-z0-9+_-]")
297
298 (defvar mime-view-file-name-regexp-1
299   (concat mime-view-file-name-char-regexp "+\\."
300           mime-view-file-name-char-regexp "+"))
301
302 (defvar mime-view-file-name-regexp-2
303   (concat (regexp-* mime-view-file-name-char-regexp)
304           "\\(\\." mime-view-file-name-char-regexp "+\\)*"))
305
306 (defun mime-raw-get-original-filename (param &optional encoding)
307   (or (mime-raw-get-uu-filename param encoding)
308       (let (ret)
309         (or (if (or (and (setq ret (mime/Content-Disposition))
310                          (setq ret (assoc "filename" (cdr ret)))
311                          )
312                     (setq ret (assoc "name" param))
313                     (setq ret (assoc "x-name" param))
314                     )
315                 (std11-strip-quoted-string (cdr ret))
316               )
317             (if (setq ret
318                       (std11-find-field-body '("Content-Description"
319                                                "Subject")))
320                 (if (or (string-match mime-view-file-name-regexp-1 ret)
321                         (string-match mime-view-file-name-regexp-2 ret))
322                     (substring ret (match-beginning 0)(match-end 0))
323                   ))
324             ))
325       ))
326
327 (defun mime-raw-get-filename (param)
328   (replace-as-filename (mime-raw-get-original-filename param))
329   )
330
331
332 ;;; @ file extraction
333 ;;;
334
335 (defun mime-method-to-save (beg end cal)
336   (goto-char beg)
337   (let* ((name
338           (save-restriction
339             (narrow-to-region beg end)
340             (mime-raw-get-filename cal)
341             ))
342          (encoding (or (cdr (assq 'encoding cal)) "7bit"))
343          (filename
344           (if (and name (not (string-equal name "")))
345               (expand-file-name name
346                                 (save-window-excursion
347                                   (call-interactively
348                                    (function
349                                     (lambda (dir)
350                                       (interactive "DDirectory: ")
351                                       dir)))))
352             (save-window-excursion
353               (call-interactively
354                (function
355                 (lambda (file)
356                   (interactive "FFilename: ")
357                   (expand-file-name file)))))))
358          )
359     (if (file-exists-p filename)
360         (or (yes-or-no-p (format "File %s exists. Save anyway? " filename))
361             (error "")))
362     (re-search-forward "\n\n")
363     (mime-write-decoded-region (match-end 0) end filename encoding)
364     ))
365
366
367 ;;; @ mail/news message
368 ;;;
369
370 (defun mime-preview-quitting-method-for-mime-show-message-mode ()
371   "Quitting method for mime-view.
372 It is registered to variable `mime-preview-quitting-method-alist'."
373   (let ((mother mime-mother-buffer)
374         (win-conf mime-preview-original-window-configuration)
375         )
376     (kill-buffer mime-raw-buffer)
377     (mime-preview-kill-buffer)
378     (set-window-configuration win-conf)
379     (pop-to-buffer mother)
380     ))
381
382 (defun mime-method-to-display-message/rfc822 (beg end cal)
383   (let* ((cnum (mime-raw-point-to-entity-number beg))
384          (new-name (format "%s-%s" (buffer-name) cnum))
385          (mother mime-preview-buffer)
386          (text-decoder
387           (cdr (or (assq major-mode mime-text-decoder-alist)
388                    (assq t mime-text-decoder-alist))))
389          str)
390     (setq str (buffer-substring beg end))
391     (switch-to-buffer new-name)
392     (erase-buffer)
393     (insert str)
394     (goto-char (point-min))
395     (if (re-search-forward "^\n" nil t)
396         (delete-region (point-min) (match-end 0))
397       )
398     (setq major-mode 'mime-show-message-mode)
399     (setq mime-text-decoder text-decoder)
400     (mime-view-mode mother)
401     ))
402
403
404 ;;; @ message/partial
405 ;;;
406
407 (defun mime-raw-write-region (start end filename)
408   "Write current region into specified file.
409 When called from a program, takes three arguments:
410 START, END and FILENAME.  START and END are buffer positions.
411 It refer `mime-raw-buffer-coding-system-alist' to choose coding-system
412 to write."
413   (let ((coding-system-for-write
414          (cdr
415           (or (assq major-mode mime-raw-buffer-coding-system-alist)
416               (assq t mime-raw-buffer-coding-system-alist)
417               ))))
418     (write-region start end filename)
419     ))
420
421 (defun mime-method-to-store-message/partial (beg end cal)
422   (goto-char beg)
423   (let* ((root-dir
424           (expand-file-name
425            (concat "m-prts-" (user-login-name)) mime-temp-directory))
426          (id (cdr (assoc "id" cal)))
427          (number (cdr (assoc "number" cal)))
428          (total (cdr (assoc "total" cal)))
429          file
430          (mother mime-preview-buffer)
431          )
432     (or (file-exists-p root-dir)
433         (make-directory root-dir)
434         )
435     (setq id (replace-as-filename id))
436     (setq root-dir (concat root-dir "/" id))
437     (or (file-exists-p root-dir)
438         (make-directory root-dir)
439         )
440     (setq file (concat root-dir "/FULL"))
441     (if (file-exists-p file)
442         (let ((full-buf (get-buffer-create "FULL"))
443               (pwin (or (get-buffer-window mother)
444                         (get-largest-window)))
445               )
446           (save-window-excursion
447             (set-buffer full-buf)
448             (erase-buffer)
449             (as-binary-input-file (insert-file-contents file))
450             (setq major-mode 'mime-show-message-mode)
451             (mime-view-mode mother)
452             )
453           (set-window-buffer pwin
454                              (save-excursion
455                                (set-buffer full-buf)
456                                mime-preview-buffer))
457           (select-window pwin)
458           )
459       (re-search-forward "^$")
460       (goto-char (1+ (match-end 0)))
461       (setq file (concat root-dir "/" number))
462       (mime-raw-write-region (point) end file)
463       (let ((total-file (concat root-dir "/CT")))
464         (setq total
465               (if total
466                   (progn
467                     (or (file-exists-p total-file)
468                         (save-excursion
469                           (set-buffer
470                            (get-buffer-create mime-temp-buffer-name))
471                           (erase-buffer)
472                           (insert total)
473                           (write-region (point-min)(point-max) total-file)
474                           (kill-buffer (current-buffer))
475                           ))
476                     (string-to-number total)
477                     )
478                 (and (file-exists-p total-file)
479                      (save-excursion
480                        (set-buffer (find-file-noselect total-file))
481                        (prog1
482                            (and (re-search-forward "[0-9]+" nil t)
483                                 (string-to-number
484                                  (buffer-substring (match-beginning 0)
485                                                    (match-end 0)))
486                                 )
487                          (kill-buffer (current-buffer))
488                          )))
489                 )))
490       (if (and total (> total 0))
491           (catch 'tag
492             (save-excursion
493               (set-buffer (get-buffer-create mime-temp-buffer-name))
494               (let ((full-buf (current-buffer)))
495                 (erase-buffer)
496                 (let ((i 1))
497                   (while (<= i total)
498                     (setq file (concat root-dir "/" (int-to-string i)))
499                     (or (file-exists-p file)
500                         (throw 'tag nil)
501                         )
502                     (as-binary-input-file (insert-file-contents file))
503                     (goto-char (point-max))
504                     (setq i (1+ i))
505                     ))
506                 (as-binary-output-file
507                  (write-region (point-min)(point-max)
508                                (expand-file-name "FULL" root-dir)))
509                 (let ((i 1))
510                   (while (<= i total)
511                     (let ((file (format "%s/%d" root-dir i)))
512                       (and (file-exists-p file)
513                            (delete-file file)
514                            ))
515                     (setq i (1+ i))
516                     ))
517                 (let ((file (expand-file-name "CT" root-dir)))
518                   (and (file-exists-p file)
519                        (delete-file file)
520                        ))
521                 (save-window-excursion
522                   (setq major-mode 'mime-show-message-mode)
523                   (mime-view-mode mother)
524                   )
525                 (let ((pwin (or (get-buffer-window mother)
526                                 (get-largest-window)
527                                 ))
528                       (pbuf (save-excursion
529                               (set-buffer full-buf)
530                               mime-preview-buffer)))
531                   (set-window-buffer pwin pbuf)
532                   (select-window pwin)
533                   )))))
534       )))
535
536
537 ;;; @ message/external-body
538 ;;;
539
540 (defvar mime-raw-dired-function
541   (if mime/use-multi-frame
542       (function dired-other-frame)
543     (function mime-raw-dired-function-for-one-frame)
544     ))
545
546 (defun mime-raw-dired-function-for-one-frame (dir)
547   (let ((win (or (get-buffer-window mime-preview-buffer)
548                  (get-largest-window))))
549     (select-window win)
550     (dired dir)
551     ))
552
553 (defun mime-method-to-display-message/external-ftp (beg end cal)
554   (let* ((site (cdr (assoc "site" cal)))
555          (directory (cdr (assoc "directory" cal)))
556          (name (cdr (assoc "name" cal)))
557          ;;(mode (cdr (assoc "mode" cal)))
558          (pathname (concat "/anonymous@" site ":" directory))
559          )
560     (message (concat "Accessing " (expand-file-name name pathname) "..."))
561     (funcall mime-raw-dired-function pathname)
562     (goto-char (point-min))
563     (search-forward name)
564     ))
565
566
567 ;;; @ rot13-47
568 ;;;
569
570 (defun mime-method-to-display-caesar (start end cal)
571   "Internal method for mime-view to display ROT13-47-48 message."
572   (let* ((cnum (mime-raw-point-to-entity-number start))
573          (new-name (format "%s-%s" (buffer-name) cnum))
574          (the-buf (current-buffer))
575          (mother mime-preview-buffer)
576          (charset (cdr (assoc "charset" cal)))
577          (encoding (cdr (assq 'encoding cal)))
578          (mode major-mode)
579          )
580     (let ((pwin (or (get-buffer-window mother)
581                     (get-largest-window)))
582           (buf (get-buffer-create new-name))
583           )
584       (set-window-buffer pwin buf)
585       (set-buffer buf)
586       (select-window pwin)
587       )
588     (setq buffer-read-only nil)
589     (erase-buffer)
590     (insert-buffer-substring the-buf start end)
591     (goto-char (point-min))
592     (if (re-search-forward "^\n" nil t)
593         (delete-region (point-min) (match-end 0))
594       )
595     (let ((m (cdr (or (assq mode mime-text-decoder-alist)
596                       (assq t mime-text-decoder-alist)))))
597       (and (functionp m)
598            (funcall m charset encoding)
599            ))
600     (mule-caesar-region (point-min) (point-max))
601     (set-buffer-modified-p nil)
602     (set-buffer mother)
603     (view-buffer new-name)
604     ))
605
606
607 ;;; @ end
608 ;;;
609
610 (provide 'mime-play)
611
612 (let* ((file mime-acting-situation-examples-file)
613        (buffer (get-buffer-create " *mime-example*")))
614   (if (file-readable-p file)
615       (unwind-protect
616           (save-excursion
617             (set-buffer buffer)
618             (erase-buffer)
619             (insert-file-contents file)
620             (eval-current-buffer)
621             ;; format check
622             (or (eq (car mime-acting-situation-examples) 'type)
623                 (setq mime-acting-situation-examples nil))
624             )
625         (kill-buffer buffer))))
626
627 ;;; mime-play.el ends here