* mmelmo-imap4-2.el (mmelmo-imap4-get-mime-entity):
[elisp/wanderlust.git] / elmo / mmelmo-imap4-2.el
1 ;;; mmelmo-imap4-1.el -- MM backend of IMAP4 for ELMO (for FLIM 1.13.x).
2
3 ;; Copyright 1998,1999,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 'mmbuffer)
32 (require 'mmelmo)
33 (defvar mmelmo-imap4-threshold nil)
34 (defvar mmelmo-imap4-skipped-parts nil)
35 (defvar mmelmo-imap4-current-message-structure nil)
36
37 ;; Buffer local variable.
38 (defvar mmelmo-imap4-fetched nil)
39 (make-variable-buffer-local 'mmelmo-imap4-fetched)
40
41 (defun mmelmo-imap4-node-id-to-string (node-id)
42   (let ((i (length node-id))
43         result)
44     (while (> i 0)
45       (setq result
46             (concat result
47                     (if result
48                         (concat "." (int-to-string
49                                      (+ 1 (nth (- i 1) node-id))))
50                       (int-to-string (or
51                                       (+ 1 (nth (- i 1) node-id))
52                                       0)))))
53       (setq i (- i 1)))
54     (or result "0")))
55
56 ;; parse IMAP4 body structure entity recursively.
57 (defun mmelmo-imap4-parse-bodystructure-object (folder
58                                                 number msgdb
59                                                 node-id object parent)
60   (cond
61    ((listp (car object));; multipart
62     (let (cur-obj children content-type ret-val (num 0))
63       (setq ret-val
64             (luna-make-entity
65              (mm-expand-class-name 'elmo-imap4)
66              :folder   folder
67              :number   number
68              :msgdb    msgdb
69              :parent   parent
70              :node-id  node-id))
71       (while (and (setq cur-obj (car object))
72                   (listp cur-obj))
73         (setq children
74               (append children
75                       (list
76                        (mmelmo-imap4-parse-bodystructure-object
77                         folder number msgdb
78                         (append (list num) node-id)
79                         cur-obj
80                         ret-val ; myself as parent
81                         ))))
82         (setq num (+ num 1))
83         (setq object (cdr object)))
84       (mime-entity-set-children-internal ret-val children)
85       (setq content-type (list (cons 'type 'multipart)))
86       (if (elmo-imap4-nth 0 object)
87           (setq content-type (append content-type
88                                      (list (cons 'subtype
89                                                  (intern
90                                                   (downcase
91                                                    (elmo-imap4-nth
92                                                     0
93                                                     object))))))))
94       (setq content-type (append content-type
95                                  (mime-parse-parameters-from-list
96                                   (elmo-imap4-nth 1 object))))
97       (mime-entity-set-content-type-internal ret-val content-type)
98       ret-val))
99    (t ;; singlepart
100     (let (content-type ret-val)
101       ;; append size information into location
102       (setq content-type (list (cons 'type (intern (downcase (car object))))))
103       (if (elmo-imap4-nth 1 object)
104           (setq content-type (append content-type
105                                      (list
106                                       (cons 'subtype
107                                             (intern
108                                              (downcase
109                                               (elmo-imap4-nth 1 object))))))))
110       (if (elmo-imap4-nth 2 object)
111           (setq content-type (append content-type
112                                      (mime-parse-parameters-from-list
113                                       (elmo-imap4-nth 2 object)))))
114       (setq ret-val
115             (luna-make-entity
116              (mm-expand-class-name 'elmo-imap4)
117              :folder folder
118              :number number
119              :size (nth 6 object)
120              :content-type content-type
121              :parent parent
122              :node-id node-id))
123       (mime-entity-set-encoding-internal ret-val
124                                          (and (elmo-imap4-nth 5 object)
125                                               (downcase
126                                                (elmo-imap4-nth 5 object))))
127       ret-val))))
128
129 (defun mmelmo-imap4-multipart-p (entity)
130   (eq (cdr (assq 'type (mime-entity-content-type entity))) 'multipart))
131
132 (defun mmelmo-imap4-rfc822part-p (entity)
133   (eq (cdr (assq 'type (mime-entity-content-type entity))) 'rfc822))
134
135 (defun mmelmo-imap4-textpart-p (entity)
136   (eq (cdr (assq 'type (mime-entity-content-type entity))) 'text))
137       
138 (defun mmelmo-imap4-get-mime-entity (folder number msgdb)
139   (let* ((spec (elmo-folder-get-spec folder))
140          (session (elmo-imap4-get-session spec)))
141     (elmo-imap4-session-select-mailbox session (elmo-imap4-spec-mailbox spec))
142     (with-current-buffer (elmo-network-session-buffer session)
143       (setq elmo-imap4-fetch-callback nil)
144       (setq elmo-imap4-fetch-callback-data nil))    
145     (mmelmo-imap4-parse-bodystructure-object
146      folder
147      number
148      msgdb
149      nil ; node-id
150      (elmo-imap4-response-value
151       (elmo-imap4-response-value
152        (elmo-imap4-send-command-wait
153         session
154         (format
155          (if elmo-imap4-use-uid
156              "uid fetch %s bodystructure"
157            "fetch %s bodystructure")
158          number)) 'fetch) 'bodystructure)
159      nil ; parent
160      )))
161
162 (defun mmelmo-imap4-read-part (entity)
163   (if (or (not mmelmo-imap4-threshold)
164           (not (mime-elmo-entity-size-internal entity))
165           (and (mime-elmo-entity-size-internal entity)
166                mmelmo-imap4-threshold
167                (<= (mime-elmo-entity-size-internal entity)
168                    mmelmo-imap4-threshold)))
169       (progn
170         (cond ((mmelmo-imap4-multipart-p entity)) ; noop
171               (t (insert (elmo-imap4-read-part
172                           (mime-elmo-entity-folder-internal entity)
173                           (mime-elmo-entity-number-internal entity)
174                           (mmelmo-imap4-node-id-to-string
175                            (mime-entity-node-id-internal entity))))))
176         (setq mmelmo-imap4-fetched t)
177         (mime-buffer-entity-set-body-start-internal entity (point-min))
178         (mime-buffer-entity-set-body-end-internal entity (point-max)))
179     (setq mmelmo-imap4-fetched nil)
180     (mime-buffer-entity-set-body-start-internal entity (point-min))
181     (mime-buffer-entity-set-body-end-internal entity (point-min))
182     (setq mmelmo-imap4-skipped-parts
183           (append
184            mmelmo-imap4-skipped-parts
185            (list (mmelmo-imap4-node-id-to-string
186                   (mime-entity-node-id-internal entity)))))))
187
188 (defun mmelmo-imap4-insert-body (entity)
189   (mime-buffer-entity-set-body-start-internal entity (- (point) 1))
190   (if (or (not mmelmo-imap4-threshold)
191           (not (mime-elmo-entity-size-internal entity))
192           (and (mime-elmo-entity-size-internal entity)
193                mmelmo-imap4-threshold
194                (<= (mime-elmo-entity-size-internal entity)
195                    mmelmo-imap4-threshold)))
196       (insert (elmo-imap4-read-part
197                (mime-elmo-entity-folder-internal entity)
198                (mime-elmo-entity-number-internal entity) "1"))
199     (setq mmelmo-imap4-skipped-parts
200           (append
201            mmelmo-imap4-skipped-parts
202            (list (mmelmo-imap4-node-id-to-string
203                   (mime-entity-node-id-internal entity)))))))
204
205 ;;; mime-elmo-imap4-entity class definitions.
206 (luna-define-class mime-elmo-imap4-entity (mime-buffer-entity)
207                    (imap folder number msgdb size))
208 (luna-define-internal-accessors 'mime-elmo-imap4-entity)
209
210 (luna-define-method initialize-instance ((entity mime-elmo-imap4-entity)
211                                          &rest init-args)
212   "The initialization method for elmo-imap4.
213 mime-elmo-entity has its own instance variable
214 `imap', `folder', `msgdb', and `size'.
215 These value must be specified as argument for `luna-make-entity'."
216   (apply (car (luna-class-find-functions
217                (luna-find-class 'standard-object)
218                'initialize-instance))
219          entity init-args))
220
221 (defun mmelmo-imap4-mime-entity-buffer (entity)
222   (if (mime-buffer-entity-buffer-internal entity)
223       (save-excursion
224         (set-buffer (mime-buffer-entity-buffer-internal entity))
225         (unless (mime-root-entity-p entity)
226           (unless mmelmo-imap4-fetched
227             (setq mmelmo-imap4-skipped-parts nil) ; No need?
228             (let ((mmelmo-imap4-threshold
229                    (mime-elmo-entity-size-internal entity)))
230               (mime-buffer-entity-set-buffer-internal entity nil)
231               (message "Fetching skipped part...")
232               (mmelmo-imap4-mime-entity-buffer entity)
233               (message "Fetching skipped part...done."))
234             (setq mmelmo-imap4-fetched t)))
235         (mime-buffer-entity-buffer-internal entity))
236     ;; No buffer exist.
237     (save-excursion
238       (set-buffer (get-buffer-create
239                    (concat mmelmo-entity-buffer-name
240                            (mmelmo-imap4-node-id-to-string
241                             (mime-entity-node-id-internal entity)))))
242       (mmelmo-original-mode)
243       (mime-buffer-entity-set-buffer-internal entity (current-buffer))
244       (let ((buffer-read-only nil))
245         (erase-buffer)
246         (mime-entity-node-id entity)
247         (if (mime-root-entity-p entity)
248             (progn
249               ;; root entity
250               (setq mmelmo-imap4-current-message-structure entity)
251               (setq mime-message-structure entity)
252               (setq mmelmo-imap4-skipped-parts nil)
253               ;; insert header
254               (insert (elmo-imap4-read-part
255                        (mime-elmo-entity-folder-internal entity)
256                        (mime-elmo-entity-number-internal entity)
257                        "header"))
258               (mime-buffer-entity-set-header-start-internal
259                entity (point-min))
260               (mime-buffer-entity-set-header-end-internal
261                entity (max (- (point) 1) 1))
262               (if (null (mime-entity-children-internal entity))
263                   (progn
264                     (mime-buffer-entity-set-body-start-internal
265                      entity (point))
266                     ;; insert body if size is OK.
267                     (mmelmo-imap4-insert-body entity)
268                     (mime-buffer-entity-set-body-end-internal
269                      entity (point)))))
270           (setq mime-message-structure
271                 mmelmo-imap4-current-message-structure)
272           (mmelmo-imap4-read-part entity)))
273       (current-buffer))))
274
275 ; mime-entity-children
276 (luna-define-method mime-entity-children ((entity
277                                            mime-elmo-imap4-entity))
278   (mime-entity-children-internal entity))
279
280 ;; override generic function for dynamic body fetching.
281 (luna-define-method mime-entity-body ((entity
282                                        mime-elmo-imap4-entity))
283   (save-excursion
284     (set-buffer (mmelmo-imap4-mime-entity-buffer entity))
285     (buffer-substring (mime-buffer-entity-body-start-internal entity)
286                       (mime-buffer-entity-body-end-internal entity))))
287
288 (luna-define-method mime-entity-content ((entity
289                                           mime-elmo-imap4-entity))
290   (save-excursion
291     (set-buffer (mmelmo-imap4-mime-entity-buffer entity))
292     (mime-decode-string
293      (buffer-substring (mime-buffer-entity-body-start-internal entity)
294                        (mime-buffer-entity-body-end-internal entity))
295      (mime-entity-encoding entity))))
296
297 (luna-define-method mime-entity-fetch-field ((entity mime-elmo-imap4-entity)
298                                              field-name)
299   (save-excursion
300     (save-restriction
301       (when (mime-buffer-entity-buffer-internal entity)
302         (set-buffer (mime-buffer-entity-buffer-internal entity))
303         (if (and (mime-buffer-entity-header-start-internal entity)
304                  (mime-buffer-entity-header-end-internal entity))
305             (progn
306               (narrow-to-region
307                (mime-buffer-entity-header-start-internal entity)
308                (mime-buffer-entity-header-end-internal entity))
309               (std11-fetch-field field-name))
310           nil)))))
311
312 (luna-define-method mime-insert-header ((entity mime-elmo-imap4-entity)
313                                         &optional invisible-fields
314                                         visible-fields)
315   (mmelmo-insert-sorted-header-from-buffer
316    (mmelmo-imap4-mime-entity-buffer entity)
317    (mime-buffer-entity-header-start-internal entity)
318    (mime-buffer-entity-header-end-internal entity)
319    invisible-fields visible-fields))
320
321 (luna-define-method mime-entity-header-buffer ((entity mime-elmo-imap4-entity))
322   (mime-buffer-entity-buffer-internal entity))
323
324 (luna-define-method mime-entity-body-buffer ((entity mime-elmo-imap4-entity))
325   (mime-buffer-entity-buffer-internal entity))
326
327 (luna-define-method mime-write-entity-content ((entity mime-elmo-imap4-entity)
328                                                filename)
329   (let ((mmelmo-imap4-threshold (mime-elmo-entity-size-internal entity)))
330     (if (mime-buffer-entity-buffer-internal entity)
331         (save-excursion
332           (set-buffer (mime-buffer-entity-buffer-internal entity))
333           (unless mmelmo-imap4-fetched
334             (setq mmelmo-imap4-skipped-parts nil) ; No need?
335             (mime-buffer-entity-set-buffer-internal entity nil) ; To re-fetch.
336             ))
337       (unless mmelmo-imap4-fetched
338         (setq mmelmo-imap4-skipped-parts nil) ; No need?
339         (let ((mmelmo-imap4-threshold (mime-elmo-entity-size-internal entity)))
340           (mime-buffer-entity-set-buffer-internal entity nil) ; To re-fetch.
341           (message "Fetching skipped part...")
342           (mime-buffer-entity-set-buffer-internal
343            entity
344            (mmelmo-imap4-mime-entity-buffer entity))
345           (message "Fetching skipped part...done.")))
346       (with-current-buffer (mime-buffer-entity-buffer-internal entity)
347         (mime-write-decoded-region
348          (mime-buffer-entity-body-start-internal entity)
349          (mime-buffer-entity-body-end-internal entity)
350          filename
351          (or (mime-entity-encoding entity) "7bit"))))))
352
353 (provide 'mmelmo-imap4-2)
354
355 ;;; mmelmo-imap4-2.el ends here