Commit be88df89 be88df89923a4dcb46e90b360ad3d06481d4f816 by Sergey Poznyakoff

Sieve core functions for scripts generated by sieve.scm.

1 parent 1965e71c
1 ;;;; GNU mailutils - a suite of utilities for electronic mail
2 ;;;; Copyright (C) 1999, 2000, 2001 Free Software Foundation, Inc.
3 ;;;;
4 ;;;; This program is free software; you can redistribute it and/or modify
5 ;;;; it under the terms of the GNU General Public License as published by
6 ;;;; the Free Software Foundation; either version 2, or (at your option)
7 ;;;; any later version.
8 ;;;;
9 ;;;; This program is distributed in the hope that it will be useful,
10 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 ;;;; GNU General Public License for more details.
13 ;;;;
14 ;;;; You should have received a copy of the GNU General Public License
15 ;;;; along with this program; if not, write to the Free Software
16 ;;;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 ;;;; This module provides core functionality for the sieve scripts.
19
20 ;;; Comparators
21 (cond
22 (sieve-parser
23 (sieve-register-comparator "i;octet" string=?)
24 (sieve-register-comparator "i;ascii-casemap" string-ci=?)))
25
26 ;;; Stop statement
27
28 (define (sieve-stop)
29 (exit))
30
31 ;;; Basic five actions:
32
33 ;;; reject
34
35 (define sieve-option-quote #f)
36 (define sieve-indent-prefix "\t")
37
38 (define (action-reject reason)
39 (let* ((out-msg (mu-message-create))
40 (outbody (mu-message-get-body out-msg))
41 (inbody (mu-message-get-body sieve-current-message)))
42 (mu-message-set-header out-msg "To"
43 (mu-message-get-header in-msg "From"))
44 (mu-message-set-header out-msg "Cc"
45 (mu-message-get-header in-msg "Cc"))
46 (mu-message-set-header out-msg "Subject"
47 (string-append
48 "Re: "
49 (mu-message-get-header in-msg "Subject")))
50 (mu-body-write outbody reason)
51
52 (cond
53 (sieve-option-quote
54 (mu-body-write outbody "\n\nOriginal message:\n")
55 (do ((hdr (mu-message-get-header-fields sieve-current-message)
56 (cdr hdr)))
57 ((null? hdr) #f)
58 (let ((s (car hdr)))
59 (mu-body-write outbody (string-append
60 sieve-indent-prefix
61 (car s) ": " (cdr s) "\n"))))
62 (mu-body-write outbody (string-append indent-prefix "\n"))
63 (do ((line (mu-body-read-line inbody) (mu-body-read-line inbody)))
64 ((eof-object? line) #f)
65 (mu-body-write outbody (string-append sieve-indent-prefix line)))))
66
67 (mu-message-send out-msg)))
68
69 ;;; fileinto
70
71 (define (action-fileinto filename)
72 (let ((outbox (mu-mailbox-open filename "cw")))
73 (cond
74 (outbox
75 (mu-mailbox-append-message outbox sieve-current-message)
76 (mu-mailbox-close outbox)
77 (mu-message-delete sieve-current-message)))))
78
79 ;;; redirect is defined in redirect.scm
80
81 ;;; keep -- does nothing worth mentioning :^)
82
83 ;;; discard
84
85 (define (action-discard)
86 (mu-message-delete sieve-current-message))
87
88 ;;; Register standard actions
89 (cond
90 (sieve-parser
91 (sieve-register-action "keep" #f)
92 (sieve-register-action "discard" action-discard)
93 (sieve-register-action "reject" action-reject 'string)
94 (sieve-register-action "fileinto" action-fileinto 'string)))
95
96
97 ;;; Some utilities.
98
99 (define (find-comp opt-args)
100 (cond
101 ((member #:comparator opt-args) =>
102 (lambda (x)
103 (car (cdr x))))
104 (else
105 string-ci=?)))
106
107 (define (find-match opt-args)
108 (cond
109 ((member #:is opt-args)
110 #:is)
111 ((member #:contains opt-args)
112 #:contains)
113 ((member #:matches opt-args)
114 #:matches)
115 (else
116 #:is)))
117
118 (define (sieve-str-str str key comp)
119 (let* ((char (string-ref key 0))
120 (str-len (string-length str))
121 (key-len (string-length key))
122 (limit (- str-len key-len)))
123 (if (< limit 0)
124 #f
125 (call-with-current-continuation
126 (lambda (xx)
127 (do ((index 0 (1+ index)))
128 ((cond
129 ((> index limit)
130 #t)
131 ;; FIXME: This is very inefficient, but I have to use this
132 ;; provided (string-index str (string-ref key 0)) may not
133 ;; work...
134 ((comp (make-shared-substring str index (+ index key-len))
135 key)
136 (xx #t))
137 (else
138 #f)) #f))
139 #f)))))
140
141 ;;; Convert sieve-style regexps to POSIX:
142
143 (define (sieve-regexp-to-posix regexp)
144 (let ((length (string-length regexp)))
145 (do ((cl '())
146 (escape #f)
147 (i 0 (1+ i)))
148 ((= i length) (list->string (reverse cl)))
149 (let ((ch (string-ref regexp i)))
150 (cond
151 (escape
152 (set! cl (append (list ch) cl))
153 (set! escape #f))
154 ((char=? ch #\\)
155 (set! escape #t))
156 ((char=? ch #\?)
157 (set! cl (append (list #\.) cl)))
158 ((char=? ch #\*)
159 (set! cl (append (list #\* #\.) cl)))
160 (else
161 (set! cl (append (list ch) cl))))))))
162
163 ;;;; Standard tests:
164
165
166 (define (test-address header-list key-list . opt-args)
167 (let ((comp (find-comp opt-args))
168 (match (find-match opt-args))
169 (part (cond
170 ((member #:localpart opt-args)
171 #:localpart)
172 ((member #:domain opt-args)
173 #:domain)
174 (else
175 #:all))))
176 (call-with-current-continuation
177 (lambda (exit)
178 (for-each
179 (lambda (key)
180 (let ((rx (if (eq? match #:matches)
181 (make-regexp (sieve-regexp-to-posix key)
182 (if (eq? comp string-ci=?)
183 regexp/icase
184 '()))
185 #f)))
186 (for-each
187 (lambda (h)
188 (let ((hdr (mu-message-get-header sieve-current-message h)))
189 (if hdr
190 (let ((naddr (mu-address-get-count hdr)))
191 (do ((n 1 (1+ n)))
192 ((> n naddr) #f)
193 (let ((addr (case part
194 ((#:all)
195 (mu-address-get-email hdr n))
196 ((#:localpart)
197 (mu-address-get-local hdr n))
198 ((#:domain)
199 (mu-address-get-domain hdr n)))))
200 (if addr
201 (case match
202 ((#:is)
203 (if (comp addr key)
204 (exit #t)))
205 ((#:contains)
206 (if (sieve-str-str addr key comp)
207 (exit #t)))
208 ((#:matches)
209 (if (regexp-exec rx addr)
210 (exit #t))))
211 (runtime-error LOG_NOTICE
212 "Can't get address parts for message "
213 sieve-current-message))))))))
214 header-list)))
215 key-list)
216 #f))))
217
218 (define (test-size key-size . comp)
219 (let ((size (mu-message-get-size sieve-current-message)))
220 (cond
221 ((null? comp) ;; An extension.
222 (= size key-size))
223 ((eq? (car comp) #:over)
224 (> size key-size))
225 ((eq? (car comp) #:under)
226 (< size key-size))
227 (else
228 (runtime-error LOG_CRIT "test-size: unknown comparator " comp)))))
229
230 (define (test-envelope part key-list . opt-list)
231 #f)
232
233 (define (test-exists header-list)
234 (call-with-current-continuation
235 (lambda (exit)
236 (for-each (lambda (hdr)
237 (if (not (mu-message-get-header sieve-current-message hdr))
238 (exit #f)))
239 header-list)
240 #t)))
241
242 (define (test-header header-list key-list . opt-args)
243 (let ((comp (find-comp opt-args))
244 (match (find-match opt-args)))
245 (call-with-current-continuation
246 (lambda (exit)
247 (for-each
248 (lambda (key)
249 (let ((rx (if (eq? match #:matches)
250 (make-regexp (sieve-regexp-to-posix key)
251 (if (eq? comp string-ci=?)
252 regexp/icase
253 '()))
254 #f)))
255 (for-each
256 (lambda (h)
257 (let ((hdr (mu-message-get-header sieve-current-message h)))
258 (if hdr
259 (case match
260 ((#:is)
261 (if (comp hdr key)
262 (exit #t)))
263 ((#:contains)
264 (if (sieve-str-str hdr key comp)
265 (exit #t)))
266 ((#:matches)
267 (if (regexp-exec rx hdr)
268 (exit #t)))))))
269 header-list)))
270 key-list)
271 #f))))
272
273 ;;; Register tests:
274 (define address-part (list (cons "localpart" #f)
275 (cons "domain" #f)
276 (cons "all" #f)))
277 (define match-type (list (cons "is" #f)
278 (cons "contains" #f)
279 (cons "matches" #f)))
280 (define size-comp (list (cons "under" #f)
281 (cons "over" #f)))
282 (define comparator (list (cons "comparator" 'string)))
283
284 (cond
285 (sieve-parser
286 (sieve-register-test "address"
287 test-address
288 (append address-part comparator match-type)
289 (list 'string-list 'string-list))
290 (sieve-register-test "size"
291 test-size
292 size-comp
293 (list 'number))
294 ; (sieve-register-test "envelope"
295 ; test-envelope
296 ; (append comparator address-part match-type)
297 ; (list 'string-list 'string-list))
298 (sieve-register-test "exists"
299 test-exists
300 '()
301 (list 'string-list))
302 (sieve-register-test "header"
303 test-header
304 (append comparator match-type)
305 (list 'string-list 'string-list))
306 (sieve-register-test "false" #f '() '())
307 (sieve-register-test "true" #t '() '())))
308
309 ;;; runtime-error
310 (define (runtime-error level . text)
311 (display (string-append "RUNTIME ERROR in " sieve-source ": "))
312 (for-each
313 (lambda (s)
314 (display s))
315 text)
316 (newline))
317
318 ;;; Sieve-main
319 (define sieve-current-message #f)
320 (define (sieve-main)
321 (let ((count (mu-mailbox-messages-count current-mailbox)))
322 (do ((n 1 (1+ n)))
323 ((> n count) #f)
324 (set! sieve-current-message
325 (mu-mailbox-get-message current-mailbox n))
326 (sieve-process-message))))