~ubuntu-branches/ubuntu/intrepid/mit-scheme/intrepid-updates

1.1.1 by Chris Hanson
Import upstream version 7.7.90
1
#| -*-Scheme-*-
2
3
$Id: imail-rmail.scm,v 1.73 2003/02/14 18:28:14 cph Exp $
4
5
Copyright 1999-2002 Massachusetts Institute of Technology
6
7
This file is part of MIT/GNU Scheme.
8
9
MIT/GNU Scheme is free software; you can redistribute it and/or modify
10
it under the terms of the GNU General Public License as published by
11
the Free Software Foundation; either version 2 of the License, or (at
12
your option) any later version.
13
14
MIT/GNU Scheme is distributed in the hope that it will be useful, but
15
WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
General Public License for more details.
18
19
You should have received a copy of the GNU General Public License
20
along with MIT/GNU Scheme; if not, write to the Free Software
21
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
22
USA.
23
24
|#
1 by Chris Hanson
Import upstream version 7.7.0
25
26
;;;; IMAIL mail reader: RMAIL back end
27
28
(declare (usual-integrations))
29

30
(define-class <rmail-folder-type> (<file-folder-type>))
31
32
(define-file-folder-type <rmail-folder-type> "Rmail"
1.1.1 by Chris Hanson
Import upstream version 7.7.90
33
  (lambda (pathname)
34
    (check-file-prefix pathname "BABYL OPTIONS:")))
