Sieve core functions for scripts generated by sieve.scm.
Showing
1 changed file
with
326 additions
and
0 deletions
guimb/scm/sieve-core.scm
0 → 100644
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)))) |
-
Please register or sign in to post a comment