* elmo.el (elmo-message-match-condition): Use elmo-message-fetch
[elisp/wanderlust.git] / elmo / elmo-file.el
1 ;;; elmo-file.el --- File interface for ELMO.
2
3 ;; Copyright (C) 2000 Yuuichi Teranishi <teranisi@gohome.org>
4
5 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
6 ;; Keywords: mail, net news
7
8 ;; This file is part of ELMO (Elisp Library for Message Orchestration).
9
10 ;; This program is free software; you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation; either version 2, or (at your option)
13 ;; any later version.
14 ;;
15 ;; This program is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 ;; GNU General Public License for more details.
19 ;;
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
22 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 ;; Boston, MA 02111-1307, USA.
24 ;;
25
26 ;;; Commentary:
27 ;;
28
29 ;;; Code:
30 ;;
31 (require 'elmo)
32 (require 'elmo-map)
33 (require 'mime-edit)
34
35 (defun elmo-file-find (files)
36   "Return the first existing filename in the FILES."
37   (let (file)
38     (while files
39       (when (file-exists-p (car files))
40         (setq file (car files)
41               files nil))
42       (setq files (cdr files)))
43     file))
44
45 (defcustom elmo-file-command (exec-installed-p "file")
46   "*Program name of the file type detection command `file'."
47   :type '(string :tag "Program name of the file")
48   :group 'elmo)
49
50 (defcustom elmo-file-command-argument
51   `("-m"
52     ,(elmo-file-find
53       '("/usr/share/magic.mime"
54         "/usr/share/file/magic.mime"
55         "/cygwin/usr/share/file/magic.mime")))
56   "*Argument list for the `file' command.
57 \(It should return the MIME content type\)"
58   :type '(repeat string)
59   :group 'elmo)
60
61 (defcustom elmo-file-fetch-max-size (* 1024 1024)
62   "*Max size of the message fetching."
63   :type 'integer
64   :group 'elmo)
65
66 (eval-and-compile
67   (luna-define-class elmo-file-folder (elmo-map-folder) (file-path))
68   (luna-define-internal-accessors 'elmo-file-folder))
69
70 (luna-define-method elmo-folder-initialize ((folder
71                                              elmo-file-folder)
72                                             name)
73   (elmo-file-folder-set-file-path-internal folder name)
74   folder)
75
76 (luna-define-method elmo-folder-expand-msgdb-path ((folder
77                                                     elmo-file-folder))
78   (expand-file-name
79    (elmo-replace-string-as-filename (elmo-folder-name-internal folder))
80    (expand-file-name "file" elmo-msgdb-directory)))
81
82 (defun elmo-file-make-date-string (attrs)
83   (let ((s (current-time-string (nth 5 attrs))))
84     (string-match "\\`\\([A-Z][a-z][a-z]\\) +[A-Z][a-z][a-z] +[0-9][0-9]? *[0-9][0-9]?:[0-9][0-9]:[0-9][0-9] *[0-9]?[0-9]?[0-9][0-9]"
85                   s)
86     (concat (elmo-match-string 1 s) ", "
87             (timezone-make-date-arpa-standard s (current-time-zone)))))
88
89 (defun elmo-file-detect-format (file)
90   "Return content-type of the FILE."
91   (if (or (not (file-exists-p file))
92           (file-directory-p file))
93       "application/octet-stream"
94     (let (type)
95       (setq type (mime-find-file-type file))
96       (if (and (string= (nth 0 type) "application")
97                (string= (nth 1 type) "octet-stream"))
98           (if elmo-file-command
99               (with-temp-buffer
100                 (when
101                     (zerop (apply 'call-process elmo-file-command
102                                   nil `(,(current-buffer) nil)
103                                   nil (append elmo-file-command-argument
104                                               (list (expand-file-name file)))))
105                   (goto-char (point-min))
106                   (when (re-search-forward ": *" nil t)
107                     (setq type (buffer-substring (match-end 0)
108                                                  (point-at-eol))))
109                   (cond
110                    ((string= "empty" type)
111                     "application/octet-stream")
112                    ((string-match "text" type)
113                     "text/plain")
114                    (t
115                     (car (split-string type))))))
116             (concat (nth 0 type) "/" (nth 1 type)))
117         (concat (nth 0 type) "/" (nth 1 type))))))
118
119 (defun elmo-file-msgdb-create-entity (msgdb folder number)
120   "Create msgdb entity for the message in the FOLDER with NUMBER."
121   (let* ((file (elmo-message-file-name folder number))
122          (attrs (file-attributes file)))
123     (and (not (file-directory-p file))
124          attrs
125          (elmo-msgdb-make-message-entity
126           (elmo-msgdb-message-entity-handler msgdb)
127           :message-id (concat "<" (elmo-replace-in-string
128                                    file "/" ":")
129                               "@" (system-name))
130           :number number
131           :size (nth 7 attrs)
132           :date (elmo-file-make-date-string attrs)
133           :subject (file-name-nondirectory file)
134           :from (concat (user-full-name (nth 2 attrs))
135                         " <" (user-login-name (nth 2 attrs)) "@"
136                         (system-name) ">")))))
137
138 (luna-define-method elmo-folder-msgdb-create ((folder elmo-file-folder)
139                                               numlist flag-table)
140   (let ((new-msgdb (elmo-make-msgdb))
141         entity mark i percent num)
142     (setq num (length numlist))
143     (setq i 0)
144     (message "Creating msgdb...")
145     (while numlist
146       (setq entity
147             (elmo-file-msgdb-create-entity new-msgdb folder (car numlist)))
148       (when entity
149         (elmo-msgdb-append-entity new-msgdb entity '(new unread)))
150       (when (> num elmo-display-progress-threshold)
151         (setq i (1+ i))
152         (setq percent (/ (* i 100) num))
153         (elmo-display-progress
154          'elmo-folder-msgdb-create "Creating msgdb..."
155          percent))
156       (setq numlist (cdr numlist)))
157     (message "Creating msgdb...done")
158     new-msgdb))
159
160 (luna-define-method elmo-folder-message-file-p ((folder elmo-file-folder))
161   t)
162
163 (luna-define-method elmo-message-file-name ((folder elmo-file-folder)
164                                             number)
165   (expand-file-name (car (split-string
166                           (elmo-map-message-location folder number)
167                           "/"))
168                     (elmo-file-folder-file-path-internal folder)))
169
170 (luna-define-method elmo-folder-message-make-temp-file-p
171   ((folder elmo-file-folder))
172   t)
173
174 (luna-define-method elmo-folder-diff ((folder elmo-file-folder))
175   (cons nil nil))
176
177 (luna-define-method elmo-folder-message-make-temp-files ((folder
178                                                           elmo-file-folder)
179                                                          numbers
180                                                          &optional
181                                                          start-number)
182   (let ((temp-dir (elmo-folder-make-temporary-directory folder))
183         (cur-number (if start-number 0)))
184     (dolist (number numbers)
185       (elmo-copy-file
186        (elmo-message-file-name folder number)
187        (expand-file-name
188         (int-to-string (if start-number (incf cur-number) number))
189         temp-dir)))
190     temp-dir))
191
192 (luna-define-method elmo-map-message-fetch ((folder elmo-file-folder)
193                                             location strategy
194                                             &optional section unseen)
195   (let ((file (expand-file-name (car (split-string location "/"))
196                                 (elmo-file-folder-file-path-internal folder)))
197         charset guess uid is-text)
198     (when (file-exists-p file)
199       (set-buffer-multibyte nil)
200       (prog1
201           (insert-file-contents-as-binary file nil 0 elmo-file-fetch-max-size)
202         (unless (or (std11-field-body "To")
203                     (std11-field-body "Cc")
204                     (std11-field-body "Subject"))
205           (setq guess (elmo-file-detect-format file))
206           (setq is-text (string-match "^text/" guess))
207           (when is-text
208             (set-buffer-multibyte t)
209             (decode-coding-region
210              (point-min) (point-max)
211              elmo-mime-display-as-is-coding-system)
212             (setq charset (detect-mime-charset-region (point-min)
213                                                       (point-max))))
214           (goto-char (point-min))
215           (setq uid (nth 2 (file-attributes file)))
216           (insert "From: " (concat (user-full-name uid)
217                                    " <"(user-login-name uid) "@"
218                                    (system-name) ">") "\n")
219           (insert "Subject: " (file-name-nondirectory file) "\n")
220           (insert "Date: "
221                   (elmo-file-make-date-string (file-attributes file))
222                   "\n")
223           (insert "Message-ID: "
224                   (concat "<" (elmo-replace-in-string file "/" ":")
225                           "@" (system-name) ">\n"))
226           (insert "Content-Type: "
227                   guess
228                   (or (and is-text
229                            (concat
230                             "; charset=" (upcase (symbol-name charset))))
231                       "")
232                   "\nMIME-Version: 1.0\n\n")
233           (when is-text
234             (encode-mime-charset-region (point-min) (point-max) charset))
235           (set-buffer-multibyte nil))))))
236
237 (luna-define-method elmo-map-folder-list-message-locations
238   ((folder elmo-file-folder))
239   (delq nil
240         (mapcar
241          (lambda (file)
242            (when (not (file-directory-p file))
243              (concat
244               file "/"
245               (mapconcat
246                'number-to-string
247                (nth 5 (file-attributes (expand-file-name
248                                         file
249                                         (elmo-file-folder-file-path-internal
250                                          folder))))
251                ":"))))
252          (directory-files (elmo-file-folder-file-path-internal folder)))))
253
254 (luna-define-method elmo-folder-exists-p ((folder elmo-file-folder))
255   (file-directory-p (elmo-file-folder-file-path-internal folder)))
256
257 (luna-define-method elmo-folder-list-subfolders ((folder elmo-file-folder)
258                                                  &optional one-level)
259   (when (file-directory-p (elmo-file-folder-file-path-internal folder))
260     (append
261      (list (elmo-folder-name-internal folder))
262      (delq nil
263            (mapcar
264             (lambda (file)
265               (when (and (file-directory-p
266                           (expand-file-name
267                            file
268                            (elmo-file-folder-file-path-internal folder)))
269                          (not (string= file "."))
270                          (not (string= file "..")))
271                 (concat (elmo-folder-name-internal folder) "/" file)))
272             (directory-files (elmo-file-folder-file-path-internal
273                               folder)))))))
274
275 (require 'product)
276 (product-provide (provide 'elmo-file) (require 'elmo-version))
277
278 ;;; elmo-file.el ends here