1 by Chris Hanson
Import upstream version 7.7.0
35
36
;;;; Server
37
38
(define-method create-file-folder-file (url (type <rmail-folder-type>))
39
  type
40
  (call-with-binary-output-file (pathname-url-pathname url)
41
    (lambda (port)
42
      (write-rmail-file-header (make-rmail-folder-header-fields '()) port))))
43
44
;;;; Folder
45
46
(define-class (<rmail-folder> (constructor (locator))) (<file-folder>)
47
  (header-fields define standard))
48
49
(define-method rmail-folder-header-fields ((folder <folder>))
50
  (compute-rmail-folder-header-fields folder))
51
52
(define (compute-rmail-folder-header-fields folder)
53
  (make-rmail-folder-header-fields (folder-flags folder)))
54
55
(define (make-rmail-folder-header-fields flags)
56
  (list (make-header-field "Version" "5")
57
	(make-header-field "Labels"
58
			   (decorated-string-append
59
			    "" "," ""
60
			    (flags->rmail-labels flags)))
61
	(make-header-field "Note" "This is the header of an rmail file.")
62
	(make-header-field "Note" "If you are seeing it in rmail,")
63
	(make-header-field "Note" "it means the file has no messages in it.")))
64
65
(define-method %open-file-resource (url (type <rmail-folder-type>))
66
  type
67
  (maybe-make-resource url make-rmail-folder))
68
69
;;;; Message
70
71
(define-class (<rmail-message>
72
	       (constructor (header-fields body flags
73
					   displayed-header-fields
74
					   internal-time)))
75
    (<file-message>)
76
  displayed-header-fields
77
  internal-time)
78
79
(define-generic rmail-message-displayed-header-fields (message))
80
81
(define-file-external-message-method rmail-message-displayed-header-fields
82
  <rmail-message>
83
  'DISPLAYED-HEADER-FIELDS
84
  string->header-fields)
85
86
(define-method rmail-message-displayed-header-fields ((message <message>))
87
  message
88
  'UNDEFINED)
89
90
(let ((accessor (slot-accessor <rmail-message> 'INTERNAL-TIME)))
91
  (define-method message-internal-time ((message <rmail-message>))
92
    (or (accessor message)
93
	(call-next-method message))))
94
95
(define-method make-message-copy ((message <message>) (folder <rmail-folder>))
96
  folder
97
  (make-rmail-message (message-header-fields message)
98
		      (file-message-body message)
99
		      (list-copy (message-flags message))
100
		      (rmail-message-displayed-header-fields message)
101
		      (message-internal-time message)))
102

103
;;;; Read RMAIL file
104
105
(define-method revert-file-folder ((folder <rmail-folder>))
106
  (read-file-folder-contents folder
107
    (lambda (port)
108
      (set-rmail-folder-header-fields! folder (read-rmail-prolog port))
109
      (let loop ((line #f) (index 0) (messages '()))
110
	(if (= 0 (remainder index 100))
111
	    (imail-ui:progress-meter index #f))
112
	(call-with-values (lambda () (read-rmail-message folder port line))
113
	  (lambda (message line)
114
	    (if message
115
		(begin
116
		  (attach-message! message folder index)
117
		  (loop line (+ index 1) (cons message messages)))
118
		(list->vector (reverse! messages)))))))))
119
120
(define (read-rmail-prolog port)
121
  (if (not (rmail-prolog-start-line? (read-required-line port)))
122
      (error "Not an RMAIL file:" port))
123
  (lines->header-fields (read-lines-to-eom port)))
124
125
(define (read-rmail-message folder port read-ahead-line)
126
  (let ((line (or read-ahead-line (read-line port))))
127
    (cond ((eof-object? line)
128
	   (values #f #f))
129
	  ((rmail-prolog-start-line? line)
130
	   (discard-to-eom port)
131
	   (read-rmail-message folder port #f))
132
	  ((rmail-message-start-line? line)
133
	   (values (read-rmail-message-1 folder port) #f))
134
	  ((umail-delimiter? line)
135
	   (read-umail-message folder line port
136
	     (lambda (line)
137
	       (or (rmail-prolog-start-line? line)
138
		   (rmail-message-start-line? line)
139
		   (umail-delimiter? line)))))
140
	  (else
141
	   (error "Malformed RMAIL file:" port)))))
142
143
(define (read-rmail-message-1 folder port)
144
  (call-with-values (lambda () (read-rmail-attributes-line port))
145
    (lambda (formatted? flags)
146
      (let* ((headers (read-rmail-alternate-headers port))
147
	     (displayed-headers (read-rmail-displayed-headers port))
148
	     (body (read-rmail-body port))
149
	     (finish
150
	      (lambda (headers displayed-headers)
151
		(call-with-values
152
		    (lambda ()
153
		      (parse-rmail-internal-time-header folder headers))
154
		  (lambda (headers time)
155
		    (make-rmail-message headers
156
					body
157
					flags
158
					displayed-headers
159
					time))))))
160
	(if formatted?
161
	    (finish headers displayed-headers)
162
	    (finish displayed-headers 'UNDEFINED))))))
163

164
(define (read-rmail-attributes-line port)
165
  (let ((line (read-required-line port)))
166
    (let ((n (string-length line))
167
	  (lose
168
	   (lambda ()
169
	     (error "Malformed RMAIL message-attributes line:" line))))
170
      (if (not (and (fix:>= n 3)
171
		    (char=? (string-ref line 1) #\,)))
172
	  (lose))
173
      (values (cond ((char=? (string-ref line 0) #\0) #f)
174
		    ((char=? (string-ref line 0) #\1) #t)
175
		    (else (lose)))
176
	      (let loop ((i 2) (flags '()) (unseen? #f))
177
		(if (fix:< i n)
178
		    (if (or (char=? (string-ref line i) #\space)
179
			    (char=? (string-ref line i) #\,))
180
			(loop (fix:+ i 1) flags unseen?)
181
			(let scan-token ((i* (fix:+ i 1)))
182
			  (if (or (fix:= i* n)
183
				  (char=? (string-ref line i*) #\space)
184
				  (char=? (string-ref line i*) #\,))
185
			      (let ((flag (substring line i i*)))
186
				(if (string-ci=? flag "unseen")
187
				    (loop i* flags #t)
188
				    (loop i* (cons flag flags) unseen?)))
189
			      (scan-token (fix:+ i* 1)))))
190
		    (if unseen?
191
			(reverse! flags)
192
			(cons "seen" (reverse! flags)))))))))
193
194
(define (read-rmail-alternate-headers port)
195
  (let ((start (xstring-port/position port)))
196
    (make-file-external-ref
197
     start
198
     (let* ((separator rmail-message:headers-separator)
199
	    (s0 (string-ref separator 0))
200
	    (sl (string-length separator)))
201
       (let loop ()
202
	 (let ((char (read-required-char port)))
203
	   (cond ((char=? char #\newline)
204
		  (let ((end (- (xstring-port/position port) 1)))
205
		    (if (not (string=? separator (read-required-line port)))
206
			(error "Missing RMAIL headers-separator string:" port))
207
		    end))
208
		 ((char=? char s0)
209
		  (let ((line (read-required-line port)))
210
		    (if (substring=? line 0 (string-length line)
211
				     separator 1 sl)
212
			(- (xstring-port/position port)
213
			   (+ (string-length line) 1))
214
			(loop))))
215
		 (else
216
		  (skip-to-line-start port)
217
		  (loop)))))))))
218
219
(define (read-rmail-displayed-headers port)
220
  (let ((start (xstring-port/position port)))
221
    (skip-past-blank-line port)
222
    (make-file-external-ref start (- (xstring-port/position port) 1))))
223
224
(define (read-rmail-body port)
225
  (let ((start (xstring-port/position port)))
226
    (input-port/discard-chars port rmail-message:end-char-set)
227
    (input-port/discard-char port)
228
    (make-file-external-ref start (- (xstring-port/position port) 1))))
229
230
(define (parse-rmail-internal-time-header folder headers)
231
  (call-with-values
232
      (lambda () (file-folder-strip-internal-headers folder headers))
233
    (lambda (headers internal-headers)
234
      (values headers
235
	      (let ((v
236
		     (get-first-header-field internal-headers
237
					     "X-IMAIL-INTERNAL-TIME"
238
					     #f)))
239
		(and v
240
		     (parse-header-field-date v)))))))
241

242
;;;; Write RMAIL file
243
244
(define-method write-file-folder ((folder <rmail-folder>) pathname)
245
  (call-with-binary-output-file pathname
246
    (lambda (port)
247
      (write-rmail-file-header (rmail-folder-header-fields folder) port)
248
      (for-each-vector-element (file-folder-messages folder)
249
	(lambda (message)
250
	  (write-rmail-message message port))))))
251
252
(define-method append-message-to-file (message url (type <rmail-folder-type>))
253
  type
254
  (call-with-binary-append-file (pathname-url-pathname url)
255
    (lambda (port)
256
      (write-rmail-message message port))))
257
258
(define (write-rmail-file-header header-fields port)
259
  (write-string "BABYL OPTIONS: -*- rmail -*-" port)
260
  (newline port)
261
  (write-header-fields header-fields port)
262
  (write-char rmail-message:end-char port))
263
264
(define (write-rmail-message message port)
265
  (write-char rmail-message:start-char port)
266
  (newline port)
267
  (let ((headers
268
	 (let ((headers (message-header-fields message))
269
	       (time (message-internal-time message)))
270
	   (if time
271
	       (cons (make-header-field "X-IMAIL-INTERNAL-TIME"
272
					(universal-time->string time))
273
		     headers)
274
	       headers)))
275
	(displayed-headers (rmail-message-displayed-header-fields message)))
276
    (let ((formatted? (not (eq? 'UNDEFINED displayed-headers))))
277
      (write-rmail-attributes-line message formatted? port)
278
      (if formatted? (write-header-fields headers port))
279
      (write-string rmail-message:headers-separator port)
280
      (newline port)
281
      (write-header-fields (if formatted? displayed-headers headers) port)
282
      (write-message-body message port)
283
      (fresh-line port)
284
      (write-char rmail-message:end-char port))))
285
286
(define (write-rmail-attributes-line message formatted? port)
287
  (write-char (if formatted? #\1 #\0) port)
288
  (write-char #\, port)
289
  (call-with-values (lambda () (flags->rmail-markers (message-flags message)))
290
    (lambda (attributes labels)
291
      (let ((write-markers
292
	     (lambda (markers)
293
	       (for-each (lambda (marker)
294
			   (write-char #\space port)
295
			   (write-string marker port)
296
			   (write-char #\, port))
297
			 markers))))
298
	(write-markers attributes)
299
	(write-char #\, port)
300
	(write-markers labels))))
301
  (newline port))
302

303
;;;; Attributes and labels
304
305
(define (rmail-markers->flags attributes labels)
306
  (let loop ((strings (append attributes labels)) (flags '()))
307
    (if (pair? strings)
308
	(loop (cdr strings) (cons (car strings) flags))
309
	(reverse!
310
	 (if (flags-member? "unseen" flags)
311
	     (flags-delete! "unseen" flags)
312
	     (cons "seen" flags))))))
313
314
(define (flags->rmail-markers flags)
315
  (let loop
316
      ((flags
317
	(if (flags-member? "seen" flags)
318
	    (flags-delete "seen" flags)
319
	    (cons "unseen" flags)))
320
       (attributes '())
321
       (labels '()))
322
    (if (pair? flags)
323
	(if (member (car flags) rmail-attributes)
324
	    (loop (cdr flags) (cons (car flags) attributes) labels)
325
	    (loop (cdr flags) attributes (cons (car flags) labels)))
326
	(values (reverse! attributes) (reverse! labels)))))
327
328
(define (flags->rmail-labels flags)
329
  (call-with-values (lambda () (flags->rmail-markers flags))
330
    (lambda (attributes labels)
331
      attributes
332
      labels)))
333
334
;;;; Syntactic Markers
335
336
(define (rmail-prolog-start-line? line)
337
  (string-prefix? "BABYL OPTIONS:" line))
338
339
(define (rmail-prolog-end-line? line)
340
  (string-prefix? "\037" line))
341
342
(define (rmail-message-start-line? line)
343
  (string=? "\f" line))
344
345
(define rmail-message:headers-separator
346
  "*** EOOH ***")
347
348
(define rmail-message:start-char
349
  #\page)
350
351
(define rmail-message:end-char
352
  (integer->char #x1f))
353
354
(define rmail-message:end-char-set
355
  (char-set rmail-message:end-char))
356
357
(define rmail-attributes
358
  '("deleted" "answered" "unseen" "filed" "forwarded" "edited" "resent"))
359
360
(define (read-lines-to-eom port)
361
  (source->list
362
   (lambda ()
363
     (if (eqv? rmail-message:end-char (peek-char port))
364
	 (begin
365
	   (read-char port)		;discard
366
	   (make-eof-object port))
367
	 (read-required-line port)))))
368
369
(define (read-to-eom port)
370
  (let ((string (read-string rmail-message:end-char-set port)))
371
    (if (or (eof-object? string)
372
	    (eof-object? (read-char port)))
373
	(error "EOF while reading RMAIL message body:" port))
374
    string))
375
376
(define (discard-to-eom port)
377
  (input-port/discard-chars port rmail-message:end-char-set)
378
  (input-port/discard-char port))