* elmo.el (toplevel): Require 'elmo-version first;
[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
35 (eval-and-compile
36   (luna-define-class mime-imap-entity (mime-entity)
37                      (size header-string body-string new))
38   (luna-define-internal-accessors 'mime-imap-entity))
39
40 ;;; @ MIME IMAP location
41 ;;    It should contain server, mailbox and uid (sequence number).
42 (eval-and-compile
43   (luna-define-class mime-imap-location () ()))
44
45 (luna-define-generic mime-imap-location-section-body (location section)
46   "Return a body string from LOCATION which corresponds to SECTION.
47 SECTION is a section string which is defined in RFC2060.")
48
49 (luna-define-generic mime-imap-location-bodystructure (location)
50   "Return a parsed bodystructure of LOCATION.
51 `NIL' should be converted to nil, `astring' should be converted to a string.")
52
53 ;;; @ Subroutines
54 ;; 
55
56 (defun mmimap-entity-section (node-id)
57   "Return a section string from NODE-ID"
58   (cond
59    ((numberp node-id)
60     (number-to-string (1+ node-id)))
61    ((listp node-id)
62     (mapconcat 
63      'mmimap-entity-section
64      (reverse node-id)
65      "."))))
66
67 (defun mmimap-parse-parameters-from-list (attrlist)
68   "Parse parameters from ATTRLIST."
69   (let (ret-val)
70     (while attrlist
71       (setq ret-val (append ret-val
72                             (list (cons (downcase (car attrlist))
73                                         (car (cdr attrlist))))))
74       (setq attrlist (cdr (cdr attrlist))))
75     ret-val))
76
77 (defun mmimap-make-mime-entity (bodystructure class location node-id parent)
78   "Analyze parsed IMAP4 BODYSTRUCTURE response and make MIME entity.
79 CLASS, LOCATION, NODE-ID, PARENT are set to the returned entity."
80   (cond
81    ((listp (car bodystructure)) ; multipart
82     (let ((num 0)
83           curp children content-type entity)
84       (setq entity
85             (luna-make-entity
86              class
87              :new      t
88              :parent   parent
89              :location location
90              :node-id  node-id))
91       (while (and (setq curp (car bodystructure))
92                   (listp curp))
93         (setq children
94               (nconc children
95                      (list
96                       (mmimap-make-mime-entity curp class
97                                                location
98                                                (nconc (list num) node-id)
99                                                entity))))
100         (setq num (+ num 1))
101         (setq bodystructure (cdr bodystructure)))
102       (mime-entity-set-children-internal entity children)
103       (setq content-type (list (cons 'type 'multipart)))
104       (if (car bodystructure)
105           (setq content-type (nconc content-type
106                                     (list (cons 'subtype
107                                                 (intern
108                                                  (downcase
109                                                   (car
110                                                    bodystructure))))))))
111       (setq content-type (append content-type
112                                  (mmimap-parse-parameters-from-list
113                                   (nth 1 bodystructure))))
114       (mime-entity-set-content-type-internal entity content-type)
115       entity))
116    (t ; singlepart
117     (let (content-type entity)
118       (setq content-type
119             (list (cons 'type (intern (downcase (car bodystructure))))))
120       (if (nth 1 bodystructure)
121           (setq content-type (append content-type
122                                      (list
123                                       (cons 'subtype
124                                             (intern
125                                              (downcase
126                                               (nth 1 bodystructure))))))))
127       (if (nth 2 bodystructure)
128           (setq content-type (append content-type
129                                      (mmimap-parse-parameters-from-list
130                                       (nth 2 bodystructure)))))
131       (setq entity
132             (luna-make-entity
133              class
134              :new  t
135              :size (nth 6 bodystructure)
136              :content-type content-type
137              :location location
138              :parent parent
139              :node-id node-id))
140       (mime-entity-set-content-type-internal entity content-type)
141       (mime-entity-set-encoding-internal entity
142                                          (and (nth 5 bodystructure)
143                                               (downcase
144                                                (nth 5 bodystructure))))
145       (if (and (nth 7 bodystructure)
146                (nth 8 bodystructure))  ; children.
147           (mime-entity-set-children-internal
148            entity
149            (list (mmimap-make-mime-entity
150                   (nth 8 bodystructure) class
151                   location node-id
152                   entity))))
153       entity))))
154
155 (luna-define-method initialize-instance :after ((entity mime-imap-entity)
156                                                 &rest init-args)
157   ;; To prevent infinite loop...
158   (if (mime-imap-entity-new-internal entity)
159       entity
160     (mmimap-make-mime-entity
161      (mime-imap-location-bodystructure 
162       (mime-entity-location-internal entity))
163      (luna-class-name entity)
164      (mime-entity-location-internal entity)
165      nil nil)))
166
167 ;;; @ entity
168 ;;
169
170 (luna-define-method mime-insert-entity ((entity mime-imap-entity))
171   ;; Root entity.
172   (if (mime-root-entity-p entity)
173       (progn
174         (insert (mime-imap-entity-header-string entity))
175         (mime-insert-entity-body entity))
176     ;; Insert body if it is not a multipart.
177     (unless (eq (mime-content-type-primary-type
178                  (mime-entity-content-type entity))
179                 'multipart)
180       (mime-insert-entity-body entity))))
181
182 (luna-define-method mime-write-entity ((entity mime-imap-entity) filename)
183   (with-temp-buffer
184     (mime-insert-entity entity)
185     (write-region-as-raw-text-CRLF (point-min) (point-max) filename)))
186
187 ;;; @ entity body
188 ;;
189
190 (luna-define-method mime-entity-body ((entity mime-imap-entity))
191   (or (mime-imap-entity-body-string-internal entity)
192       (mime-imap-entity-set-body-string-internal
193        entity
194        (mime-imap-location-section-body
195         (mime-entity-location-internal entity)
196         (mmimap-entity-section
197          (mime-entity-node-id-internal entity))))))
198
199 (luna-define-method mime-insert-entity-body ((entity mime-imap-entity))
200   (insert (mime-entity-body entity)))
201
202 (luna-define-method mime-write-entity-body ((entity mime-imap-entity)
203                                             filename)
204   (with-temp-buffer
205     (mime-insert-entity-body entity)
206     (write-region-as-binary (point-min) (point-max) filename)))
207
208 ;;; @ entity content
209 ;;
210
211 (luna-define-method mime-entity-content ((entity mime-imap-entity))
212   (let ((ret (mime-entity-body entity)))
213     (if ret
214         (mime-decode-string ret (mime-entity-encoding entity))
215       (message "Cannot decode content.")
216       nil)))
217
218 (luna-define-method mime-insert-entity-content ((entity mime-imap-entity))
219   (insert (mime-entity-content entity)))
220
221 (luna-define-method mime-write-entity-content ((entity mime-imap-entity)
222                                                filename)
223   (with-temp-buffer
224     (mime-insert-entity-body entity)
225     (mime-write-decoded-region (point-min) (point-max)
226                                filename
227                                (or (mime-entity-encoding entity) "7bit"))))
228
229 ;;; @ header field
230 ;;
231
232 (defun mime-imap-entity-header-string (entity)
233   (or (mime-imap-entity-header-string-internal entity)
234       (mime-imap-entity-set-header-string-internal
235        entity
236        (mime-imap-location-section-body
237         (mime-entity-location-internal entity)
238         (if (mime-entity-node-id-internal entity)
239             (concat (mmimap-entity-section
240                      (mime-entity-node-id-internal entity))
241                     ".HEADER")
242           "HEADER")))))
243
244 (luna-define-method mime-entity-fetch-field :around
245   ((entity mime-imap-entity) field-name)
246   (if (mime-root-entity-p entity) 
247       (or (luna-call-next-method)
248           (with-temp-buffer
249             (insert (mime-imap-entity-header-string entity))
250             (let ((ret (std11-fetch-field field-name)))
251               (when ret
252                 (or (symbolp field-name)
253                     (setq field-name
254                           (intern (capitalize (capitalize field-name)))))
255                 (mime-entity-set-original-header-internal
256                  entity
257                  (put-alist field-name ret
258                             (mime-entity-original-header-internal entity)))
259                 ret))))))
260
261 (luna-define-method mime-insert-header ((entity mime-imap-entity)
262                                         &optional invisible-fields
263                                         visible-fields)
264   (let ((the-buf (current-buffer))
265         buf p-min p-max)
266     (with-temp-buffer
267       (insert (mime-imap-entity-header-string entity))
268       (setq buf (current-buffer)
269             p-min (point-min)
270             p-max (point-max))
271       (set-buffer the-buf)
272       (mime-insert-header-from-buffer buf p-min p-max
273                                       invisible-fields visible-fields))))
274
275 ;;; @ end
276 ;;
277
278 (provide 'mmimap)
279
280 ;;; mmimap.el ends here