Synch up with main trunk and so on.
[elisp/wanderlust.git] / elmo / mmimap.el
1 ;;; mmimap.el --- MIME entity module for IMAP4rev1 (RFC2060).
2 ;;                **** This is EXPERIMENTAL *****
3
4 ;; Copyright (C) 2000 Yuuichi Teranishi <teranisi@gohome.org>
5
6 ;; Author: Yuuichi Teranishi <teranisi@gohome.org>
7 ;; Keywords: IMAP, MIME, multimedia, mail, news
8
9 ;; This file is part of FLIM (Faithful Library about Internet Message).
10
11 ;; This program is free software; you can redistribute it and/or
12 ;; modify it under the terms of the GNU General Public License as
13 ;; published by the Free Software Foundation; either version 2, or (at
14 ;; your option) any later version.
15
16 ;; This program is distributed in the hope that it will be useful, but
17 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 ;; General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
24 ;; Boston, MA 02111-1307, USA.
25
26 ;;; Commentary:
27 ;; 
28
29 ;;; Code:
30
31 (require 'mmgeneric)
32 (require 'mime)
33 (require 'pces)
34 (require 'static)
35 (require 'mime-parse)
36
37 (eval-and-compile
38   (luna-define-class mime-imap-entity (mime-entity)
39                      (size header-string body-string new))
40   (luna-define-internal-accessors 'mime-imap-entity))
41
42 ;;; @ MIME IMAP location
43 ;;    It should contain server, mailbox and uid (sequence number).
44 (eval-and-compile
45   (luna-define-class mime-imap-location () ()))
46
47 (luna-define-generic mime-imap-location-section-body (location section)
48   "Return a body string from LOCATION which corresponds to SECTION.
49 SECTION is a section string which is defined in RFC2060.")
50
51 (luna-define-generic mime-imap-location-bodystructure (location)
52   "Return a parsed bodystructure of LOCATION.
53 `NIL' should be converted to nil, `astring' should be converted to a string.")
54
55 ;;; @ Subroutines
56 ;; 
57
58 (defun mmimap-entity-section (node-id)
59   "Return a section string from NODE-ID"
60   (cond
61    ((numberp node-id)
62     (number-to-string (1+ node-id)))
63    ((listp node-id)
64     (mapconcat 
65      'mmimap-entity-section
66      (reverse node-id)
67      "."))))
68
69 (static-if (fboundp 'mime-decode-parameters)
70     (defalias 'mmimap-parse-parameters-from-list 'mime-decode-parameters)
71   (defun mmimap-parse-parameters-from-list (attrlist)
72     "Parse parameters from ATTRLIST."
73     (let (ret-val)
74       (while attrlist
75         (setq ret-val (append ret-val
76                               (list (cons (downcase (car attrlist))
77                                           (car (cdr attrlist))))))
78         (setq attrlist (cdr (cdr attrlist))))
79       ret-val)))
80
81 (defun mmimap-make-mime-entity (bodystructure class location node-id number
82                                               parent)
83   "Analyze parsed IMAP4 BODYSTRUCTURE response and make MIME entity.
84 CLASS, LOCATION, NODE-ID, PARENT are set to the returned entity."
85   (cond
86    ((listp (car bodystructure)) ; multipart
87     (let ((num 0)
88           curp children content-type entity)
89       (setq entity
90             (luna-make-entity
91              class
92              :new      t
93              :parent   parent
94              :location location
95              :node-id (if (eq number 0)
96                           node-id
97                         (nconc (list number) node-id))
98              ))
99       (while (and (setq curp (car bodystructure))
100                   (listp curp))
101         (setq children
102               (nconc children
103                      (list
104                       (mmimap-make-mime-entity curp class
105                                                location
106                                                (if (eq number 0)
107                                                    node-id
108                                                  (nconc (list number) node-id))
109                                                num
110                                                entity))))
111         (setq num (+ num 1))
112         (setq bodystructure (cdr bodystructure)))
113       (mime-entity-set-children-internal entity children)
114       (setq content-type (list (cons 'type 'multipart)))
115       (if (car bodystructure)
116           (setq content-type (nconc content-type
117                                     (list (cons 'subtype
118                                                 (intern
119                                                  (downcase
120                                                   (car
121                                                    bodystructure))))))))
122       (setq content-type (append content-type
123                                  (mmimap-parse-parameters-from-list
124                                   (nth 1 bodystructure))))
125       (mime-entity-set-content-type-internal entity content-type)
126       entity))
127    (t ; singlepart
128     (let (content-type entity)
129       (setq content-type
130             (list (cons 'type (intern (downcase (car bodystructure))))))
131       (if (nth 1 bodystructure)
132           (setq content-type (append content-type
133                                      (list
134                                       (cons 'subtype
135                                             (intern
136                                              (downcase
137                                               (nth 1 bodystructure))))))))
138       (if (nth 2 bodystructure)
139           (setq content-type (append content-type
140                                      (mmimap-parse-parameters-from-list
141                                       (nth 2 bodystructure)))))
142       (setq node-id (nconc (list number) node-id))
143       (setq entity
144             (luna-make-entity
145              class
146              :new  t
147              :size (nth 6 bodystructure)
148              :content-type content-type
149              :location location
150              :parent parent
151              :node-id node-id))
152       (mime-entity-set-content-type-internal entity content-type)
153       (mime-entity-set-encoding-internal entity
154                                          (and (nth 5 bodystructure)
155                                               (downcase
156                                                (nth 5 bodystructure))))
157       (if (and (nth 7 bodystructure)
158                (nth 8 bodystructure))  ; children.
159           (mime-entity-set-children-internal
160            entity
161            (list (mmimap-make-mime-entity
162                   (nth 8 bodystructure) class
163                   location node-id 0
164                   entity))))
165       entity))))
166
167 (luna-define-method initialize-instance :after ((entity mime-imap-entity)
168                                                 &rest init-args)
169   ;; To prevent infinite loop...
170   (if (mime-imap-entity-new-internal entity)
171       entity
172     (mmimap-make-mime-entity
173      (mime-imap-location-bodystructure 
174       (mime-entity-location-internal entity))
175      (luna-class-name entity)
176      (mime-entity-location-internal entity)
177      nil 0 nil)))
178
179 ;;; @ entity
180 ;;
181
182 (luna-define-method mime-insert-entity ((entity mime-imap-entity))
183   (if (mime-root-entity-p entity)
184       (progn
185         (insert (mime-imap-entity-header-string entity))
186         (mime-insert-entity-body entity))
187     ;; Insert body if it is not a multipart.
188     (unless (eq (mime-content-type-primary-type
189                  (mime-entity-content-type entity))
190                 'multipart)
191       (mime-insert-entity-body entity))))
192
193 (luna-define-method mime-write-entity ((entity mime-imap-entity) filename)
194   (with-temp-buffer
195     (mime-insert-entity entity)
196     (write-region-as-raw-text-CRLF (point-min) (point-max) filename)))
197
198 ;;; @ entity body
199 ;;
200
201 (luna-define-method mime-entity-body ((entity mime-imap-entity))
202   (or (mime-imap-entity-body-string-internal entity)
203       (mime-imap-entity-set-body-string-internal
204        entity
205        (mime-imap-location-section-body
206         (mime-entity-location-internal entity)
207         (mmimap-entity-section
208          (mime-entity-node-id-internal entity))))))
209
210 (luna-define-method mime-insert-entity-body ((entity mime-imap-entity))
211   (insert (mime-entity-body entity)))
212
213 (luna-define-method mime-write-entity-body ((entity mime-imap-entity)
214                                             filename)
215   (with-temp-buffer
216     (mime-insert-entity-body entity)
217     (write-region-as-binary (point-min) (point-max) filename)))
218
219 ;;; @ entity content
220 ;;
221
222 (luna-define-method mime-entity-content ((entity mime-imap-entity))
223   (let ((ret (mime-entity-body entity)))
224     (if ret
225         (mime-decode-string ret (mime-entity-encoding entity))
226       (message "Cannot decode content.")
227       nil)))
228
229 (luna-define-method mime-insert-entity-content ((entity mime-imap-entity))
230   (insert (mime-entity-content entity)))
231
232 (luna-define-method mime-write-entity-content ((entity mime-imap-entity)
233                                                filename)
234   (with-temp-buffer
235     (mime-insert-entity-body entity)
236     (mime-write-decoded-region (point-min) (point-max)
237                                filename
238                                (or (mime-entity-encoding entity) "7bit"))))
239
240 ;;; @ header field
241 ;;
242
243 (defun mime-imap-entity-header-string (entity)
244   (or (mime-imap-entity-header-string-internal entity)
245       (mime-imap-entity-set-header-string-internal
246        entity
247        (mime-imap-location-section-body
248         (mime-entity-location-internal entity)
249         (if (if (eq (car (mime-entity-node-id-internal entity)) 0)
250                 (cdr (mime-entity-node-id-internal entity))
251               (mime-entity-node-id-internal entity))
252             (concat (mmimap-entity-section
253                      (if (eq (car (mime-entity-node-id-internal entity)) 0)
254                          (cdr (mime-entity-node-id-internal entity))
255                        (mime-entity-node-id-internal entity)))
256                     ".HEADER")
257           "HEADER")))))
258
259 (luna-define-method mime-entity-fetch-field :around
260   ((entity mime-imap-entity) field-name)
261   (if (mime-root-entity-p entity) 
262       (or (luna-call-next-method)
263           (with-temp-buffer
264             (insert (mime-imap-entity-header-string entity))
265             (let ((ret (std11-fetch-field field-name)))
266               (when ret
267                 (or (symbolp field-name)
268                     (setq field-name
269                           (intern (capitalize (capitalize field-name)))))
270                 (mime-entity-set-original-header-internal
271                  entity
272                  (put-alist field-name ret
273                             (mime-entity-original-header-internal entity)))
274                 ret))))))
275
276 (luna-define-method mime-insert-header ((entity mime-imap-entity)
277                                         &optional invisible-fields
278                                         visible-fields)
279   (let ((the-buf (current-buffer))
280         buf p-min p-max)
281     (with-temp-buffer
282       (insert (mime-imap-entity-header-string entity))
283       (setq buf (current-buffer)
284             p-min (point-min)
285             p-max (point-max))
286       (set-buffer the-buf)
287       (mime-insert-header-from-buffer buf p-min p-max
288                                       invisible-fields visible-fields))))
289
290 ;;; @ end
291 ;;
292
293 (provide 'mmimap)
294
295 ;;; mmimap.el ends here