3
## ManageSieve patch v9.3 for dovecot-1.0.13.
5
## Copyright (c) 2006-2008 by Stephan Bosch <stephan@rename-it.nl>
7
## This patch is made for and partly based on the Dovecot Secure IMAP server
8
## written by Timo Sirainen <tss@iki.fi>. The managesieve service is a modified
9
## version of Dovecot's imap service implementation.
11
## This patch includes the CMU Sieve sources copyrighted by the Carnegie Mellon
12
## University. The authors are listed (after patching) at:
13
## src/lib-sieve/cmu/libsieve/AUTHORS
15
## This patch is licenced under LGPLv2.1 (see COPYING.LGPL in the dovecot sources)
17
## The included CMU Sieve code is licenced under a custom Carnegie Mellon
18
## University licence as explained in the respective source files in:
19
## src/lib-sieve/cmu/libsieve/
22
. $(dirname $0)/DPATCH
26
diff -r 894f003d9f5f README.managesieve
27
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
28
+++ b/README.managesieve Sun May 04 16:00:59 2008 +0200
30
+Dovecot-MANAGESIEVE patch v9.3 for dovecot-1.0
35
+After applying this patch, the usual ./configure, make, make install sequence
36
+is not enough. First the automake/autoconf structure needs to be rebuilt to
37
+include the ManageSieve sources in the compilation process. This requires
38
+autotools to be installed on your system.
40
+When you downloaded using Mercurial you have script called autogen.sh in your
41
+source tree. And you should proceed as specified on:
43
+http://wiki.dovecot.org/CompilingSource
45
+Otherwise, execute the following command inside the dovecot source tree:
49
+After this you can continue the usual build process
50
+(http://wiki.dovecot.org/CompilingSource):
59
+**NOTE**: If you have used the sieve plugin before and you have .dovecot.sieve
60
+files in user directories, you are advised to make a backup first. Although the
61
+managesieve daemon takes care to move these files to the sieve storage before it
62
+is substituted with a symbolic link, this is not a very well tested operation,
63
+meaning that there is a possibility that existing sieve scripts get lost.
65
+Along with all other binaries that dovecot uses, the managesieve and
66
+managesieve-login binaries are installed during make install. The only thing you
67
+need to do to activate the ManageSieve support in dovecot is to add managesieve
68
+to the protocols= configuration line in your dovecot.conf. The managesieve
69
+daemon will listen on port 2000 by default. As the implementation of the
70
+managesieve daemon is largely based on the original IMAP implementation, it is
71
+very similar in terms of configuration. In addition to most mail daemon config
72
+settings, the managesieve daemon accepts a few more. The following settings can
73
+be configured in the protocol managesieve section:
76
+ IP or host address where to listen in for connections.
78
+login_executable = /usr/libexec/dovecot/managesieve-login
79
+ Login executable location.
81
+mail_executable = /usr/libexec/dovecot/managesieve
82
+ managesieve executable location. See mail_executable for IMAP for examples
83
+ how this could be changed.
85
+managesieve_max_line_length = 65536
86
+ Maximum managesieve command line length in bytes. This setting is directly
87
+ borrowed from IMAP. But, since long command lines are very unlikely with
88
+ ManageSieve, changing this will not be very useful.
91
+ This specifies the path to the directory where the uploaded scripts are
92
+ stored. In terms of '%' variable substitution it is identical to dovecot's
93
+ mail_location setting used by the mail protocol daemons. Scripts are stored
94
+ as separate files with extension .sieve, all other files are ignored when
95
+ scripts are listed by a ManageSieve client. If this setting remains
96
+ unspecified, the mail_location setting is used as explained below.
98
+sieve = ~/.dovecot.sieve
99
+ Specifies the location of the symbolic link pointing to the active script in
100
+ the sieve storage directory. This must match the sieve setting used by
101
+ deliver. Variable substitution with % is recognized. If a regular file
102
+ exists at this location, it is moved to the sieve_storage location before
103
+ the symbolic link is installed. It is renamed to dovecot.orig.sieve and
104
+ therefore listed as dovecot.orig by a ManageSieve client.
107
+ If, for some inobvious reason, the sieve_storage remains unset, the
108
+ managesieve daemon uses the specification of the mail_location to find out
109
+ where to store the sieve files. However, this is provided only for backwards
110
+ compatibility and you should always use the sieve_storage setting in stead.
112
+managesieve_implementation_string = dovecot
113
+ To fool ManageSieve clients that are focused on CMU's timesieved you can
114
+ specify the IMPLEMENTATION capability that the dovecot reports to clients
115
+ (e.g. 'Cyrus timsieved v2.2.13').
117
+Scripts are stored at the location specified by the sieve_storage setting. The
118
+active sieve script is managed as a symbolic link pointing to the active script
119
+in the sieve storage direcotory. The location of this symlink can be specified
120
+with the 'sieve' setting. Make sure this setting is identical to what deliver is
121
+using for the Sieve plugin. The default location is ~/.dovecot.sieve. Note that
122
+if a file starting with '.' is placed inside a Maildir, it will be recognized as
123
+a folder, so try to avoid that.
125
+The current version of the managesieve daemon places the script storage
126
+directory in the mail folder as specified by the 'mail_location' setting if no
127
+'sieve_storage' is specified. Actually, it is placed in the CONTROL= directory
128
+of 'mail_location' if specified, otherwise the sieve directory is placed in the
129
+root of the mail location. In a mail or mail control directory, scripts are
130
+always stored in a 'sieve' subdirectory. Note that for some mail storage types
131
+(e.g. mbox) this script directory is listed as a mail folder, so be sure to put
132
+the sieve scripts somewhere else if you can.
134
+A storage location specified by 'sieve_storage' is always generated
135
+automatically if it does not exist (as far as the system permits the user to do
136
+so; no root privileges are used). This is similar to the behaviour of the mail
137
+daemons. Note that when 'mail_location' is used to specify the script storage
138
+location, only the 'sieve' subdirectory is generated automatically.
140
+The following provides an example configuration for ManageSieve in dovecot.conf.
141
+Only sections relevant to ManageSieve are shown. Refer to dovecot-example.conf
142
+in your patched dovecot tree for a full example with comments, but don't forget
143
+to add managesieve to the protocols setting if you use it.
145
+# Start imap, pop3 and managesieve services
146
+protocols = imap pop3 managesieve
148
+protocol managesieve {
149
+ # Specify an alternative address:port the daemon must listen on
150
+ # (default: *:2000)
151
+ #listen = localhost:2000
153
+ sieve=~/.dovecot.sieve
154
+ sieve_storage=~/sieve
160
+Like Dovecot's imapd, the ManageSieve login daemon supports proxying to multiple
161
+backend servers. Although the underlying code is copied from the imapd sources
162
+for the most part, it has some ManageSieve-specifics that have not seen much
165
+The proxy configuration wiki page for POP3 and IMAP should apply to ManageSieve
168
+http://wiki.dovecot.org/PasswordDatabase/ExtraFields/Proxy
173
+* Although this ManageSieve server should comply with the draft specification of
174
+ the ManageSieve protocol, quite a few clients don't. This is particularly true
175
+ for the TLS support. However, now that Cyrus' Timsieved has changed its
176
+ behavior towards protocol compliance, all those clients will follow
179
+ Clients known to have TLS issues:
180
+ - Thunderbird Sieve add-on: author is working on it
181
+ - AvelSieve: patch on the wiki: http://wiki.dovecot.org/ManageSieve
182
+ - KMail + kio_sieve: ...?
184
+ Unfortunately, there is no reliable way to provide a workaround for this
185
+ problem. We will have to wait for the authors of these clients to make the
186
+ proper adjustments.
188
+* Other client issues:
190
+ - SmartSieve, WebSieve:
191
+ These clients are specifically written for Cyrus timsieved and fail on
192
+ multiple stages of the protocol when connected to Dovecot ManageSieve.
194
+* The current implementation of the daemon does not have quota enforcement as
195
+ recommended in the specification. So keep in mind that malicious users could
196
+ fill your filesystem with loads of spurious scriptfiles.
198
+* The ANONYMOUS authentication mechanism is currently not supported and
204
+Stephan Bosch <stephan at rename-it dot nl>
205
+IRC: Freenode, #dovecot, S[r]us
207
+Please use the Dovecot mailing list <dovecot at dovecot.org> for questions about
208
+this package. You can post to the list without subscribing, the mail then waits
209
+in a moderator queue for a while. See http://dovecot.org/mailinglists.html
214
+The overall design of the daemon is entirely borrowed from the existing
215
+imap daemon. I have tried to apply the dovecot framework and programming
216
+paradigms as much as possible. This patch adds the following directories
217
+to the source directory:
219
+lib-managesieve: parser and quote functions (bound to disappear)
220
+lib-sievestorage: defines a storage for the sieve scripts (a bit crude)
221
+managesieve-login: the initial login daemon (derived from src/imap-login)
222
+managesieve: the actual managesieve daemon (derived form src/imap)
223
+lib-sieve: yet another copy of the cmu sieve library :/ (fixed for Dovecot 1.1)
225
+Currently the managesieve daemon simply plugs in the dovecot-master as any
226
+other mail protocol (imap,pop3). Since managesieve is not actually a mail
227
+protocol, this is probably not a good design practice. However, i designed
228
+this patch to be most uninvasive to the existing sources. I think Timo
229
+knows best how to implement this link properly.
231
+The daemon currently implements all existing MANAGESIEVE commands except the
232
+HAVESPACE command which always says 'ok'. It also implements the required
233
+support for UTF-8 strings.
239
+- Fixed bug that caused SASL mechanisms that require more than a single client
240
+ response to fail. Reported by Steffen Kaiser and occured when he tried using
241
+ the (obsolete) LOGIN mechanism.
242
+ information provided in the wiki
245
+- Fixed bug in tmp file generation for sieve-storage: errors other than EEXIST
246
+ would cause the daemon to sleep() loop indefinitely.
252
+ very time-constrained and thus hard to reproduce. The error turned out
253
+ to be related to the input handling of the login daemon during
255
+ managesieve implementation due to code duplication.
256
+ the storage was moved.
259
+ in this file more concise.
262
+ in" message is now sent by the -login process and not by the managesieve
263
+ daemon anymore. This caused a segfault every once in a while.
264
+ login_dir. 'dovecot -n' now reports correct results, but testing will show
265
+ whether the whole problem is solved.
266
+ settings, so it is now possible to explicitly configure the location of the
267
+ sieve storage and the active script respectively. The daemon still falls back
268
+ to using the mail_location (MAIL) settings if nothing else is specified.
269
+ clients have adopted to this behaviour. The latest managesieve (08) advises to
270
+ accept a missing + from clients. The server should not send any + characters
271
+ as well. This behavior is now implemented on the server.
272
+ various sub-functions for obtaining the various paths and directories.
273
+ path fails somehow. Previously, this presented the admin with a log message
274
+ that it had just eaten the script, which is not very nice.
275
+ regard to the configuration of the daemon.
278
+- Robin Breathe indicated that the regex capability was missing in the server's
279
+ SIEVE listing. It turns out I forgot to make arrangements for setting
280
+ ENABLE_REGEX in the cmu libsieve sources, so the regex extension was not
281
+ compiled in. I copied the configure.in section regarding ENABLE_REGEX from
282
+ dovecot-sieve-1.0.2 and that fixed the problem.
285
+- Corked the client output stream while producing the capability greeting and on
286
+ other some other occasions as well. Some naive client implementations expect to
287
+ receive this as a single tcp frame and it is a good practice to do so anyway.
288
+ Using this change the Thunderbird sieve extension (v0.1.1) seemed to work. However,
289
+ scripts larger than a tcp frame still caused failures. All these issues are fixed
290
+ in the latest version of the sieve add-on (currently v0.1.4).
291
+- Cleaned up the new proxy source. My editor made the indentation a complete mess
292
+ in terms of TABs vs spaces.
293
+- Added TRYLATER response codes to BYE and NO messages where appropriate.
294
+- Recopied the libsieve library into this patch to incorporate any changes that were
295
+ made (only sieve-cmu.c still needs to be compared to the old cmu-sieve.c). This
296
+ also solves the __attribute__((unused)) GCC dependencies. These were fixed long
297
+ ago by Timo.... the code duplication beast strikes again.
298
+- Removed spurious return value from void function in
299
+ src/lib-sieve/sieve-implementation.c as reported by Robin Breathe. GCC fails to
300
+ report these issues. The function involved is currently not used and serves only
301
+ as an example on how dovecot could support multiple sieve backends...
304
+- Applied patch by Uldis Pakuls to fix master_dump_settings bug
305
+- Added some compilation/installation info to this README
306
+- Moved README to source tree root as README.managesieve
307
+- Fixed minor error handling bug in sieve_storage.c with respect to a missing
309
+- Now sieve capabilities are reported as they are specified by the implementing
310
+ library and not in forced upper case. The sieve RFC now explicitly states
311
+ that sieve capability identifiers are case-sensitive. This broke compatibility
312
+ with SquirrelMail/Avelsieve.
313
+- Disabled ANONYMOUS login entirely until proper support is implemented. V4
314
+ claimed to do so as well, but in fact it only stopped announcing it.
315
+- Implemented managesieve-proxy. It is not so much a clean copy of imap-proxy,
316
+ since the managesieve greeting is much more complex and requires parsing.
317
+ Configuration is identical to imap-proxy. This seems to be a little under-
318
+ documented however (http://wiki.dovecot.org/PasswordDatabase/ExtraFields).
321
+- Added managesieve_implementation_string setting to the managesieve
322
+ configuration. This can be used to customize the default "IMPLEMENTATION"
323
+ capability response.
324
+- Denied ANONYMOUS login until proper support is implemented
325
+- Fixed problem with authenticate command regarding continued responses. In
326
+ V3 only initial response would work. Problem was caused by rc2 -> rc28
327
+ upgrade. One of the clear reasons why code duplication is a very bad idea.
328
+- Fixed readlink bug as indicated by Timo: return value of readlink can also
330
+- Fixed bug in the regular file rescue code, as introduced in the previous
331
+ version. Used stat instead of lstat. This caused the symlink to be rescued
332
+ subsequently in the next activation, thus still overwriting the initially
336
+- Updated source to compile with dovecot 1.0.rc27
337
+- Daemon now uses the same location for .dovecot.sieve as dovecot-lda
338
+ This is typically ~/.dovecot.sieve
339
+- If .dovecot.sieve is a regular file, it is now moved into the script storage as
340
+ dovecot.orig.sieve, preventing deletion of (important) active scripts
342
+- Changed error handling to yield a BYE message when the managesieve
343
+ daemon exits unexpectedly (upon login) before any commands are entered.
344
+ Horde-ingo would wait indefinitely for a response.
347
+- Fixed the bug (missing CRLF) in the authenticate command
348
+- Modified the sieve storage library making the interface much less crude.
349
+- The scripts put on the server using the putscript command are now
350
+ checked before they are accepted.
351
+- The reported SIEVE capability is now directly read from the sieve
352
+ implementation (in this case cmu), listing much more than "FILEINTO
354
+- Imported instance of libsieve source into this patch for implementation
355
+ of script checking and capability listing. THIS NEEDS TO BE CHANGED!
356
+- Fixed some minor bugs in the putscript command
361
+* Upgrade to dovecot-1.1 branch.
362
+* Enforce protocol syntax better with some of the commands. Some
363
+ commands still allow spurious extra arguments
364
+ --> Full protocol syntax conformance review.
365
+* Implement proper support for anonymous login.
366
+* Implement listing of NOTIFY capability as specified in the latest versions
367
+ of the MANAGESIEVE draft.
368
+* Implement the HAVESPACE command properly. Currently it always says ok.
369
+ Maybe this should be linked to the mail quota system that is currently
371
+* Create proper process interface for generic non-mail protocols (Timo).
372
+* Resolve exessive code duplication
373
+* Uploaded scripts are syntax checked, but this patch includes another
374
+ cmu-sieve source tree to implement this. This needs to be put in some
375
+ unified location in de dovecot(-sieve) tree. This will probably have to
376
+ wait until dovecot v2.0
377
+* Make the sieve storage a base class with (possibly) various
378
+ implementations, just like mail-storage. Currently not very useful.
380
+* Thorough testing...
382
diff -r 894f003d9f5f configure.in
383
--- a/configure.in Sun Mar 09 12:50:11 2008 +0200
384
+++ b/configure.in Sun May 04 16:00:59 2008 +0200
385
@@ -9,6 +9,8 @@ AC_ISC_POSIX
394
@@ -287,6 +289,16 @@ AC_ARG_WITH(pop3d,
397
AM_CONDITIONAL(BUILD_POP3D, test "$want_pop3d" = "yes")
399
+AC_ARG_WITH(managesieve,
400
+[ --with-managesieve Build MANAGESIEVE server (default)],
401
+ if test x$withval = xno; then
402
+ want_managesieve=no
404
+ want_managesieve=yes
406
+ want_managesieve=yes)
407
+AM_CONDITIONAL(BUILD_MANAGESIEVE, test "$want_managesieve" = "yes")
410
[ --with-deliver Build mail delivery agent (default)],
411
@@ -1812,6 +1824,11 @@ capability="IMAP4rev1 SASL-IR SORT THREA
412
capability="IMAP4rev1 SASL-IR SORT THREAD=REFERENCES MULTIAPPEND UNSELECT LITERAL+ IDLE CHILDREN NAMESPACE LOGIN-REFERRALS"
413
AC_DEFINE_UNQUOTED(CAPABILITY_STRING, "$capability", IMAP capabilities)
415
+dnl * Regexp library check, from Cyrus IMAP
416
+AC_SEARCH_LIBS(regcomp, rx regex, [
417
+ CFLAGS="$CFLAGS -DENABLE_REGEX"
418
+ AC_CHECK_HEADER(rxposix.h, CFLAGS="$CFLAGS -DHAVE_RX")])
420
CFLAGS="$CFLAGS $EXTRA_CFLAGS"
422
AC_CONFIG_HEADERS([config.h])
423
@@ -1828,8 +1845,13 @@ src/lib-imap/Makefile
424
src/lib-imap/Makefile
425
src/lib-index/Makefile
426
src/lib-mail/Makefile
427
+src/lib-managesieve/Makefile
428
src/lib-ntlm/Makefile
429
src/lib-settings/Makefile
430
+src/lib-sievestorage/Makefile
431
+src/lib-sieve/Makefile
432
+src/lib-sieve/cmu/Makefile
433
+src/lib-sieve/cmu/libsieve/Makefile
434
src/lib-storage/Makefile
435
src/lib-storage/index/Makefile
436
src/lib-storage/index/maildir/Makefile
437
@@ -1843,6 +1865,8 @@ src/imap/Makefile
439
src/imap-login/Makefile
440
src/login-common/Makefile
441
+src/managesieve/Makefile
442
+src/managesieve-login/Makefile
445
src/pop3-login/Makefile
446
diff -r 894f003d9f5f dovecot-example.conf
447
--- a/dovecot-example.conf Sun Mar 09 12:50:11 2008 +0200
448
+++ b/dovecot-example.conf Sun May 04 16:00:59 2008 +0200
450
# Base directory where to store runtime data.
451
#base_dir = /var/run/dovecot/
453
-# Protocols we want to be serving: imap imaps pop3 pop3s
454
+# Protocols we want to be serving: imap imaps pop3 pop3s managesieve
455
# If you only want to use dovecot-auth, you can set this to "none".
456
#protocols = imap imaps
459
# interfaces depending on the operating system.
461
# If you want to specify ports for each service, you will need to configure
462
-# these settings inside the protocol imap/pop3 { ... } section, so you can
463
-# specify different ports for IMAP/POP3. For example:
464
+# these settings inside the protocol imap/pop3/managesieve { ... } section,
465
+# so you can specify different ports for IMAP/POP3/MANAGESIEVE. For example:
468
# ssl_listen = *:10943
475
+# protocol managesieve {
480
@@ -646,6 +650,48 @@ protocol pop3 {
481
# missing. This option simply sends it if it's missing.
482
# The list is space-separated.
483
#pop3_client_workarounds =
487
+## MANAGESIEVE specific settings
490
+protocol managesieve {
491
+ # Login executable location.
492
+ #login_executable = /usr/libexec/dovecot/managesieve-login
494
+ # MANAGESIEVE executable location. See IMAP's mail_executable above for
495
+ # examples how this could be changed.
496
+ #mail_executable = /usr/libexec/dovecot/managesieve
498
+ # Maximum MANAGESIEVE command line length in bytes. This setting is
499
+ # directly borrowed from IMAP. But, since long command lines are very
500
+ # unlikely with MANAGESIEVE, changing this will not be very useful.
501
+ #managesieve_max_line_length = 65536
503
+ # Specifies the location of the symlink pointing to the active script in
504
+ # the sieve storage directory. This must match the SIEVE setting used by
505
+ # deliver (refer to http://wiki.dovecot.org/LDA/Sieve#location for more
506
+ # info). Variable substitution with % is recognized.
507
+ sieve=~/.dovecot.sieve
509
+ # This specifies the path to the directory where the uploaded scripts must
510
+ # be stored. In terms of '%' variable substitution it is identical to
511
+ # dovecot's mail_location setting used by the mail protocol daemons.
512
+ sieve_storage=~/sieve
514
+ # If, for some inobvious reason, the sieve_storage remains unset, the
515
+ # managesieve daemon uses the specification of the mail_location to find out
516
+ # where to store the sieve files (see explaination in README.managesieve).
517
+ # The example below, when uncommented, overrides any global mail_location
518
+ # specification and stores all the scripts in '~/mail/sieve' if sieve_storage
519
+ # is unset. However, you should always use the sieve_storage setting.
520
+ # mail_location = mbox:~/mail
522
+ # To fool managesieve clients that are focused on timesieved you can
523
+ # specify the IMPLEMENTATION capability that the dovecot reports to clients
524
+ # (default: dovecot).
525
+ #managesieve_implementation_string = Cyrus timsieved v2.2.13
529
diff -r 894f003d9f5f src/Makefile.am
530
--- a/src/Makefile.am Sun Mar 09 12:50:11 2008 +0200
531
+++ b/src/Makefile.am Sun May 04 16:00:59 2008 +0200
532
@@ -4,6 +4,10 @@ endif
538
+if BUILD_MANAGESIEVE
539
+MANAGESIEVE = lib-managesieve lib-sievestorage lib-sieve managesieve managesieve-login
543
@@ -26,5 +30,6 @@ SUBDIRS = \
550
diff -r 894f003d9f5f src/lib-managesieve/Makefile.am
551
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
552
+++ b/src/lib-managesieve/Makefile.am Sun May 04 16:00:59 2008 +0200
554
+noinst_LIBRARIES = libmanagesieve.a
557
+ -I$(top_srcdir)/src/lib \
558
+ -I$(top_srcdir)/src/lib-charset \
559
+ -I$(top_srcdir)/src/lib-mail
561
+libmanagesieve_a_SOURCES = \
562
+ managesieve-quote.c \
563
+ managesieve-parser.c
566
+ managesieve-quote.h \
567
+ managesieve-parser.h
568
diff -r 894f003d9f5f src/lib-managesieve/managesieve-parser.c
569
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
570
+++ b/src/lib-managesieve/managesieve-parser.c Sun May 04 16:00:59 2008 +0200
573
+#include "istream.h"
574
+#include "ostream.h"
575
+#include "strescape.h"
576
+#include "managesieve-parser.h"
578
+#define is_linebreak(c) \
579
+ ((c) == '\r' || (c) == '\n')
581
+#define LIST_ALLOC_SIZE 7
583
+enum arg_parse_type {
584
+ ARG_PARSE_NONE = 0,
588
+ ARG_PARSE_LITERAL_DATA
591
+struct managesieve_parser {
594
+ struct istream *input;
595
+ struct ostream *output;
596
+ size_t max_line_size;
597
+ enum managesieve_parser_flags flags;
599
+ /* reset by managesieve_parser_reset(): */
601
+ struct managesieve_arg_list *root_list;
602
+ struct managesieve_arg_list *cur_list;
604
+ enum arg_parse_type cur_type;
605
+ size_t cur_pos; /* parser position in input buffer */
607
+ int str_first_escape; /* ARG_PARSE_STRING: index to first '\' */
608
+ uoff_t literal_size; /* ARG_PARSE_LITERAL: string size */
612
+ unsigned int literal_skip_crlf:1;
613
+ unsigned int literal_nonsync:1;
614
+ unsigned int eol:1;
615
+ unsigned int fatal_error:1;
619
+#define LIST_REALLOC(parser, old_list, new_size) \
620
+ p_realloc((parser)->pool, old_list, \
621
+ sizeof(struct managesieve_arg_list) + \
622
+ (old_list == NULL ? 0 : \
623
+ sizeof(struct managesieve_arg_list) * (old_list)->alloc), \
624
+ sizeof(struct managesieve_arg_list) * (new_size))
626
+static void managesieve_args_realloc(struct managesieve_parser *parser, size_t size)
628
+ parser->cur_list = LIST_REALLOC(parser, parser->cur_list, size);
629
+ parser->cur_list->alloc = size;
631
+ parser->root_list = parser->cur_list;
634
+struct managesieve_parser *
635
+managesieve_parser_create(struct istream *input, struct ostream *output,
636
+ size_t max_line_size)
638
+ struct managesieve_parser *parser;
640
+ parser = i_new(struct managesieve_parser, 1);
641
+ parser->pool = pool_alloconly_create("MANAGESIEVE parser", 8192);
642
+ parser->input = input;
643
+ parser->output = output;
644
+ parser->max_line_size = max_line_size;
646
+ managesieve_args_realloc(parser, LIST_ALLOC_SIZE);
650
+void managesieve_parser_destroy(struct managesieve_parser **parser)
652
+ pool_unref((*parser)->pool);
657
+void managesieve_parser_reset(struct managesieve_parser *parser)
659
+ p_clear(parser->pool);
661
+ parser->line_size = 0;
663
+ parser->root_list = NULL;
664
+ parser->cur_list = NULL;
666
+ parser->cur_type = ARG_PARSE_NONE;
667
+ parser->cur_pos = 0;
669
+ parser->str_first_escape = 0;
670
+ parser->literal_size = 0;
672
+ parser->error = NULL;
674
+ parser->literal_skip_crlf = FALSE;
675
+ parser->eol = FALSE;
677
+ managesieve_args_realloc(parser, LIST_ALLOC_SIZE);
680
+const char *managesieve_parser_get_error(struct managesieve_parser *parser, bool *fatal)
682
+ *fatal = parser->fatal_error;
683
+ return parser->error;
686
+/* skip over everything parsed so far, plus the following whitespace */
687
+static int managesieve_parser_skip_to_next(struct managesieve_parser *parser,
688
+ const unsigned char **data,
693
+ for (i = parser->cur_pos; i < *data_size; i++) {
694
+ if ((*data)[i] != ' ')
698
+ parser->line_size += i;
699
+ i_stream_skip(parser->input, i);
700
+ parser->cur_pos = 0;
704
+ return *data_size > 0;
707
+static struct managesieve_arg *managesieve_arg_create(struct managesieve_parser *parser)
709
+ struct managesieve_arg *arg;
711
+ i_assert(parser->cur_list != NULL);
714
+ if (parser->cur_list->size == parser->cur_list->alloc)
715
+ managesieve_args_realloc(parser, parser->cur_list->alloc * 2);
717
+ arg = &parser->cur_list->args[parser->cur_list->size];
718
+ parser->cur_list->size++;
723
+static void managesieve_parser_save_arg(struct managesieve_parser *parser,
724
+ const unsigned char *data, size_t size)
726
+ struct managesieve_arg *arg;
728
+ arg = managesieve_arg_create(parser);
730
+ switch (parser->cur_type) {
731
+ case ARG_PARSE_ATOM:
732
+ /* simply save the string */
733
+ arg->type = MANAGESIEVE_ARG_ATOM;
734
+ arg->_data.str = p_strndup(parser->pool, data, size);
736
+ case ARG_PARSE_STRING:
737
+ /* data is quoted and may contain escapes. */
738
+ i_assert(size > 0);
740
+ arg->type = MANAGESIEVE_ARG_STRING;
741
+ arg->_data.str = p_strndup(parser->pool, data+1, size-1);
743
+ /* remove the escapes */
744
+ if (parser->str_first_escape >= 0 &&
745
+ (parser->flags & MANAGESIEVE_PARSE_FLAG_NO_UNESCAPE) == 0) {
746
+ /* -1 because we skipped the '"' prefix */
747
+ str_unescape(arg->_data.str +
748
+ parser->str_first_escape-1);
751
+ case ARG_PARSE_LITERAL_DATA:
752
+ if ((parser->flags & MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE) != 0) {
753
+ /* save literal size */
754
+ arg->type = MANAGESIEVE_ARG_LITERAL_SIZE;
755
+ arg->_data.literal_size = parser->literal_size;
756
+ } else if ((parser->flags &
757
+ MANAGESIEVE_PARSE_FLAG_LITERAL_TYPE) != 0) {
758
+ arg->type = MANAGESIEVE_ARG_LITERAL;
759
+ arg->_data.str = p_strndup(parser->pool, data, size);
761
+ arg->type = MANAGESIEVE_ARG_STRING;
762
+ arg->_data.str = p_strndup(parser->pool, data, size);
769
+ parser->cur_type = ARG_PARSE_NONE;
772
+static int is_valid_atom_char(struct managesieve_parser *parser, char chr)
774
+ if (IS_ATOM_SPECIAL((unsigned char)chr)) {
775
+ parser->error = "Invalid characters in atom";
777
+ } else if ((chr & 0x80) != 0) {
778
+ parser->error = "8bit data in atom";
785
+static int managesieve_parser_read_atom(struct managesieve_parser *parser,
786
+ const unsigned char *data, size_t data_size)
790
+ /* read until we've found space, CR or LF. */
791
+ for (i = parser->cur_pos; i < data_size; i++) {
792
+ if (data[i] == ' ' || data[i] == ')' ||
793
+ is_linebreak(data[i])) {
794
+ managesieve_parser_save_arg(parser, data, i);
796
+ } else if (!is_valid_atom_char(parser, data[i]))
800
+ parser->cur_pos = i;
801
+ return parser->cur_type == ARG_PARSE_NONE;
804
+static int managesieve_parser_read_string(struct managesieve_parser *parser,
805
+ const unsigned char *data, size_t data_size)
810
+ /* QUOTED-CHAR = SAFE-UTF8-CHAR / "\" QUOTED-SPECIALS
811
+ * quoted = <"> *QUOTED-CHAR <">
812
+ * ;; limited to 1024 octets between the <">s
815
+ /* read until we've found non-escaped ", CR or LF */
816
+ for (i = parser->cur_pos; i < data_size; i++) {
817
+ if (data[i] == '"') {
818
+ managesieve_parser_save_arg(parser, data, i);
820
+ i++; /* skip the trailing '"' too */
824
+ if (data[i] == '\\') {
825
+ if (i+1 == data_size) {
826
+ /* known data ends with '\' - leave it to
827
+ next time as well if it happens to be \" */
831
+ /* save the first escaped char */
832
+ if (parser->str_first_escape < 0)
833
+ parser->str_first_escape = i;
835
+ /* skip the escaped char */
838
+ if ( !IS_QUOTED_SPECIAL(data[i]) ) {
839
+ parser->error = "Escaped quoted-string character is not a QUOTED-SPECIAL.";
846
+ /* Enforce valid UTF-8
848
+ if ( (utf8_len = UTF8_LEN(data[i])) == 0 ) {
849
+ parser->error = "String contains invalid character.";
853
+ if ( utf8_len > 1 ) {
854
+ bool overlong = FALSE;
856
+ if ( (i+utf8_len-1) >= data_size ) {
857
+ /* Known data ends in the middle of a UTF-8 character;
858
+ * leave it to next time.
863
+ /* Check for overlong UTF-8 sequences */
864
+ switch (utf8_len) {
866
+ if (!(data[i] & 0x1E)) overlong = TRUE;
869
+ if (!(data[i] & 0x0F) && !(data[i+1] & 0x20)) overlong = TRUE;
872
+ if (!(data[i] & 0x07) && !(data[i+1] & 0x30)) overlong = TRUE;
875
+ if (!(data[i] & 0x03) && !(data[i+1] & 0x38)) overlong = TRUE;
878
+ if (!(data[i] & 0x01) && !(data[i+1] & 0x3C)) overlong = TRUE;
885
+ parser->error = "String contains invalid/overlong UTF-8 character.";
892
+ /* Parse the series of UTF8_1 characters */
893
+ for (; utf8_len > 0; utf8_len--, i++ ) {
894
+ if (!IS_UTF8_1(data[i])) {
895
+ parser->error = "String contains invalid UTF-8 character.";
902
+ parser->cur_pos = i;
903
+ return parser->cur_type == ARG_PARSE_NONE;
906
+static int managesieve_parser_literal_end(struct managesieve_parser *parser)
908
+ if ((parser->flags & MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE) == 0) {
909
+ if (parser->line_size >= parser->max_line_size ||
910
+ parser->literal_size >
911
+ parser->max_line_size - parser->line_size) {
912
+ /* too long string, abort. */
913
+ parser->error = "Literal size too large";
914
+ parser->fatal_error = TRUE;
919
+ parser->cur_type = ARG_PARSE_LITERAL_DATA;
920
+ parser->literal_skip_crlf = TRUE;
922
+ parser->cur_pos = 0;
926
+static int managesieve_parser_read_literal(struct managesieve_parser *parser,
927
+ const unsigned char *data,
930
+ size_t i, prev_size;
932
+ /* expecting digits + "}" */
933
+ for (i = parser->cur_pos; i < data_size; i++) {
934
+ if (data[i] == '}') {
935
+ parser->line_size += i+1;
936
+ i_stream_skip(parser->input, i+1);
938
+ return managesieve_parser_literal_end(parser);
941
+ if (parser->literal_nonsync) {
942
+ parser->error = "Expecting '}' after '+'";
946
+ if (data[i] == '+') {
947
+ parser->literal_nonsync = TRUE;
951
+ if (data[i] < '0' || data[i] > '9') {
952
+ parser->error = "Invalid literal size";
956
+ prev_size = parser->literal_size;
957
+ parser->literal_size = parser->literal_size*10 + (data[i]-'0');
959
+ if (parser->literal_size < prev_size) {
960
+ /* wrapped around, abort. */
961
+ parser->error = "Literal size too large";
966
+ parser->cur_pos = i;
970
+static int managesieve_parser_read_literal_data(struct managesieve_parser *parser,
971
+ const unsigned char *data,
974
+ if (parser->literal_skip_crlf) {
976
+ /* skip \r\n or \n, anything else gives an error */
977
+ if (data_size == 0)
980
+ if (*data == '\r') {
981
+ parser->line_size++;
982
+ data++; data_size--;
983
+ i_stream_skip(parser->input, 1);
985
+ if (data_size == 0)
989
+ if (*data != '\n') {
990
+ parser->error = "Missing LF after literal size";
994
+ parser->line_size++;
995
+ data++; data_size--;
996
+ i_stream_skip(parser->input, 1);
998
+ parser->literal_skip_crlf = FALSE;
1000
+ i_assert(parser->cur_pos == 0);
1003
+ if ((parser->flags & MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE) == 0) {
1004
+ /* now we just wait until we've read enough data */
1005
+ if (data_size < parser->literal_size) {
1008
+ managesieve_parser_save_arg(parser, data,
1009
+ (size_t)parser->literal_size);
1010
+ parser->cur_pos = (size_t)parser->literal_size;
1014
+ /* we want to save only literal size, not the literal itself. */
1015
+ parser->eol = TRUE;
1016
+ managesieve_parser_save_arg(parser, NULL, 0);
1021
+/* Returns TRUE if argument was fully processed. Also returns TRUE if
1022
+ an argument inside a list was processed. */
1023
+static int managesieve_parser_read_arg(struct managesieve_parser *parser)
1025
+ const unsigned char *data;
1028
+ data = i_stream_get_data(parser->input, &data_size);
1029
+ if (data_size == 0)
1032
+ while (parser->cur_type == ARG_PARSE_NONE) {
1033
+ /* we haven't started parsing yet */
1034
+ if (!managesieve_parser_skip_to_next(parser, &data, &data_size))
1036
+ i_assert(parser->cur_pos == 0);
1038
+ switch (data[0]) {
1041
+ /* unexpected end of line */
1042
+ parser->eol = TRUE;
1045
+ parser->cur_type = ARG_PARSE_STRING;
1046
+ parser->str_first_escape = -1;
1049
+ parser->cur_type = ARG_PARSE_LITERAL;
1050
+ parser->literal_size = 0;
1051
+ parser->literal_nonsync = FALSE;
1054
+ if (!is_valid_atom_char(parser, data[0]))
1056
+ parser->cur_type = ARG_PARSE_ATOM;
1060
+ parser->cur_pos++;
1063
+ i_assert(data_size > 0);
1065
+ switch (parser->cur_type) {
1066
+ case ARG_PARSE_ATOM:
1067
+ if (!managesieve_parser_read_atom(parser, data, data_size))
1070
+ case ARG_PARSE_STRING:
1071
+ if (!managesieve_parser_read_string(parser, data, data_size))
1074
+ case ARG_PARSE_LITERAL:
1075
+ if (!managesieve_parser_read_literal(parser, data, data_size))
1078
+ /* pass through to parsing data. since input->skip was
1079
+ modified, we need to get the data start position again. */
1080
+ data = i_stream_get_data(parser->input, &data_size);
1082
+ /* fall through */
1083
+ case ARG_PARSE_LITERAL_DATA:
1084
+ if (!managesieve_parser_read_literal_data(parser, data, data_size))
1091
+ i_assert(parser->cur_type == ARG_PARSE_NONE);
1095
+/* ARG_PARSE_NONE checks that last argument isn't only partially parsed. */
1096
+#define IS_UNFINISHED(parser) \
1097
+ ((parser)->cur_type != ARG_PARSE_NONE || \
1098
+ (parser)->cur_list != parser->root_list)
1100
+static int finish_line(struct managesieve_parser *parser, unsigned int count,
1101
+ struct managesieve_arg **args)
1103
+ parser->line_size += parser->cur_pos;
1104
+ i_stream_skip(parser->input, parser->cur_pos);
1105
+ parser->cur_pos = 0;
1107
+ if (count >= parser->root_list->alloc) {
1108
+ /* unused arguments must be NIL-filled. */
1109
+ parser->root_list =
1110
+ LIST_REALLOC(parser, parser->root_list, count+1);
1111
+ parser->root_list->alloc = count+1;
1114
+ parser->root_list->args[parser->root_list->size].type = MANAGESIEVE_ARG_EOL;
1116
+ *args = parser->root_list->args;
1117
+ return parser->root_list->size;
1120
+int managesieve_parser_read_args(struct managesieve_parser *parser, unsigned int count,
1121
+ enum managesieve_parser_flags flags, struct managesieve_arg **args)
1123
+ parser->flags = flags;
1125
+ while (!parser->eol && (count == 0 || parser->root_list->size < count ||
1126
+ IS_UNFINISHED(parser))) {
1127
+ if (!managesieve_parser_read_arg(parser))
1130
+ if (parser->line_size > parser->max_line_size) {
1131
+ parser->error = "MANAGESIEVE command line too large";
1136
+ if (parser->error != NULL) {
1137
+ /* error, abort */
1138
+ parser->line_size += parser->cur_pos;
1139
+ i_stream_skip(parser->input, parser->cur_pos);
1140
+ parser->cur_pos = 0;
1143
+ } else if ((!IS_UNFINISHED(parser) && count > 0 &&
1144
+ parser->root_list->size >= count) || parser->eol) {
1145
+ /* all arguments read / end of line. */
1146
+ return finish_line(parser, count, args);
1148
+ /* need more data */
1154
+int managesieve_parser_finish_line(struct managesieve_parser *parser, unsigned int count,
1155
+ enum managesieve_parser_flags flags,
1156
+ struct managesieve_arg **args)
1158
+ const unsigned char *data;
1162
+ ret = managesieve_parser_read_args(parser, count, flags, args);
1164
+ /* we should have noticed end of everything except atom */
1165
+ if (parser->cur_type == ARG_PARSE_ATOM) {
1166
+ data = i_stream_get_data(parser->input, &data_size);
1167
+ managesieve_parser_save_arg(parser, data, data_size);
1170
+ return finish_line(parser, count, args);
1173
+const char *managesieve_parser_read_word(struct managesieve_parser *parser)
1175
+ const unsigned char *data;
1176
+ size_t i, data_size;
1178
+ data = i_stream_get_data(parser->input, &data_size);
1180
+ for (i = 0; i < data_size; i++) {
1181
+ if (data[i] == ' ' || data[i] == '\r' || data[i] == '\n')
1185
+ if (i < data_size) {
1186
+ data_size = i + (data[i] == ' ' ? 1 : 0);
1187
+ parser->line_size += data_size;
1188
+ i_stream_skip(parser->input, data_size);
1189
+ return p_strndup(parser->pool, data, i);
1195
+const char *managesieve_arg_string(struct managesieve_arg *arg)
1197
+ if (arg->type == MANAGESIEVE_ARG_STRING)
1198
+ return arg->_data.str;
1203
+int managesieve_arg_number
1204
+ (struct managesieve_arg *arg, uoff_t *number)
1211
+ if (arg->type == MANAGESIEVE_ARG_ATOM) {
1212
+ data = arg->_data.str;
1213
+ while (data[i] != '\0') {
1214
+ if (data[i] < '0' || data[i] > '9')
1217
+ *number = (*number)*10 + (data[i] -'0');
1227
+char *_managesieve_arg_str_error(const struct managesieve_arg *arg)
1229
+ i_panic("Tried to access managesieve_arg type %d as string", arg->type);
1233
+uoff_t _managesieve_arg_literal_size_error(const struct managesieve_arg *arg)
1235
+ i_panic("Tried to access managesieve_arg type %d as literal size", arg->type);
1239
+struct managesieve_arg_list *_managesieve_arg_list_error(const struct managesieve_arg *arg)
1241
+ i_panic("Tried to access managesieve_arg type %d as list", arg->type);
1244
diff -r 894f003d9f5f src/lib-managesieve/managesieve-parser.h
1245
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
1246
+++ b/src/lib-managesieve/managesieve-parser.h Sun May 04 16:00:59 2008 +0200
1248
+#ifndef __MANAGESIEVE_PARSER_H
1249
+#define __MANAGESIEVE_PARSER_H
1252
+ * QUOTED-SPECIALS = <"> / "\"
1254
+#define IS_QUOTED_SPECIAL(c) \
1255
+ ((c) == '"' || (c) == '\\')
1258
+ * ATOM-SPECIALS = "(" / ")" / "{" / SP / CTL / QUOTED-SPECIALS
1260
+#define IS_ATOM_SPECIAL(c) \
1261
+ ((c) == '(' || (c) == ')' || (c) == '{' || \
1262
+ (c) <= 32 || (c) == 0x7f || \
1263
+ IS_QUOTED_SPECIAL(c))
1268
+#define IS_CHAR(c) \
1269
+ (((c) & 0x80) == 0)
1272
+ * TEXT-CHAR = %x01-09 / %x0B-0C / %x0E-7F
1273
+ * ;; any CHAR except CR and LF
1275
+#define IS_TEXT_CHAR(c) \
1276
+ (IS_CHAR(c) && (c) != '\r' && (c) != '\n')
1279
+ * SAFE-CHAR = %x01-09 / %x0B-0C / %x0E-21 /
1280
+ * %x23-5B / %x5D-7F
1281
+ * ;; any TEXT-CHAR except QUOTED-SPECIALS
1283
+#define IS_SAFE_CHAR(c) \
1284
+ (IS_TEXT_CHAR(c) && !IS_QUOTED_SPECIAL(c))
1286
+/* UTF8-1 = %x80-BF
1288
+#define IS_UTF8_1(c) \
1289
+ (((c) & 0xC0) == 0x80)
1291
+/* UTF8-2 = %xC0-DF UTF8-1
1293
+#define IS_UTF8_2S(c) \
1294
+ (((c) & 0xE0) == 0xC0)
1296
+/* UTF8-3 = %xE0-EF 2UTF8-1
1298
+#define IS_UTF8_3S(c) \
1299
+ (((c) & 0xF0) == 0xE0)
1301
+/* UTF8-4 = %xF0-F7 3UTF8-1
1303
+#define IS_UTF8_4S(c) \
1304
+ (((c) & 0xF8) == 0xF0)
1306
+/* UTF8-5 = %xF8-FB 4UTF8-1
1308
+#define IS_UTF8_5S(c) \
1309
+ (((c) & 0xFC) == 0xF8)
1311
+/* UTF8-6 = %xFC-FD 5UTF8-1
1313
+#define IS_UTF8_6S(c) \
1314
+ (((c) & 0xFE) == 0xFC)
1316
+/* SAFE-UTF8-CHAR = SAFE-CHAR / UTF8-2 / UTF8-3 / UTF8-4 /
1319
+#define UTF8_LEN(c) \
1320
+ ( IS_SAFE_CHAR(c) ? 1 : \
1321
+ IS_UTF8_2S(c) ? 2 : \
1322
+ IS_UTF8_3S(c) ? 3 : \
1323
+ IS_UTF8_4S(c) ? 4 : \
1324
+ IS_UTF8_5S(c) ? 5 : \
1325
+ IS_UTF8_6S(c) ? 6 : 0 )
1327
+enum managesieve_parser_flags {
1328
+ /* Set this flag if you wish to read only size of literal argument
1329
+ and not convert literal into string. Useful when you need to deal
1330
+ with large literal sizes. The literal must be the last read
1332
+ MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE = 0x01,
1333
+ /* Don't remove '\' chars from string arguments */
1334
+ MANAGESIEVE_PARSE_FLAG_NO_UNESCAPE = 0x02,
1335
+ /* Return literals as MANAGESIEVE_ARG_LITERAL instead of MANAGESIEVE_ARG_STRING */
1336
+ MANAGESIEVE_PARSE_FLAG_LITERAL_TYPE = 0x04
1339
+enum managesieve_arg_type {
1340
+ MANAGESIEVE_ARG_ATOM = 0,
1341
+ MANAGESIEVE_ARG_STRING,
1343
+ /* literals are returned as MANAGESIEVE_ARG_STRING by default */
1344
+ MANAGESIEVE_ARG_LITERAL,
1345
+ MANAGESIEVE_ARG_LITERAL_SIZE,
1347
+ MANAGESIEVE_ARG_EOL /* end of argument list */
1350
+struct managesieve_parser;
1352
+struct managesieve_arg {
1353
+ enum managesieve_arg_type type;
1357
+ uoff_t literal_size;
1361
+#define MANAGESIEVE_ARG_STR(arg) \
1362
+ ((arg)->type == MANAGESIEVE_ARG_STRING || \
1363
+ (arg)->type == MANAGESIEVE_ARG_ATOM || \
1364
+ (arg)->type == MANAGESIEVE_ARG_LITERAL ? \
1365
+ (arg)->_data.str : _managesieve_arg_str_error(arg))
1367
+#define MANAGESIEVE_ARG_LITERAL_SIZE(arg) \
1368
+ (((arg)->type == MANAGESIEVE_ARG_LITERAL_SIZE) ? \
1369
+ (arg)->_data.literal_size : _managesieve_arg_literal_size_error(arg))
1371
+struct managesieve_arg_list {
1372
+ size_t size, alloc;
1373
+ struct managesieve_arg args[1]; /* variable size */
1377
+/* Create new MANAGESIEVE argument parser. output is used for sending command
1378
+ continuation requests for literals.
1380
+ max_line_size can be used to approximately limit the maximum amount of
1381
+ memory that gets allocated when parsing a line. Input buffer size limits
1382
+ the maximum size of each parsed token.
1384
+ Usually the largest lines are large only because they have a one huge
1385
+ message set token, so you'll probably want to keep input buffer size the
1386
+ same as max_line_size. That means the maximum memory usage is around
1387
+ 2 * max_line_size. */
1388
+struct managesieve_parser *
1389
+managesieve_parser_create(struct istream *input, struct ostream *output,
1390
+ size_t max_line_size);
1391
+void managesieve_parser_destroy(struct managesieve_parser **parser);
1393
+/* Reset the parser to initial state. */
1394
+void managesieve_parser_reset(struct managesieve_parser *parser);
1396
+/* Return the last error in parser. fatal is set to TRUE if there's no way to
1397
+ continue parsing, currently only if too large non-sync literal size was
1399
+const char *managesieve_parser_get_error(struct managesieve_parser *parser, bool *fatal);
1401
+/* Read a number of arguments. This function doesn't call i_stream_read(), you
1402
+ need to do that. Returns number of arguments read (may be less than count
1403
+ in case of EOL), -2 if more data is needed or -1 if error occurred.
1405
+ count-sized array of arguments are stored into args when return value is
1406
+ 0 or larger. If all arguments weren't read, they're set to NIL. count
1407
+ can be set to 0 to read all arguments in the line. Last element in
1408
+ args is always of type MANAGESIEVE_ARG_EOL. */
1409
+int managesieve_parser_read_args(struct managesieve_parser *parser, unsigned int count,
1410
+ enum managesieve_parser_flags flags, struct managesieve_arg **args);
1412
+/* just like managesieve_parser_read_args(), but assume \n at end of data in
1414
+int managesieve_parser_finish_line(struct managesieve_parser *parser, unsigned int count,
1415
+ enum managesieve_parser_flags flags,
1416
+ struct managesieve_arg **args);
1418
+/* Read one word - used for reading tag and command name.
1419
+ Returns NULL if more data is needed. */
1420
+const char *managesieve_parser_read_word(struct managesieve_parser *parser);
1422
+/* Returns the managesieve argument as string. If it is no string this returns NULL */
1423
+const char *managesieve_arg_string(struct managesieve_arg *arg);
1425
+/* Returns 1 if the argument is a number. If it is no number this returns -1.
1426
+ * The number itself is stored in *number.
1428
+int managesieve_arg_number
1429
+ (struct managesieve_arg *arg, uoff_t *number);
1431
+/* Error functions */
1432
+char *_managesieve_arg_str_error(const struct managesieve_arg *arg);
1433
+uoff_t _managesieve_arg_literal_size_error(const struct managesieve_arg *arg);
1434
+struct managesieve_arg_list *_managesieve_arg_list_error(const struct managesieve_arg *arg);
1437
diff -r 894f003d9f5f src/lib-managesieve/managesieve-quote.c
1438
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
1439
+++ b/src/lib-managesieve/managesieve-quote.c Sun May 04 16:00:59 2008 +0200
1443
+#include "managesieve-parser.h"
1444
+#include "managesieve-quote.h"
1446
+/* Turn the value string into a valid MANAGESIEVE string or literal, no matter
1447
+ * what. QUOTED-SPECIALS are escaped, but any invalid (UTF-8) character
1448
+ * is simply removed. Linebreak characters are not considered invalid, but
1449
+ * they do force the generation of a string literal.
1451
+void managesieve_quote_append(string_t *str, const unsigned char *value,
1452
+ size_t value_len, bool compress_lwsp)
1454
+ size_t i, extra = 0;
1462
+ if (value == NULL) {
1463
+ str_append(str, "\"\"");
1467
+ if (value_len == (size_t)-1)
1468
+ value_len = strlen((const char *) value);
1470
+ for (i = 0; i < value_len; i++) {
1471
+ switch (value[i]) {
1474
+ if (last_lwsp && compress_lwsp) {
1483
+ last_lwsp = FALSE;
1491
+ /* Enforce valid UTF-8
1493
+ if ( (utf8_len=UTF8_LEN(value[i])) == 0 ) {
1499
+ if ( utf8_len > 1 ) {
1500
+ int c = utf8_len - 1;
1502
+ if ( (i+utf8_len-1) >= value_len ) {
1503
+ /* Value ends in the middle of a UTF-8 character;
1504
+ * Kill the partial UTF-8 character
1506
+ extra += i + utf8_len - value_len;
1511
+ /* Parse the series of UTF8_1 characters */
1512
+ for (i++; c > 0; c--, i++ ) {
1513
+ if (!IS_UTF8_1(value[i])) {
1514
+ extra += utf8_len - c;
1521
+ last_lwsp = FALSE;
1526
+ /* no linebreak chars, return as (escaped) "string" */
1527
+ str_append_c(str, '"');
1529
+ /* return as literal */
1530
+ str_printfa(str, "{%"PRIuSIZE_T"}\r\n", value_len - extra);
1533
+ if (!modify && (literal || !escape))
1534
+ str_append_n(str, value, value_len);
1537
+ for (i = 0; i < value_len; i++) {
1538
+ switch (value[i]) {
1541
+ last_lwsp = FALSE;
1543
+ str_append_c(str, '\\');
1544
+ str_append_c(str, value[i]);
1548
+ if (!last_lwsp || !compress_lwsp)
1549
+ str_append_c(str, ' ');
1555
+ str_append_c(str, value[i]);
1558
+ /* Enforce valid UTF-8
1560
+ if ( (utf8_len=UTF8_LEN(value[i])) == 0 )
1563
+ if ( utf8_len > 1 ) {
1564
+ int c = utf8_len - 1;
1567
+ if ( (i+utf8_len-1) >= value_len ) {
1568
+ /* Value ends in the middle of a UTF-8 character;
1569
+ * Kill the partial character
1575
+ /* Parse the series of UTF8_1 characters */
1576
+ for (j = i+1; c > 0; c--, j++ ) {
1577
+ if (!IS_UTF8_1(value[j])) {
1578
+ /* Skip until after this erroneous character */
1584
+ /* Append the UTF-8 character. Last octet is done later */
1586
+ for (; c > 0; c--, i++ )
1587
+ str_append_c(str, value[i]);
1590
+ last_lwsp = FALSE;
1591
+ str_append_c(str, value[i]);
1598
+ str_append_c(str, '"');
1601
+char *managesieve_quote(pool_t pool, const unsigned char *value, size_t value_len)
1606
+ if (value == NULL)
1610
+ str = t_str_new(value_len + MAX_INT_STRLEN + 5);
1611
+ managesieve_quote_append(str, value, value_len, TRUE);
1612
+ ret = p_strndup(pool, str_data(str), str_len(str));
1617
diff -r 894f003d9f5f src/lib-managesieve/managesieve-quote.h
1618
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
1619
+++ b/src/lib-managesieve/managesieve-quote.h Sun May 04 16:00:59 2008 +0200
1621
+#ifndef __IMAP_QUOTE_H
1622
+#define __IMAP_QUOTE_H
1624
+/* Return value suitable for sending to client, either as quoted-string or
1625
+ literal. Note that this also converts TABs into spaces, multiple spaces
1626
+ into single space and NULs to #128. */
1627
+char *managesieve_quote(pool_t pool, const unsigned char *value, size_t value_len);
1629
+/* Append to existing string. */
1630
+void managesieve_quote_append(string_t *str, const unsigned char *value,
1631
+ size_t value_len, bool compress_lwsp);
1633
+#define managesieve_quote_append_string(str, value, compress_lwsp) \
1634
+ managesieve_quote_append(str, (const unsigned char *)(value), \
1635
+ (size_t)-1, compress_lwsp)
1638
diff -r 894f003d9f5f src/lib-sieve/Makefile.am
1639
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
1640
+++ b/src/lib-sieve/Makefile.am Sun May 04 16:00:59 2008 +0200
1642
+noinst_LTLIBRARIES = libsieve.la
1647
+ -I$(top_srcdir)/src/lib \
1648
+ -I$(top_srcdir)/src/lib-storage \
1649
+ -I$(top_srcdir)/src/lib-mail \
1650
+ -I$(top_srcdir)/src/lib-sievestorage
1652
+# FIXME: Make this configurable
1653
+libsieve_la_LIBADD = \
1654
+ cmu/libsieve_cmu.la
1656
+libsieve_la_SOURCES = \
1657
+ sieve-implementation.c
1660
+ sieve-implementation.h \
1661
+ sieve-implementation-private.h
1662
diff -r 894f003d9f5f src/lib-sieve/cmu/Makefile.am
1663
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
1664
+++ b/src/lib-sieve/cmu/Makefile.am Sun May 04 16:00:59 2008 +0200
1666
+noinst_LTLIBRARIES = libsieve_cmu.la
1671
+ -I$(top_srcdir)/src/lib \
1672
+ -I$(top_srcdir)/src/lib-sieve \
1673
+ -I$(top_srcdir)/src/lib-sievestorage \
1674
+ -I$(top_srcdir)/src/lib-mail \
1675
+ -I$(top_srcdir)/src/lib-storage \
1676
+ -I$(top_srcdir)/src/deliver
1678
+libsieve_cmu_la_LIBADD = libsieve/libsieve.la
1680
+libsieve_cmu_la_SOURCES = \
1690
diff -r 894f003d9f5f src/lib-sieve/cmu/cmu-sieve.c
1691
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
1692
+++ b/src/lib-sieve/cmu/cmu-sieve.c Sun May 04 16:00:59 2008 +0200
1695
+/* Copyright (C) 2005-2006 Timo Sirainen */
1698
+#include "ioloop.h"
1700
+#include "hostpid.h"
1702
+#include "str-sanitize.h"
1703
+#include "write-full.h"
1704
+#include "libsieve/sieve_interface.h"
1705
+#include "sieve-script.h"
1706
+#include "sieve-implementation-private.h"
1709
+#include <unistd.h>
1710
+#include <sys/stat.h>
1711
+#include <sys/wait.h>
1713
+extern struct sieve_implementation cmu_sieve;
1715
+struct et_list *_et_list = NULL;
1717
+/* data per script */
1718
+typedef struct script_data {
1728
+/* gets the header "head" from msg. */
1729
+static int getheader(void *v, const char *phead, const char ***body)
1731
+ sieve_msgdata_t *m = v;
1733
+ if (phead==NULL) return SIEVE_FAIL;
1734
+ *body = (const char **)sieve_runenv_get_mail_headers(m->context, phead);
1739
+ return SIEVE_FAIL;
1743
+static int getsize(void *mc, int *size)
1745
+ sieve_msgdata_t *md = mc;
1748
+ psize = sieve_runenv_get_mail_size(md->context);
1749
+ if (psize == (uoff_t)-1)
1750
+ return SIEVE_FAIL;
1756
+static int getenvelope(void *mc, const char *field, const char ***contents)
1758
+ array_t ARRAY_DEFINE(values, const char *);
1759
+ sieve_msgdata_t *m = (sieve_msgdata_t *) mc;
1762
+ // FIXME: Should prob. not put this on the default pool
1763
+ ARRAY_CREATE(&values, default_pool, const char *, 1);
1765
+ if (!sieve_runenv_get_envelope(m->context, field, &values) ) {
1767
+ return SIEVE_FAIL;
1771
+ array_append(&values, &data, 1);
1773
+ *contents = array_count(&values) == 1 ? NULL :
1774
+ array_get_modifyable(&values, NULL);
1776
+ return *contents != NULL ? SIEVE_OK : SIEVE_FAIL;
1779
+static int sieve_redirect(void *ac,
1780
+ void *ic __attr_unused__,
1781
+ void *sc __attr_unused__, void *mc, const char **errmsg)
1783
+ sieve_redirect_context_t *rc = (sieve_redirect_context_t *) ac;
1784
+ sieve_msgdata_t *m = mc;
1785
+ const char *dupeid;
1788
+ /* if we have a msgid, we can track our redirects */
1789
+ dupeid = m->id == NULL ? NULL : t_strdup_printf("%s-%s", m->id, rc->addr);
1790
+ if (dupeid != NULL) {
1791
+ /* ok, let's see if we've redirected this message before */
1792
+ if (sieve_runenv_is_duplicate(m->context, dupeid, strlen(dupeid))) {
1793
+ /*duplicate_log(m->id, sd->username, "redirect");*/
1794
+ i_info("discarded duplicate forward (%s -> %s)",
1795
+ str_sanitize(m->id, 80), str_sanitize(rc->addr, 80));
1800
+ if ((res = sieve_runenv_send_forward(m->context, rc->addr)) == 0) {
1801
+ /* mark this message as redirected */
1802
+ i_info("forwarded id %s to <%s>",
1803
+ m->id == NULL ? "" : str_sanitize(m->id, 80),
1804
+ str_sanitize(rc->addr, 80));
1806
+ if (dupeid != NULL) {
1807
+ sieve_runenv_mark_duplicate(m->context,dupeid, strlen(dupeid),
1808
+ DUPLICATE_DEFAULT_KEEP);
1813
+ *errmsg = "Error sending mail";
1814
+ return SIEVE_FAIL;
1817
+static int sieve_discard(void *ac __attr_unused__,
1818
+ void *ic __attr_unused__,
1819
+ void *sc __attr_unused__, void *mc,
1820
+ const char **errmsg __attr_unused__)
1822
+ sieve_msgdata_t *md = mc;
1824
+ /* ok, we won't file it, but log it */
1825
+ i_info("discarded id %s", md->id == NULL ? "" : str_sanitize(md->id, 80));
1829
+static int sieve_reject(void *ac,
1830
+ void *ic __attr_unused__,
1831
+ void *sc __attr_unused__,
1832
+ void *mc, const char **errmsg)
1834
+ sieve_reject_context_t *rc = (sieve_reject_context_t *) ac;
1835
+ sieve_msgdata_t *md = (sieve_msgdata_t *) mc;
1838
+ if ((res = sieve_runenv_send_rejection(md->context, rc->msg)) == 0) {
1839
+ i_info("rejected id %s",
1840
+ md->id == NULL ? "" : str_sanitize(md->id, 80));
1843
+ *errmsg = "Error sending mail";
1844
+ return SIEVE_FAIL;
1846
+ return SIEVE_FAIL;
1849
+static int sieve_fileinto(void *ac,
1850
+ void *ic __attr_unused__,
1851
+ void *sc __attr_unused__,
1853
+ const char **errmsg __attr_unused__)
1855
+ sieve_fileinto_context_t *fc = (sieve_fileinto_context_t *) ac;
1856
+ sieve_msgdata_t *md = (sieve_msgdata_t *) mc;
1858
+ if (sieve_runenv_mail_save
1859
+ (md->context, fc->mailbox, fc->imapflags->flag,
1860
+ fc->imapflags->nflags) < 0)
1861
+ return SIEVE_FAIL;
1863
+ i_info("saved mail to %s", fc->mailbox);
1867
+static int sieve_keep(void *ac,
1868
+ void *ic __attr_unused__,
1869
+ void *sc __attr_unused__,
1870
+ void *mc, const char **errmsg __attr_unused__)
1872
+ sieve_keep_context_t *kc = (sieve_keep_context_t *) ac;
1873
+ sieve_msgdata_t *md = (sieve_msgdata_t *) mc;
1875
+ if (sieve_runenv_mail_save
1876
+ (md->context, NULL, kc->imapflags->flag,
1877
+ kc->imapflags->nflags) < 0)
1878
+ return SIEVE_FAIL;
1883
+static int sieve_notify(void *ac __attr_unused__,
1884
+ void *interp_context __attr_unused__,
1885
+ void *script_context __attr_unused__,
1886
+ void *mc __attr_unused__,
1887
+ const char **errmsg __attr_unused__)
1889
+ return SIEVE_FAIL;
1892
+static int autorespond(void *ac,
1893
+ void *ic __attr_unused__,
1894
+ void *sc __attr_unused__,
1896
+ const char **errmsg __attr_unused__)
1898
+ sieve_autorespond_context_t *arc = (sieve_autorespond_context_t *) ac;
1899
+ sieve_msgdata_t *md = (sieve_msgdata_t *) mc;
1902
+ /* ok, let's see if we've responded before */
1903
+ ret = sieve_runenv_is_duplicate(md->context, arc->hash, arc->len) ?
1904
+ SIEVE_DONE : SIEVE_OK;
1906
+ if (ret == SIEVE_OK) {
1907
+ sieve_runenv_mark_duplicate(md->context, arc->hash, arc->len,
1908
+ arc->days * (24 * 60 * 60));
1914
+static int send_response(void *ac,
1915
+ void *ic __attr_unused__,
1916
+ void *sc __attr_unused__,
1918
+ const char **errmsg)
1920
+ sieve_send_response_context_t *src = (sieve_send_response_context_t *) ac;
1921
+ sieve_msgdata_t *md = mc;
1922
+ const char *outmsgid;
1924
+ if (sieve_runenv_send_message
1925
+ (md->context, src->fromaddr, src->addr,
1926
+ src->subj, src->mime, src->msg, &outmsgid) == 0) {
1927
+ sieve_runenv_mark_duplicate
1928
+ (md->context, outmsgid, strlen(outmsgid), DUPLICATE_DEFAULT_KEEP);
1931
+ *errmsg = "Error sending mail";
1932
+ return SIEVE_FAIL;
1936
+/* vacation support */
1937
+sieve_vacation_t vacation = {
1938
+ 1, /* min response */
1939
+ 31, /* max response */
1940
+ &autorespond, /* autorespond() */
1941
+ &send_response /* send_response() */
1944
+/* imapflags support */
1945
+static char *markflags[] = { "\\flagged" };
1946
+static sieve_imapflags_t mark = { markflags, 1 };
1948
+static int sieve_parse_error_handler(int lineno, const char *msg,
1949
+ void *ic __attr_unused__,
1952
+ script_data_t *sd = (script_data_t *) sc;
1954
+ if (sd->errors == NULL)
1955
+ sd->errors = str_new(default_pool, 1024);
1957
+ if (sd->num_errors == 1)
1958
+ str_append(sd->errors, "\r\n");
1960
+ str_printfa(sd->errors, "line %d: %s", lineno, msg);
1962
+ if (sd->num_errors >= 1)
1963
+ str_append(sd->errors, "\r\n");
1969
+static int sieve_execute_error_handler(const char *msg,
1970
+ void *ic __attr_unused__,
1971
+ void *sc __attr_unused__,
1972
+ void *mc __attr_unused__)
1974
+ i_info("sieve runtime error: %s", msg);
1978
+static sieve_interp_t *setup_sieve(void)
1980
+ sieve_interp_t *interp = NULL;
1983
+ res = sieve_interp_alloc(&interp, NULL);
1984
+ if (res != SIEVE_OK)
1985
+ i_fatal("sieve_interp_alloc() returns %d\n", res);
1987
+ res = sieve_register_redirect(interp, &sieve_redirect);
1988
+ if (res != SIEVE_OK)
1989
+ i_fatal("sieve_register_redirect() returns %d\n", res);
1990
+ res = sieve_register_discard(interp, &sieve_discard);
1991
+ if (res != SIEVE_OK)
1992
+ i_fatal("sieve_register_discard() returns %d\n", res);
1993
+ res = sieve_register_reject(interp, &sieve_reject);
1994
+ if (res != SIEVE_OK)
1995
+ i_fatal("sieve_register_reject() returns %d\n", res);
1996
+ res = sieve_register_fileinto(interp, &sieve_fileinto);
1997
+ if (res != SIEVE_OK)
1998
+ i_fatal("sieve_register_fileinto() returns %d\n", res);
1999
+ res = sieve_register_keep(interp, &sieve_keep);
2000
+ if (res != SIEVE_OK)
2001
+ i_fatal("sieve_register_keep() returns %d\n", res);
2002
+ res = sieve_register_imapflags(interp, &mark);
2003
+ if (res != SIEVE_OK)
2004
+ i_fatal("sieve_register_imapflags() returns %d\n", res);
2005
+ res = sieve_register_notify(interp, &sieve_notify);
2006
+ if (res != SIEVE_OK)
2007
+ i_fatal("sieve_register_notify() returns %d\n", res);
2008
+ res = sieve_register_size(interp, &getsize);
2009
+ if (res != SIEVE_OK)
2010
+ i_fatal("sieve_register_size() returns %d\n", res);
2011
+ res = sieve_register_header(interp, &getheader);
2012
+ if (res != SIEVE_OK)
2013
+ i_fatal("sieve_register_header() returns %d\n", res);
2015
+ res = sieve_register_envelope(interp, &getenvelope);
2016
+ if (res != SIEVE_OK)
2017
+ i_fatal("sieve_register_envelope() returns %d\n", res);
2018
+ res = sieve_register_vacation(interp, &vacation);
2019
+ if (res != SIEVE_OK)
2020
+ i_fatal("sieve_register_vacation() returns %d\n", res);
2021
+ res = sieve_register_parse_error(interp, &sieve_parse_error_handler);
2022
+ if (res != SIEVE_OK)
2023
+ i_fatal("sieve_register_parse_error() returns %d\n", res);
2024
+ res = sieve_register_execute_error(interp, &sieve_execute_error_handler);
2025
+ if (res != SIEVE_OK)
2026
+ i_fatal("sieve_register_execute_error() returns %d\n", res);
2032
+_cmu_sieve_compile(sieve_interp_t *interp, script_data_t *sdata,
2033
+ const char *script_path, const char *compiled_path)
2035
+ struct stat st, st2;
2036
+ sieve_script_t *script;
2037
+ bytecode_info_t *bc;
2038
+ const char *temp_path;
2042
+ if (stat(script_path, &st) < 0) {
2043
+ if (errno == ENOENT)
2045
+ i_error("stat(%s) failed: %m", script_path);
2048
+ if (compiled_path != NULL) {
2049
+ if (stat(compiled_path, &st2) < 0) {
2050
+ if (errno != ENOENT) {
2051
+ i_error("stat(%s) failed: %m", script_path);
2055
+ if (st.st_mtime < st2.st_mtime)
2060
+ /* need to compile */
2061
+ f = fopen(script_path, "r");
2063
+ i_error("fopen(%s) failed: %m", script_path);
2067
+ ret = sieve_script_parse(interp, f, sdata, &script);
2068
+ if (ret != SIEVE_OK) {
2069
+ if (sdata->errors == NULL)
2070
+ sieve_set_error("parse error %d", ret);
2072
+ sieve_set_error("%s", str_c(sdata->errors));
2077
+ if (compiled_path != NULL) {
2078
+ if (sieve_generate_bytecode(&bc, script) < 0) {
2079
+ i_error("sieve_generate_bytecode() failed");
2083
+ /* write to temp file */
2084
+ temp_path = t_strconcat(compiled_path, ".tmp", NULL);
2085
+ fd = open(temp_path, O_CREAT | O_TRUNC | O_WRONLY, 0600);
2087
+ i_error("open(%s) failed: %m", temp_path);
2091
+ if (sieve_emit_bytecode(fd, bc) < 0) {
2092
+ i_error("sieve_emit_bytecode() failed");
2096
+ if (close(fd) < 0)
2097
+ i_error("close() failed: %m");
2099
+ /* and finally replace the script */
2100
+ if (rename(temp_path, compiled_path) < 0) {
2101
+ i_error("rename(%s, %s) failed: %m", temp_path, compiled_path);
2109
+static int cmu_sieve_compile
2110
+ (struct sieve_script *script, bool verify_only __attr_unused__)
2112
+ const char *script_path;
2113
+ sieve_interp_t *interp;
2114
+ script_data_t sdata;
2117
+ script_path = sieve_script_filename(script);
2119
+ if (script_path == NULL)
2122
+ interp = setup_sieve();
2124
+ memset(&sdata, 0, sizeof(sdata));
2126
+ ret = _cmu_sieve_compile(interp, &sdata, script_path, NULL);
2128
+ /* Let's not assume that an error string is created only when an error
2129
+ * occurs...better safe than sorry
2131
+ if (sdata.errors != NULL)
2132
+ str_free(&sdata.errors);
2137
+static int cmu_sieve_run
2138
+ (struct sieve_script *script, void *context)
2140
+ const char *script_path;
2141
+ sieve_interp_t *interp;
2142
+ sieve_bytecode_t *bytecode;
2143
+ script_data_t sdata;
2144
+ sieve_msgdata_t mdata;
2145
+ const char *compiled_path, *path;
2148
+ script_path = sieve_script_filename(script);
2150
+ if (script_path == NULL)
2153
+ interp = setup_sieve();
2155
+ memset(&sdata, 0, sizeof(sdata));
2157
+ compiled_path = t_strconcat(script_path, "c", NULL);
2158
+ ret = _cmu_sieve_compile(interp, &sdata, script_path, compiled_path);
2160
+ if (sdata.errors != NULL) {
2161
+ path = t_strconcat(script_path, ".err", NULL);
2162
+ //dovecot_sieve_write_error_file(&sdata, path);
2163
+ str_free(&sdata.errors);
2168
+ memset(&mdata, 0, sizeof(mdata));
2169
+ mdata.context = context;
2170
+ mdata.id = sieve_runenv_get_mail_first_header(context, "Message-ID");
2172
+ if ((ret = sieve_script_load(compiled_path, &bytecode)) != SIEVE_OK) {
2173
+ i_error("sieve_script_load(%s) failed: %d", compiled_path, ret);
2177
+ if (sieve_execute_bytecode(bytecode, interp,
2178
+ &sdata, &mdata) != SIEVE_OK)
2184
+struct sieve_implementation cmu_sieve = {
2185
+ MEMBER(name) "cmu",
2186
+ { cmu_sieve_compile,
2188
+ sieve_listextensions
2192
diff -r 894f003d9f5f src/lib-sieve/cmu/imparse.c
2193
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
2194
+++ b/src/lib-sieve/cmu/imparse.c Sun May 04 16:00:59 2008 +0200
2197
+ * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
2199
+ * Redistribution and use in source and binary forms, with or without
2200
+ * modification, are permitted provided that the following conditions
2203
+ * 1. Redistributions of source code must retain the above copyright
2204
+ * notice, this list of conditions and the following disclaimer.
2206
+ * 2. Redistributions in binary form must reproduce the above copyright
2207
+ * notice, this list of conditions and the following disclaimer in
2208
+ * the documentation and/or other materials provided with the
2211
+ * 3. The name "Carnegie Mellon University" must not be used to
2212
+ * endorse or promote products derived from this software without
2213
+ * prior written permission. For permission or any other legal
2214
+ * details, please contact
2215
+ * Office of Technology Transfer
2216
+ * Carnegie Mellon University
2217
+ * 5000 Forbes Avenue
2218
+ * Pittsburgh, PA 15213-3890
2219
+ * (412) 268-4387, fax: (412) 268-7395
2220
+ * tech-transfer@andrew.cmu.edu
2222
+ * 4. Redistributions of any form whatsoever must retain the following
2224
+ * "This product includes software developed by Computing Services
2225
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
2227
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
2228
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
2229
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
2230
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2231
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
2232
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
2233
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2237
+#include "imparse.h"
2239
+int imparse_isatom(const char *s)
2243
+ if (!*s) return 0;
2246
+ if (*s & 0x80 || *s < 0x1f || *s == 0x7f ||
2247
+ *s == ' ' || *s == '{' || *s == '(' || *s == ')' ||
2248
+ *s == '\"' || *s == '%' || *s == '*' || *s == '\\') return 0;
2250
+ if (len >= 1024) return 0;
2253
diff -r 894f003d9f5f src/lib-sieve/cmu/imparse.h
2254
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
2255
+++ b/src/lib-sieve/cmu/imparse.h Sun May 04 16:00:59 2008 +0200
2257
+#ifndef __IMPARSE_H
2258
+#define __IMPARSE_H
2260
+extern int imparse_isatom (const char *s);
2263
diff -r 894f003d9f5f src/lib-sieve/cmu/libconfig.h
2264
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
2265
+++ b/src/lib-sieve/cmu/libconfig.h Sun May 04 16:00:59 2008 +0200
2267
+#ifndef __LIBCONFIG_H
2268
+#define __LIBCONFIG_H
2270
+#define IMAPOPT_RFC3028_STRICT 1
2272
+#define config_getswitch(n) 1
2275
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/AUTHORS
2276
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
2277
+++ b/src/lib-sieve/cmu/libsieve/AUTHORS Sun May 04 16:00:59 2008 +0200
2281
+Larry Greenfield <leg+sieve@andrew.cmu.edu> wrote the first pass.
2283
+Alexy Melnikov <alexey.melnikov@isode.com> submitted some bug fixes and
2286
+Ken Murchison <ken@oceana.com> took the ball, added more extensions
2287
+than existed in the known world, and overall improved the code mightily.
2288
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/Makefile.am
2289
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
2290
+++ b/src/lib-sieve/cmu/libsieve/Makefile.am Sun May 04 16:00:59 2008 +0200
2292
+pkglibexecdir = $(libexecdir)/dovecot
2294
+noinst_LTLIBRARIES = libsieve.la
2296
+AM_YFLAGS = -d -p $*
2300
+ -I$(top_srcdir)/src/lib \
2303
+addr-lex.c: addr-lex.l
2304
+ $(LEX) -t -Paddr addr-lex.l > addr-lex.c
2306
+sieve-lex.c: sieve-lex.l
2307
+ $(LEX) -t sieve-lex.l > sieve-lex.c
2309
+libsieve_la_SOURCES = \
2336
+ sieve_interface.h \
2339
+pkglibexec_PROGRAMS = sievec sieved
2352
+ $(top_srcdir)/src/lib/liblib.a
2356
+ $(top_srcdir)/src/lib/liblib.a
2367
+ $(notbuilt_sources)
2368
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/NEWS
2369
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
2370
+++ b/src/lib-sieve/cmu/libsieve/NEWS Sun May 04 16:00:59 2008 +0200
2377
+- Compliant with RFC 3028. As a result, fileinto and redirect only
2378
+ accept a single string and NOT a string-list.
2380
+- Compliant with draft-martin-sieve-notify-01. As a result, notify
2381
+ actions will need to be updated to the new syntax.
2386
+- Compliant with draft-showalter-sieve-11.txt and
2387
+ draft-showalter-sieve-vacation-03.txt.
2389
+- Added support for the regex, imapflags, notify and subaddress extensions.
2390
+ See README for references.
2392
+- Verifies email addresses in redirect and vacation actions are syntactically
2393
+ correct (compliant with RFC822).
2395
+- Run-time error reporting.
2397
+- Changed callback interface to use callback contexts instead of individual
2398
+ parameters. Also added an error string buffer for run-time error reporting.
2400
+- Vacation will not reply to any message containing an "auto-submitted"
2401
+ header containing anything other than "no".
2406
+Now included with imapd distribution (hell, why not?).
2408
+Error returning and recovering:
2409
+ added error recovering to the parser (but not much!)
2410
+ added error messages to the parser
2412
+Working on error returning and error recovering.
2414
+ detect some errors in lexer?
2416
+Working on even better parsing:
2417
+ verify addresses could be addresses
2418
+ verify mailboxes could be mailboxes
2419
+ verify outgoing headers can be headers
2424
+Changed for integration with cyrus deliver.
2429
+Added additional callbacks (ok, so I want to make my integration with deliver
2430
+easier) and envelope and vacation support.
2432
+Made it compile without libcyrus.
2433
+It should compile without libcyrus, but then it does not implement the
2434
+"address" test. That's just too much work to do when I have a neato
2435
+library to do it for me.
2443
+- Updated to draft-showalter-sieve-07bis.txt
2445
+- Simple API (see sieve_interface.h; currently mostly undocumented)
2447
+- Implements all of the optional features except "envelope"
2449
+- Maintains "if it parses, it probably runs" behavior. (Goal: minimize
2455
+- prototype implementation
2456
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/README
2457
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
2458
+++ b/src/lib-sieve/cmu/libsieve/README Sun May 04 16:00:59 2008 +0200
2465
+This code is typically distributed as part of Cyrus imapd 1.6 and higher.
2466
+This code will be configured and compiled from the cyrus-imapd directory.
2468
+Notes on implementation
2469
+-----------------------
2471
+This is an implementation of a simple Sieve API. This API is
2472
+well-suited for incorporating in other programs, but is not
2473
+extensible. (If there is interest, we may implement an extensible API
2476
+If you wish to compile Sieve without compiling all of imapd, you'll
2477
+have to create a Makefile for it. I recommend you use Makefile.in as
2480
+It should compile without libcyrus, but then it does not implement the
2481
+"address" test. That's just too much work to do when I have a neato
2482
+library to do it for me.
2484
+There's a simple "test" application included, which is not built by
2485
+default (type "make test" to build it). It expects:
2487
+test <message> <script>
2489
+And prints out the actions taken or errors encountered. (This
2490
+implementation will attempt all the actions or no actions.)
2492
+Questions and comments to:
2493
+Derrick Brashear (shadow+sieve@andrew.cmu.edu)
2497
+[SIEVE] Showalter, T., "Sieve: A Mail Filtering Language",
2498
+RFC 3028, January, 2001.
2500
+[VACATION] Showalter, T., "Sieve: Vacation Extension",
2501
+draft-showalter-sieve-vacation-04.txt, August, 2000.
2503
+[IMAPFLAGS] Melnikov, A., "Sieve -- IMAP flag extension",
2504
+draft-melnikov-sieve-imapflags-03.txt, July, 2000.
2506
+[NOTIFY] Martin, T., Segmuller, W.,
2507
+"Sieve -- An extension for providing instant notifications",
2508
+draft-martin-sieve-notify-01.txt, June, 2001.
2510
+[REGEX] Murchison, K., "Sieve: Regular Expression Extension",
2511
+draft-murchison-sieve-regex-04.txt, August, 2001.
2513
+[RELATIONAL] Segmuller, W., "Sieve Extension: Relational Tests",
2514
+RFC 3431, December 2002.
2516
+[SUBADDR] Murchison, K., "Sieve Email Filtering -- Subaddress Extension",
2517
+RFC 3598, September 2003.
2519
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/addr-lex.l
2520
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
2521
+++ b/src/lib-sieve/cmu/libsieve/addr-lex.l Sun May 04 16:00:59 2008 +0200
2525
+ * addr-lex.l -- RFC 822 address lexer
2529
+/***********************************************************
2530
+ Copyright 1999 by Carnegie Mellon University
2532
+ All Rights Reserved
2534
+Permission to use, copy, modify, and distribute this software and its
2535
+documentation for any purpose and without fee is hereby granted,
2536
+provided that the above copyright notice appear in all copies and that
2537
+both that copyright notice and this permission notice appear in
2538
+supporting documentation, and that the name of Carnegie Mellon
2539
+University not be used in advertising or publicity pertaining to
2540
+distribution of the software without specific, written prior
2543
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
2544
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
2545
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
2546
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2547
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2548
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
2549
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2550
+******************************************************************/
2553
+#include <string.h>
2556
+#define YY_INPUT(b, r, ms) (r = addrinput(b, ms))
2558
+int addrinput(char *buf, int max_size);
2559
+void addrerror(const char *);
2561
+static int ncom; /* number of open comments */
2566
+%option prefix="addr"
2568
+%x QSTRING DOMAINLIT COMMENT
2572
+\" { BEGIN QSTRING; return yytext[0]; }
2573
+\[ { BEGIN DOMAINLIT; return yytext[0]; }
2574
+\( { ncom = 1; BEGIN COMMENT; }
2575
+\) { addrerror("address parse error, "
2576
+ "unexpected `')'' "
2577
+ "(unbalanced comment)");
2580
+[^\(\)<>@,;:\\".\[\] \n\r]+ return ATOM;
2582
+[\t \n\r]+ /* ignore whitespace */
2583
+. return yytext[0];
2585
+<QSTRING>([^\n\r"\\]|\\.)* return QTEXT;
2586
+<QSTRING>\" { BEGIN INITIAL; return yytext[0]; }
2588
+<DOMAINLIT>([^\[\]\n\r\\]|\\.)* return DTEXT;
2589
+<DOMAINLIT>\] { BEGIN INITIAL; return yytext[0]; }
2591
+<COMMENT>([^\(\)\n\0\\]|\\.)* /* ignore comments */
2592
+<COMMENT>\( ncom++;
2593
+<COMMENT>\) { ncom--; if (ncom == 0) BEGIN INITIAL; }
2594
+<COMMENT><<EOF>> { addrerror("address parse error, "
2595
+ "expecting `')'' "
2596
+ "(unterminated comment)");
2601
+/* take input from address string provided by sieve parser */
2602
+int addrinput(char *buf, int max_size)
2604
+ extern char *addrptr; /* current position in address string */
2605
+ size_t n; /* number of characters to read from string */
2607
+ n = (int)strlen(addrptr) < max_size ? (int)strlen(addrptr) : max_size;
2609
+ memcpy(buf, addrptr, n);
2614
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/addr.y
2615
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
2616
+++ b/src/lib-sieve/cmu/libsieve/addr.y Sun May 04 16:00:59 2008 +0200
2620
+ * addr.y -- RFC 822 address parser
2624
+/***********************************************************
2625
+ Copyright 1999 by Carnegie Mellon University
2627
+ All Rights Reserved
2629
+Permission to use, copy, modify, and distribute this software and its
2630
+documentation for any purpose and without fee is hereby granted,
2631
+provided that the above copyright notice appear in all copies and that
2632
+both that copyright notice and this permission notice appear in
2633
+supporting documentation, and that the name of Carnegie Mellon
2634
+University not be used in advertising or publicity pertaining to
2635
+distribution of the software without specific, written prior
2638
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
2639
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
2640
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
2641
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2642
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2643
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
2644
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2645
+******************************************************************/
2647
+#include <stdlib.h>
2648
+#include <string.h>
2651
+#include "script.h"
2652
+#include "xmalloc.h"
2654
+int addrerror(char *msg);
2655
+extern int yylex(void);
2657
+#define YYERROR_VERBOSE /* i want better error messages! */
2660
+%token ATOM QTEXT DTEXT
2663
+sieve_address: addrspec /* simple address */
2664
+ | phrase '<' addrspec '>' /* name & addr-spec */
2667
+addrspec: localpart '@' domain /* global-address */
2670
+localpart: word /* uninterpreted, case-preserved */
2671
+ | word '.' localpart
2675
+ | subdomain '.' domain
2678
+subdomain: domainref
2682
+domainref: ATOM /* symbolic reference */
2685
+domainlit: '[' DTEXT ']'
2696
+qstring: '"' QTEXT '"'
2701
+/* copy address error message into buffer provided by sieve parser */
2702
+int addrerror(char *s)
2704
+ extern char addrerr[ADDRERR_SIZE];
2706
+ strlcpy(addrerr, s, sizeof(addrerr));
2709
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/bc_dump.c
2710
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
2711
+++ b/src/lib-sieve/cmu/libsieve/bc_dump.c Sun May 04 16:00:59 2008 +0200
2713
+/* bc_generate.c -- sieve bytecode- almost flattened bytecode
2717
+/***********************************************************
2718
+ Copyright 2001 by Carnegie Mellon University
2720
+ All Rights Reserved
2722
+Permission to use, copy, modify, and distribute this software and its
2723
+documentation for any purpose and without fee is hereby granted,
2724
+provided that the above copyright notice appear in all copies and that
2725
+both that copyright notice and this permission notice appear in
2726
+supporting documentation, and that the name of Carnegie Mellon
2727
+University not be used in advertising or publicity pertaining to
2728
+distribution of the software without specific, written prior
2731
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
2732
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
2733
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
2734
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
2735
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
2736
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
2737
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
2738
+******************************************************************/
2740
+#ifdef HAVE_CONFIG_H
2741
+#include <config.h>
2744
+#include "sieve_interface.h"
2745
+#include "bytecode.h"
2748
+struct bytecode_info
2750
+ bytecode_t *data;/* pointer to almost-flat bytecode */
2751
+ size_t scriptend; /* used by emit code to know final length of bytecode */
2752
+ size_t reallen; /* allocated length of 'data' */
2757
+/*this would work a lot better if we actually could tell how many levels deep in if statements we were. currently it doesn't know*/
2759
+static void print_spaces(int n)
2762
+ while(temp_n++ < (n))
2767
+/* Dump a stringlist. Return the last address used by the list */
2768
+static int dump_sl(bytecode_info_t *d, int ip, int level)
2770
+ int numstr = d->data[ip].listlen;
2773
+ for(i=0; i<numstr; i++) {
2774
+ print_spaces(level*4);
2775
+ printf(" {%d}",d->data[++ip].len);
2776
+ printf("%s\n",d->data[++ip].str);
2782
+static int dump_test(bytecode_info_t *d, int ip, int level);
2784
+/* Dump a testlist. Return the last address used by the list */
2785
+static int dump_tl(bytecode_info_t *d, int ip, int level)
2787
+ int numtest = d->data[ip].listlen;
2790
+ for(i=0; i<numtest; i++) {
2791
+ print_spaces(level*4);
2792
+ printf(" (until %d)\n", d->data[++ip].jump);
2793
+ ip = dump_test(d, ++ip, level);
2799
+/* Dump a test, return the last address used by the test */
2800
+static int dump_test(bytecode_info_t *d, int ip, int level ) {
2802
+ print_spaces(level*4);
2803
+ switch(d->data[ip].op) {
2805
+ printf("%d: TRUE\n",ip);
2809
+ printf("%d: FALSE\n",ip);
2813
+ printf("%d: NOT TEST(\n",ip++);
2814
+ /* printf(" (until %d)\n", d->data[ip++].jump);*/
2815
+ ip = dump_test(d,ip, level);
2816
+ print_spaces(level*4);
2821
+ printf("%d: SIZE TAG(%d) NUM(%d)\n",ip,
2822
+ d->data[ip+1].value, d->data[ip+2].value);
2827
+ printf("%d: EXISTS\n",ip++);
2828
+ ip = dump_sl(d,ip,level);
2832
+ printf("%d: ALLOF (\n",ip++);
2833
+ ip = dump_tl(d,ip,level);
2834
+ print_spaces(level*4);
2839
+ printf("%d: ANYOF (\n",ip++);
2840
+ ip = dump_tl(d,ip, level);
2841
+ print_spaces(level*4);
2846
+ printf("%d: HEADER (\n",ip++);
2847
+ print_spaces(level*4);
2848
+ if (d->data[ip].value == B_COUNT || d->data[ip].value == B_VALUE)
2850
+ printf(" MATCH:%d RELATION:%d COMP:%d HEADERS:\n",
2851
+ d->data[ip].value, d->data[ip+1].value,d->data[ip+2].value);
2853
+ printf(" MATCH:%d COMP:%d HEADERS:\n",d->data[ip].value, d->data[ip+2].value);
2856
+ ip = dump_sl(d,ip,level);
2858
+ print_spaces(level*4);
2859
+ printf(" DATA:\n");
2860
+ ip = dump_sl(d,ip,level);
2865
+ printf("%d: %s (\n",ip++,
2866
+ d->data[ip].op == BC_ADDRESS ? "ADDRESS" : "ENVELOPE");
2867
+ print_spaces(level*4);
2868
+ if (d->data[ip].value == B_COUNT || d->data[ip].value == B_VALUE)
2870
+ printf(" MATCH:%d RELATION: %d COMP: %d TYPE: %d HEADERS:\n",
2871
+ d->data[ip].value, d->data[ip+1].value, d->data[ip+2].value, d->data[ip+3].value);
2873
+ printf(" MATCH:%d COMP:%d TYPE:%d HEADERS:\n",
2874
+ d->data[ip].value,d->data[ip+1].value,d->data[ip+3].value);
2877
+ ip = dump_sl(d,ip,level); ip++;
2878
+ print_spaces(level*4);
2879
+ printf(" DATA:\n");
2880
+ ip = dump_sl(d,ip,level);
2884
+ printf("%d: TEST(%d)\n",ip,d->data[ip].op);
2891
+void dump(bytecode_info_t *d, int level)
2894
+ printf("Dumping almost flattened bytecode\n\n");
2898
+ for(i=0; i<d->scriptend; i++) {
2899
+ print_spaces(level*4);
2900
+ switch(d->data[i].op) {
2902
+ printf("%d: REJECT {%d}%s\n",i,
2903
+ d->data[i+1].len,d->data[i+2].str);
2907
+ if (d->data[i+3].jump== -1)
2909
+ printf("%d: IF THEN(%d) POST(%d) TEST(\n",i,
2910
+ d->data[i+1].jump,d->data[i+2].jump);
2914
+ printf("%d: IF THEN(%d) ELSE(%d) POST(%d) TEST(\n",i,
2915
+ d->data[i+1].jump,d->data[i+2].jump,
2916
+ d->data[i+3].jump);
2918
+ i = dump_test(d,i+4, level+1);
2923
+ printf("%d: STOP\n",i);
2927
+ printf("%d: DISCARD\n",i);
2931
+ printf("%d: KEEP\n",i);
2935
+ printf("%d: MARK\n",i);
2939
+ printf("%d: UNMARK\n",i);
2943
+ printf("%d: FILEINTO {%d}%s\n",i,
2944
+ d->data[i+1].len,d->data[i+2].str);
2949
+ printf("%d: REDIRECT {%d}%s\n",i,
2950
+ d->data[i+1].len,d->data[i+2].str);
2955
+ printf("%d: SETFLAG\n",i);
2956
+ i=dump_sl(d,++i, level);
2960
+ printf("%d: ADDFLAG\n",i);
2961
+ i=dump_sl(d,++i,level);
2964
+ case B_REMOVEFLAG:
2965
+ printf("%d: REMOVEFLAG\n",i);
2966
+ i=dump_sl(d,++i,level);
2970
+ printf("%d: DENOTIFY priority %d,comp %d %d %s\n",
2972
+ d->data[i+1].value,
2973
+ d->data[i+2].value,
2974
+ d->data[i+3].value,
2975
+ (d->data[i+4].len == -1 ? "[nil]" : d->data[i+5].str));
2980
+ printf("%d: NOTIFY\n METHOD(%s),\n ID(%s),\n OPTIONS",
2983
+ (d->data[i+3].len == -1 ? "[nil]" : d->data[i+4].str));
2985
+ i=dump_sl(d,i,level);
2986
+ printf(" PRIORITY(%d),\n MESSAGE({%d}%s)\n",
2987
+ d->data[i+1].value, d->data[i+2].len,d->data[i+3].str);
2992
+ printf("%d:VACATION\n",i);
2994
+ i=dump_sl(d,i,level);
2995
+ printf("SUBJ({%d}%s) MESG({%d}%s)\n DAYS(%d) MIME(%d)\n",
2996
+ d->data[i+1].len, (d->data[i+1].len == -1 ? "[nil]" : d->data[i+2].str),
2997
+ d->data[i+3].len, (d->data[i+3].len == -1 ? "[nil]" : d->data[i+4].str),
2998
+ d->data[i+5].value, d->data[i+6].value);
3003
+ printf("%d: JUMP HUH? this shouldn't be here>?!",i);
3006
+ printf("%d: NULL\n",i);
3009
+ printf("%d: %d\n",i,d->data[i].op);
3013
+ printf("full len is: %d\n", d->scriptend);
3017
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/bc_emit.c
3018
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
3019
+++ b/src/lib-sieve/cmu/libsieve/bc_emit.c Sun May 04 16:00:59 2008 +0200
3021
+/* bc_emit.c -- sieve bytecode - pass 2 of the compiler
3026
+/***********************************************************
3027
+ Copyright 2001 by Carnegie Mellon University
3029
+ All Rights Reserved
3031
+Permission to use, copy, modify, and distribute this software and its
3032
+documentation for any purpose and without fee is hereby granted,
3033
+provided that the above copyright notice appear in all copies and that
3034
+both that copyright notice and this permission notice appear in
3035
+supporting documentation, and that the name of Carnegie Mellon
3036
+University not be used in advertising or publicity pertaining to
3037
+distribution of the software without specific, written prior
3040
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
3041
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
3042
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
3043
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
3044
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
3045
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
3046
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
3047
+******************************************************************/
3049
+#ifdef HAVE_CONFIG_H
3050
+#include <config.h>
3053
+#include "xmalloc.h"
3054
+#include "sieve_interface.h"
3057
+#include "bytecode.h"
3059
+#include <sys/types.h>
3060
+#include <unistd.h>
3064
+void dump(bytecode_info_t *d);
3067
+static inline int write_int (int fd, int x)
3070
+ return (write(fd, &y, sizeof(int)));
3075
+struct bytecode_info
3077
+ bytecode_t *data;/* pointer to almost-flat bytecode */
3078
+ size_t scriptend; /* used by emit code to know final length of bytecode */
3079
+ size_t reallen; /* allocated length of 'data' */
3082
+/* Pad null bytes onto the end of the string we just wrote */
3083
+/* returns -1 on failure or number of bytes written on success */
3084
+static int align_string(int fd, int string_len)
3086
+ /* Keep in mind that we always want to pad a string with *at least*
3087
+ * one zero, that's why sometimes we have to pad with 4 */
3088
+ int needed = sizeof(int) - (string_len % sizeof(int));
3089
+ if (needed>= 0 && needed <=4)
3091
+ if(write(fd, "\0\0\0\0", needed) == -1) return -1;
3096
+/*all functions keep codep up to date as they use it.
3097
+ the amount that has been written to the file is maintained by the
3098
+ filelen variable in bc_action_emit
3099
+ the other bc_xxx_emit funtions keep track of how much they (and any functions they call) have written and return this value
3103
+/* Write out a stringlist to a given file descriptor.
3104
+ * return # of bytes written on success and -1 on error */
3106
+/* stringlist: <# listitems>
3107
+ <pos of listend (bytes)>
3108
+ <string:(size)(aligned string)>
3110
+static int bc_stringlist_emit(int fd, int *codep, bytecode_info_t *bc)
3112
+ int len = bc->data[(*codep)++].len;
3115
+ int wrote = 2*sizeof(int);
3118
+ /* Write out number of items in the list */
3119
+ if (write_int(fd, len)== -1) return -1 ;
3121
+ /* skip one spot end of list position*/
3122
+ begin=lseek(fd,0,SEEK_CUR);
3123
+ lseek(fd,sizeof(int),SEEK_CUR);
3125
+ /* Loop through all the items of the list, writing out length and string
3127
+ for(i=0; i < len; i++)
3129
+ int datalen = bc->data[(*codep)++].len;
3131
+ if(write_int(fd, datalen) == -1) return -1;
3132
+ wrote += sizeof(int);
3134
+ if(write(fd, bc->data[(*codep)++].str, datalen) == -1) return -1;
3137
+ ret = align_string(fd,datalen);
3138
+ if(ret == -1) return -1;
3142
+ end=lseek(fd,0,SEEK_CUR);
3144
+ /* go back and write end of list position */
3145
+ lseek(fd,begin,SEEK_SET);
3146
+ if(write_int(fd, end) == -1) return -1;
3148
+ /* return to the end */
3149
+ lseek(fd,end,SEEK_SET);
3153
+static int bc_test_emit(int fd, int *codep, bytecode_info_t *bc);
3155
+/* Write out a testlist to a given file descriptor.
3156
+ * return # of bytes written on success and -1 on error */
3157
+static int bc_testlist_emit(int fd, int *codep, bytecode_info_t *bc)
3159
+ int len = bc->data[(*codep)++].len;
3163
+ int wrote = 2*sizeof(int);
3165
+ /* Write out number of items in the list */
3166
+ if(write_int(fd, len)== -1) return -1;
3168
+ /* skip one spot for end of list position*/
3169
+ begin = lseek(fd, 0, SEEK_CUR);
3170
+ lseek(fd, sizeof(int), SEEK_CUR);
3172
+ /* Loop through all the items of the list, writing out each
3173
+ * test as we reach it in sequence. */
3174
+ for(i=0; i < len; i++) {
3175
+ int nextcodep = bc->data[(*codep)++].jump;
3177
+ ret = bc_test_emit(fd, codep, bc);
3178
+ if(ret < 0 ) return -1;
3181
+ *codep = nextcodep;
3183
+ end = lseek(fd, 0, SEEK_CUR);
3185
+ /* go back and write the end of list position */
3186
+ lseek(fd,begin,SEEK_SET);
3187
+ if(write_int(fd, end) == -1) return -1;
3189
+ /*return to the end */
3190
+ lseek(fd,end,SEEK_SET);
3195
+/* emit the bytecode for a test. returns -1 on failure or size of
3196
+ * emitted bytecode on success */
3197
+static int bc_test_emit(int fd, int *codep, bytecode_info_t *bc)
3199
+ int wrote=0;/* Relative offset to account for interleaved strings */
3202
+ int ret; /* Temporary Return Value Variable */
3204
+ /* Output this opcode */
3205
+ if(write_int(fd, bc->data[(*codep)].op) == -1)
3207
+ wrote += sizeof(int);
3209
+ switch(bc->data[(*codep)++].op) {
3212
+ /* No parameter opcodes */
3217
+ /* Single parameter: another test */
3218
+ ret = bc_test_emit(fd, codep, bc);
3228
+ /*where we jump to?*/
3229
+ /* Just drop a testlist */
3230
+ ret = bc_testlist_emit(fd, codep, bc);
3238
+ /* Drop tag and number */
3239
+ if(write_int(fd, bc->data[(*codep)].value) == -1)
3241
+ if(write_int(fd, bc->data[(*codep)+1].value) == -1)
3244
+ wrote += 2 * sizeof(int);
3251
+ ret = bc_stringlist_emit(fd, codep, bc);
3252
+ if(ret < 0) return -1;
3260
+ /* Drop match type */
3261
+ if(write_int(fd, bc->data[(*codep)].value) == -1)
3263
+ wrote += sizeof(int);
3265
+ /*drop comparator */
3266
+ if(write_int(fd, bc->data[(*codep)].value) == -1)
3268
+ wrote += sizeof(int);
3270
+ /*now drop relation*/
3271
+ if(write_int(fd, bc->data[(*codep)].value) == -1)
3273
+ wrote += sizeof(int);
3275
+ /* Now drop headers */
3276
+ ret = bc_stringlist_emit(fd, codep, bc);
3277
+ if(ret < 0) return -1;
3279
+ /* Now drop data */
3280
+ ret = bc_stringlist_emit(fd, codep, bc);
3281
+ if(ret < 0) return -1;
3290
+ /* Drop match type */
3291
+ if(write_int(fd, bc->data[(*codep)].value) == -1)
3293
+ wrote += sizeof(int);
3295
+ /*drop comparator */
3296
+ if(write_int(fd, bc->data[(*codep)].value) == -1)
3298
+ wrote += sizeof(int);
3300
+ /*now drop relation*/
3301
+ if(write_int(fd, bc->data[(*codep)].value) == -1)
3303
+ wrote += sizeof(int);
3305
+ /*now drop address part*/
3306
+ if(write_int(fd, bc->data[(*codep)].value) == -1)
3308
+ wrote += sizeof(int);
3310
+ /* Now drop headers */
3311
+ ret = bc_stringlist_emit(fd, codep, bc);
3312
+ if(ret < 0) return -1;
3314
+ /* Now drop data */
3315
+ ret = bc_stringlist_emit(fd, codep, bc);
3316
+ if(ret < 0) return -1;
3322
+ /* Unknown testcode? */
3328
+/* emit the bytecode to a file descriptor given a flattened parse tree
3329
+ * returns -1 on failure, size of emitted bytecode on success.
3331
+ * this takes care of everything except the comparisons */
3332
+static int bc_action_emit(int fd, int codep, int stopcodep,
3333
+ bytecode_info_t *bc, int filelen)
3335
+ int len; /* Temporary Length Variable */
3336
+ int ret; /* Temporary Return Value Variable */
3337
+ int start_filelen = filelen;
3340
+ /*debugging variable to check filelen*/
3343
+ /*syslog(LOG_DEBUG, "entered bc_action_emit with filelen: %d", filelen);*/
3345
+ /* All non-string data MUST be sizeof(int)
3346
+ byte alligned so the end of each string may require a pad */
3348
+ * Note that for purposes of jumps you must multiply codep by sizeof(int)
3350
+ while(codep < stopcodep) {
3351
+ /* Output this opcode */
3352
+ if(write_int(fd, bc->data[codep].op) == -1)
3355
+ filelen+=sizeof(int);
3357
+ switch(bc->data[codep++].op) {
3363
+ * jump (false condition)
3365
+ * (if there is an else) jump(finish)
3366
+ * (if there is an else) else
3369
+ int testEndLoc=-1;
3370
+ int testdist, thendist, elsedist;
3373
+ int jumpFalseLoc=-1;/*this is the location that is being reserved
3374
+ for the first jump command
3375
+ we jump to the false condition of the test*/
3377
+ int jumpEndLoc=-1; /* this is the location that is being reserved
3378
+ for the optional jump command
3379
+ it jumps over the else statement to the end*/
3381
+ int jumpop= B_JUMP;
3383
+ /*leave space to store the location of end of the test*/
3384
+ ret = lseek(fd, sizeof(int), SEEK_CUR);
3385
+ if(ret == -1) return ret;
3387
+ testEndLoc=filelen;
3388
+ filelen+=sizeof(int);
3390
+ /* spew the test */
3393
+ testdist = bc_test_emit(fd, &c, bc);
3394
+ if(testdist == -1)return -1;
3395
+ filelen +=testdist;
3397
+ /*store the location for hte end of the test
3398
+ *this is important for short circuiting of allof/anyof*/
3400
+ if(lseek(fd, testEndLoc, SEEK_SET) == -1)
3402
+ if(write_int(fd,jumpto) == -1)
3405
+ if(lseek(fd,filelen,SEEK_SET) == -1)
3408
+ /* leave space for jump */
3409
+ if(write_int(fd, jumpop) == -1)
3411
+ ret = lseek(fd, sizeof(int), SEEK_CUR);
3414
+ jumpFalseLoc=filelen+sizeof(int);
3416
+ filelen +=2*sizeof(int); /*jumpop + jump*/
3418
+ /* spew the then code */
3419
+ thendist = bc_action_emit(fd, bc->data[codep].value,
3420
+ bc->data[codep+1].value, bc,
3423
+ filelen+=thendist;
3425
+ /* there is an else case */
3426
+ if(bc->data[codep+2].value != -1)
3428
+ /* leave space for jump */
3429
+ if(write_int(fd, jumpop) == -1)
3431
+ ret = lseek(fd, sizeof(int), SEEK_CUR);
3435
+ jumpEndLoc=filelen+sizeof(int);
3436
+ filelen+=2*sizeof(int);/*jumpop + jump*/
3439
+ /*put previous jump to the end of the then code,
3440
+ *or the end of the jump if there is an else case */
3442
+ if(lseek(fd, jumpFalseLoc, SEEK_SET) == -1)
3444
+ if(write_int(fd,jumpto) == -1)
3446
+ if(lseek(fd,filelen,SEEK_SET) == -1)
3449
+ /* there is an else case */
3450
+ if(bc->data[codep+2].value != -1) {
3451
+ /* spew the else code */
3452
+ elsedist = bc_action_emit(fd, bc->data[codep+1].value,
3453
+ bc->data[codep+2].value, bc,
3456
+ filelen+=elsedist;
3458
+ /*put jump to the end of the else code*/
3460
+ if(lseek(fd, jumpEndLoc, SEEK_SET) == -1)
3462
+ if(write_int(fd,jumpto) == -1)
3464
+ if(lseek(fd,filelen,SEEK_SET) == -1)
3467
+ codep = bc->data[codep+2].value;
3469
+ codep = bc->data[codep+1].value;
3479
+ len = bc->data[codep++].len;
3480
+ if(write_int(fd,len) == -1)
3483
+ filelen+=sizeof(int);
3485
+ if(write(fd,bc->data[codep++].str,len) == -1)
3488
+ ret = align_string(fd, len);
3492
+ filelen += len + ret;
3498
+ case B_REMOVEFLAG:
3499
+ /* Dump just a stringlist */
3500
+ ret = bc_stringlist_emit(fd, &codep, bc);
3507
+ /* method string, id string, options string list,
3508
+ priotity, Message String */
3510
+ for(i=0; i<2; i++) {
3511
+ len = bc->data[codep++].len;
3512
+ if(write_int(fd,len) == -1)
3514
+ filelen += sizeof(int);
3517
+ /* this will probably only happen for the id */
3518
+ /* this is a nil string */
3519
+ /* skip the null pointer and make up for it
3520
+ * by adjusting the offset */
3525
+ if(write(fd,bc->data[codep++].str,len) == -1)
3528
+ ret = align_string(fd, len);
3532
+ filelen += len + ret;
3537
+ ret = bc_stringlist_emit(fd, &codep, bc);
3543
+ if(write_int(fd, bc->data[codep].value) == -1)
3546
+ filelen += sizeof(int);
3548
+ len = bc->data[codep++].len;
3549
+ if(write_int(fd,len) == -1)
3551
+ filelen += sizeof(int);
3553
+ if(write(fd,bc->data[codep++].str,len) == -1)
3556
+ ret = align_string(fd, len);
3557
+ if(ret == -1) return -1;
3559
+ filelen += len + ret;
3564
+ /* priority num,comptype num,relat num, comp string*/
3567
+ if(write_int(fd, bc->data[codep].value) == -1)
3569
+ filelen += sizeof(int);
3572
+ if(write_int(fd, bc->data[codep].value) == -1)
3574
+ filelen += sizeof(int);
3577
+ if(write_int(fd, bc->data[codep].value) == -1)
3579
+ filelen += sizeof(int);
3583
+ len = bc->data[codep++].len;
3584
+ if(write_int(fd,len) == -1)
3586
+ filelen += sizeof(int);
3590
+ /* this is a nil string */
3591
+ /* skip the null pointer and make up for it
3592
+ * by adjusting the offset */
3597
+ if(write(fd,bc->data[codep++].str,len) == -1)
3600
+ ret = align_string(fd, len);
3601
+ if(ret == -1) return -1;
3603
+ filelen += len + ret;
3607
+ /* Address list, Subject String, Message String,
3608
+ Days (word), Mime (word) */
3610
+ /*new code-this might be broken*/
3611
+ ret = bc_stringlist_emit(fd, &codep, bc);
3612
+ if(ret < 0) return -1;
3614
+ /*end of new code*/
3616
+ for(i=0; i<2; i++) {/*writing strings*/
3618
+ /*write length of string*/
3619
+ len = bc->data[codep++].len;
3620
+ if(write_int(fd,len) == -1)
3622
+ filelen += sizeof(int);
3626
+ /* this is a nil string */
3627
+ /* skip the null pointer and make up for it
3628
+ * by adjusting the offset */
3634
+ if(write(fd,bc->data[codep++].str,len) == -1)
3637
+ ret = align_string(fd, len);
3638
+ if(ret == -1) return -1;
3640
+ filelen += len + ret;
3645
+ if(write_int(fd,bc->data[codep].value) == -1)
3648
+ filelen += sizeof(int);
3650
+ if(write_int(fd,bc->data[codep].value) == -1)
3653
+ filelen += sizeof(int);
3662
+ /* No Parameters! */
3666
+ /* Unknown opcode? */
3670
+ return filelen - start_filelen;
3673
+/* spew the bytecode to disk */
3674
+int sieve_emit_bytecode(int fd, bytecode_info_t *bc)
3676
+ /* First output version number (4 bytes) */
3677
+ int data = BYTECODE_VERSION;
3679
+ /*this is a string, so it is happy*/
3680
+ if(write(fd, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN) == -1)
3683
+ if(write_int(fd, data) == -1) return -1;
3689
+ /*the sizeof(int) is to account for the version # at the begining*/
3690
+ return bc_action_emit(fd, 0, bc->scriptend, bc, sizeof(int) + BYTECODE_MAGIC_LEN);
3693
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/bc_eval.c
3694
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
3695
+++ b/src/lib-sieve/cmu/libsieve/bc_eval.c Sun May 04 16:00:59 2008 +0200
3697
+/* bc_eval.c - evaluate the bytecode
3700
+/***********************************************************
3701
+ Copyright 2001 by Carnegie Mellon University
3703
+ All Rights Reserved
3705
+Permission to use, copy, modify, and distribute this software and its
3706
+documentation for any purpose and without fee is hereby granted,
3707
+provided that the above copyright notice appear in all copies and that
3708
+both that copyright notice and this permission notice appear in
3709
+supporting documentation, and that the name of Carnegie Mellon
3710
+University not be used in advertising or publicity pertaining to
3711
+distribution of the software without specific, written prior
3714
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
3715
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
3716
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
3717
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
3718
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
3719
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
3720
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
3721
+******************************************************************/
3723
+#ifdef HAVE_CONFIG_H
3724
+#include <config.h>
3727
+#include "sieve_interface.h"
3728
+#include "interp.h"
3729
+#include "message.h"
3731
+#include "bytecode.h"
3733
+#include "xmalloc.h"
3735
+#include <string.h>
3738
+/**************************************************************************/
3739
+/**************************************************************************/
3740
+/**************************************************************************/
3741
+/**************************EXECUTING BYTECODE******************************/
3742
+/**************************************************************************/
3743
+/**************************************************************************/
3744
+/**************************************************************************/
3745
+/**************************************************************************/
3747
+/* Given a bytecode_input_t at the beginning of a string (the len block),
3748
+ * return the string, the length, and the bytecode index of the NEXT
3750
+int unwrap_string(bytecode_input_t *bc, int pos, const char **str, int *len)
3752
+ int local_len = ntohl(bc[pos].value);
3756
+ if(local_len == -1) {
3757
+ /* -1 length indicates NULL */
3760
+ /* This cast is ugly, but necessary */
3761
+ *str = (const char *)&bc[pos].str;
3763
+ /* Compute the next index */
3764
+ pos += ((ROUNDUP(local_len+1))/sizeof(bytecode_input_t));
3767
+ if(len) *len = local_len;
3773
+/* this is used by notify to pass the options list to do_notify
3774
+ * do_notify needs null-terminated (char *)[],
3775
+ * we have a stringlist, the beginning of which is pointed at by pos */
3776
+static const char ** bc_makeArray(bytecode_input_t *bc, int *pos)
3779
+ const char** array;
3780
+ int len = ntohl(bc[*pos].value);
3782
+ (*pos)+=2; /* Skip # Values and Total Byte Length */
3784
+ array=(const char **)xmalloc((len+1) * sizeof(char *));
3786
+ for (i=0; i<len; i++) {
3787
+ *pos = unwrap_string(bc, *pos, &(array[i]), NULL);
3795
+/* Compile a regular expression for use during parsing */
3796
+static regex_t * bc_compile_regex(const char *s, int ctag,
3797
+ char *errmsg, size_t errsiz)
3800
+ regex_t *reg = (regex_t *) xmalloc(sizeof(regex_t));
3802
+ if ( (ret=regcomp(reg, s, ctag)) != 0)
3804
+ (void) regerror(ret, reg, errmsg, errsiz);
3811
+/* Determine if addr is a system address */
3812
+static int sysaddr(const char *addr)
3814
+ if (!strncasecmp(addr, "MAILER-DAEMON", 13))
3817
+ if (!strncasecmp(addr, "LISTSERV", 8))
3820
+ if (!strncasecmp(addr, "majordomo", 9))
3823
+ if (strstr(addr, "-request"))
3826
+ if (!strncmp(addr, "owner-", 6))
3832
+/* look for myaddr and myaddrs in the body of a header - return the match */
3833
+static char* look_for_me(char *myaddr, int numaddresses,
3834
+ bytecode_input_t *bc, int i, const char **body)
3836
+ char *found = NULL;
3840
+ /* loop through each TO header */
3841
+ for (l = 0; body[l] != NULL && !found; l++) {
3842
+ void *data = NULL, *marker = NULL;
3845
+ parse_address(body[l], &data, &marker);
3847
+ /* loop through each address in the header */
3849
+ ((addr = get_address(ADDRESS_ALL,&data, &marker, 1))!= NULL)) {
3851
+ if (!strcasecmp(addr, myaddr)) {
3852
+ found = xstrdup(myaddr);
3858
+ for(x=0; x<numaddresses; x++)
3860
+ void *altdata = NULL, *altmarker = NULL;
3864
+ curra = unwrap_string(bc, curra, &str, NULL);
3866
+ /* is this address one of my addresses? */
3867
+ parse_address(str, &altdata, &altmarker);
3869
+ altaddr = get_address(ADDRESS_ALL, &altdata, &altmarker, 1);
3871
+ if (!strcasecmp(addr,altaddr)) {
3872
+ found=xstrdup(str);
3876
+ free_address(&altdata, &altmarker);
3880
+ free_address(&data, &marker);
3886
+/* Determine if we should respond to a vacation message */
3887
+static int shouldRespond(void * m, sieve_interp_t *interp,
3888
+ int numaddresses, bytecode_input_t* bc,
3889
+ int i, char **from, char **to)
3891
+ const char **body;
3893
+ char *myaddr = NULL;
3895
+ void *data = NULL, *marker = NULL;
3899
+ char *reply_to=NULL;
3901
+ /* is there an Auto-Submitted keyword other than "no"? */
3902
+ strcpy(buf, "auto-submitted");
3903
+ if (interp->getheader(m, buf, &body) == SIEVE_OK) {
3904
+ /* we don't deal with comments, etc. here */
3905
+ /* skip leading white-space */
3906
+ while (*body[0] && isspace((int) *body[0])) body[0]++;
3907
+ if (strcasecmp(body[0], "no")) l = SIEVE_DONE;
3910
+ /* is there a Precedence keyword of "junk | bulk | list"? */
3911
+ strcpy(buf, "precedence");
3912
+ if (interp->getheader(m, buf, &body) == SIEVE_OK) {
3913
+ /* we don't deal with comments, etc. here */
3914
+ /* skip leading white-space */
3915
+ while (*body[0] && isspace((int) *body[0])) body[0]++;
3916
+ if (!strcasecmp(body[0], "junk") ||
3917
+ !strcasecmp(body[0], "bulk") ||
3918
+ !strcasecmp(body[0], "list"))
3922
+ /* Note: the domain-part of all addresses are canonicalized */
3923
+ /* grab my address from the envelope */
3924
+ if (l == SIEVE_OK) {
3925
+ strcpy(buf, "to");
3926
+ l = interp->getenvelope(m, buf, &body);
3929
+ parse_address(body[0], &data, &marker);
3930
+ tmp = get_address(ADDRESS_ALL, &data, &marker, 1);
3931
+ myaddr = (tmp != NULL) ? xstrdup(tmp) : NULL;
3932
+ free_address(&data, &marker);
3936
+ if (l == SIEVE_OK) {
3937
+ strcpy(buf, "from");
3938
+ l = interp->getenvelope(m, buf, &body);
3940
+ if (l == SIEVE_OK && body[0]) {
3941
+ /* we have to parse this address & decide whether we
3942
+ want to respond to it */
3943
+ parse_address(body[0], &data, &marker);
3944
+ tmp = get_address(ADDRESS_ALL, &data, &marker, 1);
3945
+ reply_to = (tmp != NULL) ? xstrdup(tmp) : NULL;
3946
+ free_address(&data, &marker);
3948
+ /* first, is there a reply-to address? */
3949
+ if (reply_to == NULL) {
3953
+ /* first, is it from me? */
3954
+ if (l == SIEVE_OK && !strcmp(myaddr, reply_to)) {
3958
+ /* ok, is it any of the other addresses i've
3960
+ if (l == SIEVE_OK)
3963
+ for(x=0; x<numaddresses; x++) {
3964
+ const char *address;
3966
+ curra = unwrap_string(bc, curra, &address, NULL);
3968
+ if (!strcmp(address, reply_to))
3973
+ /* ok, is it a system address? */
3974
+ if (l == SIEVE_OK && sysaddr(reply_to)) {
3978
+ if (l == SIEVE_OK) {
3979
+ /* ok, we're willing to respond to the sender.
3980
+ but is this message to me? that is, is my address
3981
+ in the TO, CC or BCC fields? */
3982
+ if (strcpy(buf, "to"),
3983
+ interp->getheader(m, buf, &body) == SIEVE_OK)
3984
+ found = look_for_me(myaddr, numaddresses ,bc, i, body);
3985
+ if (!found && (strcpy(buf, "cc"),
3986
+ (interp->getheader(m, buf, &body) == SIEVE_OK)))
3987
+ found = look_for_me(myaddr, numaddresses, bc, i, body);
3988
+ if (!found && (strcpy(buf, "bcc"),
3989
+ (interp->getheader(m, buf, &body) == SIEVE_OK)))
3990
+ found = look_for_me(myaddr, numaddresses, bc, i, body);
3994
+ /* ok, ok, if we got here maybe we should reply */
3995
+ if (myaddr) free(myaddr);
4001
+/* Evaluate a bytecode test */
4002
+static int eval_bc_test(sieve_interp_t *interp, void* m,
4003
+ bytecode_input_t * bc, int * ip)
4007
+ int x,y,z;/* loop variable */
4008
+ int list_len; /* for allof/anyof/exists */
4009
+ int list_end; /* for allof/anyof/exists */
4010
+ int address=0;/*to differentiate between address and envelope*/
4011
+ comparator_t * comp=NULL;
4012
+ void * comprock=NULL;
4013
+ int op= ntohl(bc[i].op);
4018
+ res=0; i++; break;
4021
+ res=1; i++; break;
4025
+ res = eval_bc_test(interp,m, bc, &i);
4026
+ if(res >= 0) res = !res; /* Only invert in non-error case */
4029
+ case BC_EXISTS:/*3*/
4037
+ list_len=ntohl(bc[headersi].len);
4038
+ list_end=ntohl(bc[headersi+1].value)/4;
4042
+ for(x=0; x<list_len && res; x++)
4046
+ currh = unwrap_string(bc, currh, &str, NULL);
4048
+ if(interp->getheader(m,str, &val) != SIEVE_OK)
4052
+ i=list_end; /* adjust for short-circuit */
4055
+ case BC_SIZE:/*4*/
4058
+ int sizevar=ntohl(bc[i+1].value);
4059
+ int x=ntohl(bc[i+2].value);
4061
+ if (interp->getsize(m, &s) != SIEVE_OK)
4064
+ if (sizevar ==B_OVER) {
4074
+ case BC_ANYOF:/*5*/
4076
+ list_len=ntohl(bc[i+1].len);
4077
+ list_end=ntohl(bc[i+2].len)/4;
4080
+ /* need to process all of them, to ensure our instruction pointer stays
4081
+ * in the right place */
4082
+ for (x=0; x<list_len && !res; x++) {
4084
+ tmp = eval_bc_test(interp,m,bc,&i);
4092
+ i = list_end; /* handle short-circuting */
4095
+ case BC_ALLOF:/*6*/
4097
+ list_len=ntohl(bc[i+1].len);
4098
+ list_end=ntohl(bc[i+2].len)/4;
4101
+ /* return 1 unless you find one that isn't true, then return 0 */
4102
+ for (x=0; x<list_len && res; x++) {
4104
+ tmp = eval_bc_test(interp,m,bc,&i);
4112
+ i = list_end; /* handle short-circuiting */
4115
+ case BC_ADDRESS:/*7*/
4117
+ /* fall through */
4118
+ case BC_ENVELOPE:/*8*/
4120
+ const char ** val;
4122
+ void * marker=NULL;
4124
+ int addrpart=ADDRESS_ALL;/* XXX correct default behavior?*/
4126
+ int headersi=i+5;/* the i value for the begining of the headers */
4127
+ int datai=(ntohl(bc[headersi+1].value)/4);
4129
+ int numheaders=ntohl(bc[headersi].len);
4130
+ int numdata=ntohl(bc[datai].len);
4132
+ int currh, currd; /* current header, current data */
4134
+ int match=ntohl(bc[i+1].value);
4135
+ int relation=ntohl(bc[i+2].value);
4136
+ int comparator=ntohl(bc[i+3].value);
4137
+ int apart=ntohl(bc[i+4].value);
4140
+ int isReg = (match==B_REGEX);
4143
+ char errbuf[100]; /* Basically unused, as regexps are tested at compile */
4145
+ /* set up variables needed for compiling regex */
4148
+ if (comparator== B_ASCIICASEMAP)
4150
+ ctag = REG_EXTENDED | REG_NOSUB | REG_ICASE;
4154
+ ctag = REG_EXTENDED | REG_NOSUB;
4158
+ /*find the correct comparator fcn*/
4159
+ comp = lookup_comp(comparator, match, relation, &comprock);
4162
+ res = SIEVE_RUN_ERROR;
4166
+ /*find the part of the address that we want*/
4170
+ addrpart = ADDRESS_ALL; break;
4172
+ addrpart = ADDRESS_LOCALPART; break;
4174
+ addrpart = ADDRESS_DOMAIN; break;
4176
+ addrpart = ADDRESS_USER; break;
4178
+ addrpart = ADDRESS_DETAIL; break;
4180
+ /* this shouldn't happen with correcct bytecode */
4181
+ res = SIEVE_RUN_ERROR;
4184
+ if(res == SIEVE_RUN_ERROR) break;
4186
+ /*loop through all the headers*/
4189
+ printf("about to process %d headers\n", numheaders);
4191
+ for (x=0; x<numheaders && !res; x++)
4193
+ const char *this_header;
4195
+ currh = unwrap_string(bc, currh, &this_header, NULL);
4197
+ /* Try the next string if we don't have this one */
4200
+ if(interp->getheader(m, this_header, &val) != SIEVE_OK)
4203
+ printf(" [%d] header %s is %s\n", x, this_header, val[0]);
4207
+ if(interp->getenvelope(m, this_header, &val) != SIEVE_OK)
4211
+ /*header exists, now to test it*/
4212
+ /*search through all the headers that match*/
4214
+ for (y=0; val[y]!=NULL && !res; y++) {
4217
+ printf("about to parse %s\n", val[y]);
4220
+ if (parse_address(val[y], &data, &marker)!=SIEVE_OK)
4224
+ (addr = get_address(addrpart, &data, &marker, 0))) {
4226
+ printf("working addr %s\n", (addr ? addr : "[nil]"));
4229
+ if (match == B_COUNT) {
4232
+ /*search through all the data*/
4234
+ for (z=0; z<numdata && !res; z++)
4236
+ const char *data_val;
4238
+ currd = unwrap_string(bc, currd, &data_val, NULL);
4241
+ reg = bc_compile_regex(data_val, ctag,
4242
+ errbuf, sizeof(errbuf));
4249
+ res |= comp(val[y], (const char *)reg,
4254
+ printf("%s compared to %s(from script)\n",
4257
+ res |= comp(addr, data_val, comprock);
4259
+ } /* For each data */
4261
+ } /* For each address */
4263
+ free_address(&data, &marker);
4264
+ }/* For each message header */
4267
+ printf("end of loop, res is %d, x is %d (%d)\n", res, x, numheaders);
4269
+ } /* For each script header */
4271
+ if (match == B_COUNT)
4273
+ sprintf(scount, "%u", count);
4274
+ /* search through all the data */
4276
+ for (z=0; z<numdata && !res; z++)
4278
+ const char *data_val;
4280
+ currd = unwrap_string(bc, currd, &data_val, NULL);
4282
+ res |= comp(scount, data_val, comprock);
4287
+ i=(ntohl(bc[datai+1].value)/4);
4291
+ case BC_HEADER:/*9*/
4295
+ int headersi=i+4;/*the i value for the begining of hte headers*/
4296
+ int datai=(ntohl(bc[headersi+1].value)/4);
4298
+ int numheaders=ntohl(bc[headersi].len);
4299
+ int numdata=ntohl(bc[datai].len);
4301
+ int currh, currd; /*current header, current data*/
4303
+ int match=ntohl(bc[i+1].value);
4304
+ int relation=ntohl(bc[i+2].value);
4305
+ int comparator=ntohl(bc[i+3].value);
4308
+ int isReg = (match==B_REGEX);
4311
+ char errbuf[100]; /* Basically unused, regexps tested at compile */
4313
+ /* set up variables needed for compiling regex */
4316
+ if (comparator== B_ASCIICASEMAP)
4318
+ ctag= REG_EXTENDED | REG_NOSUB | REG_ICASE;
4322
+ ctag= REG_EXTENDED | REG_NOSUB;
4327
+ /*find the correct comparator fcn*/
4328
+ comp=lookup_comp(comparator, match, relation, &comprock);
4331
+ res = SIEVE_RUN_ERROR;
4335
+ /*search through all the flags for the header*/
4337
+ for(x=0; x<numheaders && !res; x++)
4339
+ const char *this_header;
4341
+ currh = unwrap_string(bc, currh, &this_header, NULL);
4343
+ if(interp->getheader(m, this_header, &val) != SIEVE_OK) {
4344
+ continue; /*this header does not exist, search the next*/
4347
+ printf ("val %s %s %s\n", val[0], val[1], val[2]);
4350
+ /* search through all the headers that match */
4352
+ for (y=0; val[y]!=NULL && !res; y++)
4354
+ if (match == B_COUNT) {
4357
+ /*search through all the data*/
4359
+ for (z=0; z<numdata && !res; z++)
4361
+ const char *data_val;
4363
+ currd = unwrap_string(bc, currd, &data_val, NULL);
4366
+ reg= bc_compile_regex(data_val, ctag, errbuf,
4375
+ res |= comp(val[y], (const char *)reg,
4379
+ res |= comp(val[y], data_val, comprock);
4386
+ if (match == B_COUNT )
4388
+ sprintf(scount, "%u", count);
4389
+ /*search through all the data*/
4391
+ for (z=0; z<numdata && !res; z++)
4393
+ const char *data_val;
4395
+ currd = unwrap_string(bc, currd, &data_val, NULL);
4397
+ printf("%d, %s \n", count, data_val);
4399
+ res |= comp(scount, data_val, comprock);
4405
+ i=(ntohl(bc[datai+1].value)/4);
4411
+ printf("WERT, can't evaluate if statement. %d is not a valid command",
4414
+ return SIEVE_RUN_ERROR;
4424
+/* The entrypoint for bytecode evaluation */
4425
+int sieve_eval_bc(sieve_interp_t *i, const void *bc_in, unsigned int bc_len,
4426
+ void *m, sieve_imapflags_t * imapflags,
4427
+ action_list_t *actions,
4428
+ notify_list_t *notify_list,
4429
+ const char **errmsg)
4432
+ int ip = 0, ip_max = (bc_len/sizeof(bytecode_input_t));
4437
+ bytecode_input_t *bc = (bytecode_input_t *)bc_in;
4440
+ * a) have bytecode
4441
+ * b) it is atleast long enough for the magic number, the version
4442
+ * and one opcode */
4443
+ if(!bc) return SIEVE_FAIL;
4444
+ if(bc_len < (BYTECODE_MAGIC_LEN + 2*sizeof(bytecode_input_t)))
4445
+ return SIEVE_FAIL;
4447
+ if(memcmp(bc, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN)) {
4448
+ *errmsg = "Not a bytecode file";
4449
+ return SIEVE_FAIL;
4452
+ ip = BYTECODE_MAGIC_LEN / sizeof(bytecode_input_t);
4454
+ version= ntohl(bc[ip].op);
4456
+ /* this is because there was a time where integers were not network byte
4457
+ order. all the scripts written then would have version 0x01 written
4458
+ in host byte order.*/
4460
+ if(version == (int)ntohl(1)) {
4463
+ "Incorrect Bytecode Version, please recompile (use sievec)";
4466
+ return SIEVE_FAIL;
4469
+ if( version != BYTECODE_VERSION) {
4472
+ "Incorrect Bytecode Version, please recompile (use sievec)";
4474
+ return SIEVE_FAIL;
4478
+ printf("version number %d\n",version);
4481
+ for(ip++; ip<ip_max; ) {
4482
+ op=ntohl(bc[ip].op);
4489
+ res = do_keep(actions, imapflags);
4490
+ if (res == SIEVE_RUN_ERROR)
4491
+ *errmsg = "Keep can not be used with Reject";
4495
+ case B_DISCARD:/*2*/
4496
+ res=do_discard(actions);
4500
+ case B_REJECT:/*3*/
4501
+ ip = unwrap_string(bc, ip+1, &data, NULL);
4503
+ res = do_reject(actions, data);
4505
+ if (res == SIEVE_RUN_ERROR)
4506
+ *errmsg = "Reject can not be used with any other action";
4510
+ case B_FILEINTO:/*4*/
4512
+ ip = unwrap_string(bc, ip+1, &data, NULL);
4514
+ res = do_fileinto(actions, data, imapflags);
4516
+ if (res == SIEVE_RUN_ERROR)
4517
+ *errmsg = "Fileinto can not be used with Reject";
4522
+ case B_REDIRECT:/*5*/
4524
+ ip = unwrap_string(bc, ip+1, &data, NULL);
4526
+ res = do_redirect(actions, data);
4528
+ if (res == SIEVE_RUN_ERROR)
4529
+ *errmsg = "Redirect can not be used with Reject";
4536
+ int testend=ntohl(bc[ip+1].value);
4540
+ result=eval_bc_test(i, m, bc, &ip);
4543
+ *errmsg = "Invalid test";
4544
+ return SIEVE_FAIL;
4545
+ } else if (result) {
4546
+ /*skip over jump instruction*/
4555
+ res = do_mark(actions);
4559
+ case B_UNMARK:/*9*/
4560
+ res = do_unmark(actions);
4564
+ case B_ADDFLAG:/*10*/
4567
+ int list_len=ntohl(bc[ip+1].len);
4569
+ ip+=3; /* skip opcode, list_len, and list data len */
4571
+ for (x=0; x<list_len; x++) {
4572
+ ip = unwrap_string(bc, ip, &data, NULL);
4574
+ res = do_addflag(actions, data);
4576
+ if (res == SIEVE_RUN_ERROR)
4577
+ *errmsg = "addflag can not be used with Reject";
4585
+ int list_len=ntohl(bc[ip+1].len);
4587
+ ip+=3; /* skip opcode, list_len, and list data len */
4589
+ ip = unwrap_string(bc, ip, &data, NULL);
4591
+ res = do_setflag(actions, data);
4593
+ if (res == SIEVE_RUN_ERROR) {
4594
+ *errmsg = "setflag can not be used with Reject";
4596
+ for (x=1; x<list_len; x++) {
4597
+ ip = unwrap_string(bc, ip, &data, NULL);
4599
+ res = do_addflag(actions, data);
4601
+ if (res == SIEVE_RUN_ERROR)
4602
+ *errmsg = "setflag can not be used with Reject";
4609
+ case B_REMOVEFLAG:
4612
+ int list_len=ntohl(bc[ip+1].len);
4614
+ ip+=3; /* skip opcode, list_len, and list data len */
4616
+ for (x=0; x<list_len; x++) {
4617
+ ip = unwrap_string(bc, ip, &data, NULL);
4619
+ res = do_removeflag(actions, data);
4621
+ if (res == SIEVE_RUN_ERROR)
4622
+ *errmsg = "removeflag can not be used with Reject";
4630
+ const char * method;
4631
+ const char **options = NULL;
4632
+ const char *priority = NULL;
4633
+ const char * message;
4639
+ ip = unwrap_string(bc, ip, &method, NULL);
4642
+ ip = unwrap_string(bc, ip, &id, NULL);
4645
+ options=bc_makeArray(bc, &ip);
4648
+ pri=ntohl(bc[ip].value);
4656
+ priority="normal";
4665
+ res=SIEVE_RUN_ERROR;
4669
+ ip = unwrap_string(bc, ip, &message, NULL);
4671
+ res = do_notify(notify_list, id, method, options,
4672
+ priority, message);
4679
+ * i really have no idea what the count matchtype should do here.
4680
+ * the sanest thing would be to use 1.
4681
+ * however that would require passing on the match type to do_notify.
4685
+ comparator_t *comp = NULL;
4687
+ const char *pattern;
4690
+ const char *priority = NULL;
4691
+ void *comprock = NULL;
4697
+ pri=ntohl(bc[ip].value);
4705
+ priority="normal";
4714
+ res=SIEVE_RUN_ERROR;
4717
+ if(res == SIEVE_RUN_ERROR)
4720
+ comparator =ntohl( bc[ip].value);
4723
+ if (comparator == B_ANY)
4725
+ ip++;/* skip placeholder this has no comparator function */
4728
+ int x= ntohl(bc[ip].value);
4731
+ comp=lookup_comp(B_ASCIICASEMAP,comparator,
4735
+ ip = unwrap_string(bc, ip, &pattern, NULL);
4737
+ if (comparator == B_REGEX)
4739
+ char errmsg[1024]; /* Basically unused */
4741
+ reg=bc_compile_regex(pattern,
4742
+ REG_EXTENDED | REG_NOSUB | REG_ICASE,
4743
+ errmsg, sizeof(errmsg));
4745
+ res = SIEVE_RUN_ERROR;
4747
+ res = do_denotify(notify_list, comp, reg,
4748
+ comprock, priority);
4752
+ res = do_denotify(notify_list, comp, pattern,
4753
+ comprock, priority);
4761
+ char *fromaddr = NULL; /* relative to message we send */
4762
+ char *toaddr = NULL; /* relative to message we send */
4763
+ const char *message = NULL;
4765
+ char subject[1024];
4770
+ x=ntohl( bc[ip].len);
4772
+ respond=shouldRespond(m, i, x, bc, ip+2,
4773
+ &fromaddr, &toaddr);
4775
+ ip=(ntohl(bc[ip+1].value)/4);
4776
+ if (respond==SIEVE_OK)
4778
+ ip = unwrap_string(bc, ip, &data, NULL);
4782
+ /* we have to generate a subject */
4784
+ strlcpy(buf, "subject", sizeof(buf));
4785
+ if (i->getheader(m, buf, &s) != SIEVE_OK ||
4787
+ strlcpy(subject, "Automated reply", sizeof(subject));
4789
+ /* s[0] contains the original subject */
4790
+ const char *origsubj = s[0];
4792
+ while (!strncasecmp(origsubj, "Re: ", 4))
4795
+ snprintf(subject, sizeof(subject), "Re: %s", origsubj);
4798
+ /* user specified subject */
4799
+ strlcpy(subject, data, sizeof(subject));
4802
+ ip = unwrap_string(bc, ip, &message, NULL);
4804
+ res = do_vacation(actions, toaddr, fromaddr,
4805
+ xstrdup(subject), message,
4806
+ ntohl(bc[ip].value), ntohl(bc[ip+1].value));
4810
+ if (res == SIEVE_RUN_ERROR)
4811
+ *errmsg = "Vacation can not be used with Reject or Vacation";
4812
+ } else if (respond == SIEVE_DONE) {
4813
+ /* skip subject and message */
4815
+ ip = unwrap_string(bc, ip, &data, NULL);
4816
+ ip = unwrap_string(bc, ip, &data, NULL);
4818
+ ip+=2;/*skip days and mime flag*/
4820
+ res = SIEVE_RUN_ERROR; /* something is bad */
4825
+ case B_NULL:/*15*/
4829
+ case B_JUMP:/*16*/
4830
+ ip= ntohl(bc[ip+1].jump);
4834
+ if(errmsg) *errmsg = "Invalid sieve bytecode";
4835
+ return SIEVE_FAIL;
4838
+ if (res) /* we've either encountered an error or a stop */
4843
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/bc_generate.c
4844
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
4845
+++ b/src/lib-sieve/cmu/libsieve/bc_generate.c Sun May 04 16:00:59 2008 +0200
4847
+/* bc_generate.c -- sieve bytecode- almost flattened bytecode
4851
+/***********************************************************
4852
+ Copyright 2001 by Carnegie Mellon University
4854
+ All Rights Reserved
4856
+Permission to use, copy, modify, and distribute this software and its
4857
+documentation for any purpose and without fee is hereby granted,
4858
+provided that the above copyright notice appear in all copies and that
4859
+both that copyright notice and this permission notice appear in
4860
+supporting documentation, and that the name of Carnegie Mellon
4861
+University not be used in advertising or publicity pertaining to
4862
+distribution of the software without specific, written prior
4865
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
4866
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
4867
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
4868
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
4869
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
4870
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
4871
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
4872
+******************************************************************/
4874
+#ifdef HAVE_CONFIG_H
4875
+#include <config.h>
4878
+#include "xmalloc.h"
4879
+#include "sieve_interface.h"
4881
+#include "script.h"
4885
+#include "bytecode.h"
4887
+#include <assert.h>
4888
+#include <string.h>
4892
+struct bytecode_info
4894
+ bytecode_t *data;/* pointer to almost-flat bytecode */
4895
+ size_t scriptend; /* used by emit code to know final length of bytecode */
4896
+ size_t reallen; /* allocated length of 'data' */
4899
+static int bc_test_generate(int codep, bytecode_info_t *retval, test_t *t);
4901
+/* returns false if the request can't be satisfied, true if it can. */
4903
+static int atleast(bytecode_info_t *arr, size_t len)
4905
+ if(arr->reallen < len) {
4906
+ /* too small; double if that's big enough, otherwise increase to the
4907
+ requested size. */
4908
+ arr->reallen = (len > arr->reallen * 2 ? len : arr->reallen * 2);
4909
+ arr->data = xrealloc(arr->data, arr->reallen*sizeof(bytecode_t));
4911
+ { /* out of memory? */
4920
+ * functions of the form bc_XXX_generate have the following properties:
4921
+ * on success they return an int that corresponds to the next empty location
4922
+ * for code, and on failure they return -1.
4924
+ * they will take a bytecode_info_t as a parameter and modify it by
4925
+ * making it larger and adding more bytecommands in the pass 1 form
4928
+/* given a location and a string list, compile it into almost-flat form.
4929
+ * <list len> <string len><string ptr><string len><string ptr> etc... */
4930
+static int bc_stringlist_generate(int codep, bytecode_info_t *retval,
4933
+ int len_codep = codep;
4935
+ stringlist_t *cur;
4939
+ /* Bounds check the string list length */
4940
+ if(!atleast(retval,codep+1))
4943
+ for(cur=sl; cur; cur=cur->next)
4946
+ assert((cur->s)!=NULL);
4948
+ /* Bounds check for each string before we allocate it */
4949
+ if(!atleast(retval,codep+2))
4952
+ retval->data[codep++].len = strlen(cur->s);
4953
+ retval->data[codep++].str = cur->s;
4956
+ retval->data[len_codep].listlen = strcount;
4961
+/* write a list of tests into almost-flat form, starting at codep.
4962
+ * returns the next code location, -1 on error. */
4964
+/* <list len> <next test ptr> <test ...> <next test ptr> <test ...> ... */
4965
+static int bc_testlist_generate(int codep, bytecode_info_t *retval,
4968
+ int len_codep = codep;
4969
+ int testcount = 0;
4974
+ /* Bounds check the test list length */
4975
+ if(!atleast(retval,codep+1))
4978
+ for(cur=tl; cur; cur=cur->next) {
4979
+ int oldcodep = codep;
4981
+ /* Make room for tail marker */
4982
+ if(!atleast(retval,codep+1))
4986
+ codep = bc_test_generate(codep+1, retval, cur->t);
4988
+ retval->data[oldcodep].jump = codep;
4991
+ retval->data[len_codep].listlen = testcount;
4995
+/* output a relation into almost-flat form at codep.
4996
+ * returns new codep on success, -1 on failure. */
4997
+static int bc_relation_generate(int codep, bytecode_info_t *retval, int relat)
4999
+ if (!atleast(retval, codep + 1)) return -1;
5003
+ retval->data[codep++].value= B_GT;
5006
+ retval->data[codep++].value= B_GE;
5009
+ retval->data[codep++].value= B_LT;
5012
+ retval->data[codep++].value= B_LE;
5015
+ retval->data[codep++].value= B_EQ;
5018
+ retval->data[codep++].value= B_NE;
5021
+ /* comparator has no relational field */
5022
+ retval->data[codep++].value= -1;
5027
+/* writes a single comparator into almost-flat form starting at codep.
5028
+ * will always write out 3 words
5029
+ * returns the next code location or -1 on error. */
5030
+static int bc_comparator_generate(int codep, bytecode_info_t *retval,
5031
+ int comptag, int relat,
5032
+ const char *comparator)
5034
+ assert(retval != NULL);
5037
+ if (!atleast(retval, codep + 1)) return -1;
5039
+ switch (comptag) {
5041
+ retval->data[codep++].value = B_IS;
5044
+ retval->data[codep++].value = B_CONTAINS;
5047
+ retval->data[codep++].value = B_MATCHES;
5049
+#ifdef ENABLE_REGEX
5051
+ retval->data[codep++].value = B_REGEX;
5055
+ retval->data[codep++].value = B_COUNT;
5058
+ retval->data[codep++].value = B_VALUE;
5066
+ codep = bc_relation_generate(codep, retval, relat);
5068
+ /* comparator (value specified with :comparator) */
5069
+ if (!atleast(retval, codep + 1)) return -1;
5071
+ /* xxx perhaps extend comparator.h to have
5072
+ lookup_comp return an index, and then
5073
+ lookup_by_index return the actual comparator?
5075
+ we can then eliminate the comptag above, too. */
5077
+ if (!strcmp (comparator, "i;octet"))
5078
+ retval->data[codep++].value = B_OCTET;
5079
+ else if (!strcmp (comparator, "i;ascii-casemap"))
5080
+ retval->data[codep++].value = B_ASCIICASEMAP;
5081
+ else if (!strcmp (comparator, "i;ascii-numeric"))
5082
+ retval->data[codep++].value = B_ASCIINUMERIC;
5089
+/* writes a single test into almost-flat form starting at codep.
5090
+ * returns the next code location or -1 on error. */
5091
+static int bc_test_generate(int codep, bytecode_info_t *retval, test_t *t)
5093
+ if(!retval) return -1;
5095
+ case STRUE: /* BC_TRUE */
5096
+ if(!atleast(retval,codep+1)) return -1;
5097
+ retval->data[codep++].op = BC_TRUE;
5099
+ case SFALSE:/* BC_FALSE */
5100
+ if(!atleast(retval,codep+1)) return -1;
5101
+ retval->data[codep++].op = BC_FALSE;
5103
+ case NOT: /* BC_NOT {subtest : test} */
5104
+ if(!atleast(retval,codep+1)) return -1;
5105
+ retval->data[codep++].op = BC_NOT;
5106
+ codep = bc_test_generate(codep, retval, t->u.t);
5107
+ if (codep == -1) return -1;
5109
+ case SIZE: /* BC_SIZE (B_OVER | B_UNDER) {size : int} */
5110
+ if(!atleast(retval,codep+3)) return -1;
5111
+ retval->data[codep++].op = BC_SIZE;
5112
+ retval->data[codep++].value = (t->u.sz.t == OVER
5113
+ ? B_OVER : B_UNDER);
5114
+ retval->data[codep++].value = t->u.sz.n;
5116
+ case EXISTS:/* BC_EXISTS { headers : string list } */
5117
+ if(!atleast(retval,codep+1)) return -1;
5118
+ retval->data[codep++].op = BC_EXISTS;
5119
+ codep= bc_stringlist_generate(codep, retval, t->u.sl);
5121
+ case ANYOF:/* BC_ANYOF { tests : test list } */
5122
+ if(!atleast(retval,codep+1)) return -1;
5123
+ retval->data[codep++].op = BC_ANYOF;
5124
+ codep=bc_testlist_generate(codep, retval, t->u.tl);
5125
+ if (codep == -1) return -1;
5127
+ case ALLOF: /* BC_ALLOF { tests : test list } */
5128
+ if(!atleast(retval,codep+1)) return -1;
5129
+ retval->data[codep++].op = BC_ALLOF;
5130
+ codep= bc_testlist_generate(codep, retval, t->u.tl);
5131
+ if (codep == -1) return -1;
5134
+ /* BC_HEADER { c: comparator } { headers : string list }
5135
+ { patterns : string list }
5138
+ if(!atleast(retval,codep + 1)) return -1;
5139
+ retval->data[codep++].op = BC_HEADER;
5142
+ codep = bc_comparator_generate(codep, retval,
5145
+ t->u.h.comparator);
5146
+ if (codep == -1) return -1;
5149
+ codep = bc_stringlist_generate(codep, retval, t->u.h.sl);
5150
+ if (codep == -1) return -1;
5153
+ codep = bc_stringlist_generate(codep, retval, t->u.h.pl);
5154
+ if (codep == -1) return -1;
5158
+ /* (BC_ADDRESS | BC_ENVELOPE) {c : comparator}
5159
+ (B_ALL | B_LOCALPART | ...) { header : string list }
5160
+ { pattern : string list } */
5162
+ if(!atleast(retval,codep+1)) return -1;
5164
+ retval->data[codep++].op = (t->type == ADDRESS)
5165
+ ? BC_ADDRESS : BC_ENVELOPE;
5167
+ codep = bc_comparator_generate(codep, retval,t->u.ae.comptag,
5169
+ t->u.ae.comparator);
5170
+ if (codep == -1) return -1;
5172
+ if(!atleast(retval,codep+1)) return -1;
5175
+ switch(t->u.ae.addrpart) {
5177
+ retval->data[codep++].value = B_ALL;
5180
+ retval->data[codep++].value = B_LOCALPART;
5183
+ retval->data[codep++].value = B_DOMAIN;
5186
+ retval->data[codep++].value = B_USER;
5189
+ retval->data[codep++].value = B_DETAIL;
5196
+ codep = bc_stringlist_generate(codep, retval, t->u.ae.sl);
5197
+ if (codep == -1) return -1;
5200
+ codep = bc_stringlist_generate(codep, retval, t->u.ae.pl);
5201
+ if (codep == -1) return -1;
5212
+/* generate a not-quite-flattened bytecode */
5213
+/* returns address of next instruction or -1 on error*/
5214
+/* needs current instruction, buffer for the code, and a current parse tree */
5215
+/* sieve is cool because everything is immediate! */
5216
+static int bc_action_generate(int codep, bytecode_info_t *retval,
5219
+ int jumploc,baseloc;
5221
+ if(!retval) return -1;
5224
+ if(!atleast(retval,codep+1)) return -1;
5225
+ retval->data[codep++].op = B_NULL;
5232
+ /* STOP (no arguments) */
5233
+ if(!atleast(retval,codep+1)) return -1;
5234
+ retval->data[codep++].op = B_STOP;
5237
+ /* DISCARD (no arguments) */
5238
+ if(!atleast(retval,codep+1)) return -1;
5239
+ retval->data[codep++].op = B_DISCARD;
5242
+ /* KEEP (no arguments) */
5243
+ if(!atleast(retval,codep+1)) return -1;
5244
+ retval->data[codep++].op = B_KEEP;
5247
+ /* MARK (no arguments) */
5248
+ if(!atleast(retval,codep+1)) return -1;
5249
+ retval->data[codep++].op = B_MARK;
5252
+ /* UNMARK (no arguments) */
5253
+ if(!atleast(retval,codep+1)) return -1;
5254
+ retval->data[codep++].op = B_UNMARK;
5258
+ if(!atleast(retval,codep+6)) return -1;
5259
+ retval->data[codep++].op = B_DENOTIFY;
5260
+ switch(c->u.d.priority) {
5262
+ retval->data[codep++].value = B_LOW;
5265
+ retval->data[codep++].value = B_NORMAL;
5268
+ retval->data[codep++].value = B_HIGH;
5271
+ retval->data[codep++].value = B_ANY;
5276
+ switch(c->u.d.comptag) {
5278
+ retval->data[codep++].value = B_IS;
5281
+ retval->data[codep++].value = B_CONTAINS;
5284
+ retval->data[codep++].value = B_MATCHES;
5286
+#ifdef ENABLE_REGEX
5288
+ retval->data[codep++].value = B_REGEX;
5292
+ retval->data[codep++].value = B_ANY;
5297
+ codep = bc_relation_generate(codep, retval, c->u.d.relation);
5299
+ if(c->u.d.pattern)
5301
+ retval->data[codep++].len = strlen(c->u.d.pattern);
5302
+ retval->data[codep++].str = c->u.d.pattern;
5304
+ retval->data[codep++].len = -1;
5305
+ retval->data[codep++].str = NULL;
5310
+ /* REJECT (STRING: len + dataptr) */
5311
+ if(!atleast(retval,codep+3)) return -1;
5312
+ retval->data[codep++].op = B_REJECT;
5313
+ retval->data[codep++].len = strlen(c->u.str);
5314
+ retval->data[codep++].str = c->u.str;
5317
+ /* FILEINTO (STRING: len + dataptr) */
5318
+ if(!atleast(retval,codep+3)) return -1;
5319
+ retval->data[codep++].op = B_FILEINTO;
5320
+ retval->data[codep++].len = strlen(c->u.str);
5321
+ retval->data[codep++].str = c->u.str;
5324
+ /* REDIRECT (STRING: len + dataptr) */
5325
+ if(!atleast(retval,codep+3)) return -1;
5326
+ retval->data[codep++].op = B_REDIRECT;
5327
+ retval->data[codep++].len = strlen(c->u.str);
5328
+ retval->data[codep++].str = c->u.str;
5331
+ /* ADDFLAG stringlist */
5332
+ if(!atleast(retval,codep+1)) return -1;
5333
+ retval->data[codep++].op = B_ADDFLAG;
5334
+ codep = bc_stringlist_generate(codep,retval,c->u.sl);
5336
+ if(codep == -1) return -1;
5339
+ /* SETFLAG stringlist */
5340
+ if(!atleast(retval,codep+1)) return -1;
5341
+ retval->data[codep++].op = B_SETFLAG;
5342
+ codep = bc_stringlist_generate(codep,retval,c->u.sl);
5344
+ if(codep == -1) return -1;
5347
+ /* REMOVEFLAG stringlist */
5348
+ if(!atleast(retval,codep+1)) return -1;
5349
+ retval->data[codep++].op = B_REMOVEFLAG;
5350
+ codep = bc_stringlist_generate(codep,retval,c->u.sl);
5352
+ if(codep == -1) return -1;
5356
+ (STRING: len + dataptr)
5357
+ (STRING: len + dataptr)
5359
+ (STRING: len + dataptr)
5360
+ (STRING: len + dataptr)
5361
+ method/id /options list/priority/message
5364
+ if(!atleast(retval,codep+5)) return -1;
5365
+ retval->data[codep++].op = B_NOTIFY;
5367
+ retval->data[codep++].len = strlen(c->u.n.method);
5368
+ retval->data[codep++].str = c->u.n.method;
5372
+ retval->data[codep++].len = strlen(c->u.n.id);
5373
+ retval->data[codep++].str = c->u.n.id;
5377
+ retval->data[codep++].len = -1;
5378
+ retval->data[codep++].str = NULL;
5381
+ codep = bc_stringlist_generate(codep,retval,c->u.n.options);
5382
+ if(codep == -1) return -1;
5384
+ if(!atleast(retval,codep+3)) return -1;
5386
+ switch(c->u.n.priority) {
5388
+ retval->data[codep++].value = B_LOW;
5391
+ retval->data[codep++].value = B_NORMAL;
5394
+ retval->data[codep++].value = B_HIGH;
5397
+ retval->data[codep++].value = B_ANY;
5403
+ retval->data[codep++].len = strlen(c->u.n.message);
5404
+ retval->data[codep++].str = c->u.n.message;
5408
+ STRINGLIST addresses
5409
+ STRING subject (if len is -1, then subject was NULL)
5410
+ STRING message (again, len == -1 means subject was NULL)
5415
+ if(!atleast(retval,codep+1)) return -1;
5416
+ retval->data[codep++].op = B_VACATION;
5418
+ codep = bc_stringlist_generate(codep,retval,c->u.v.addresses);
5419
+ if (codep == -1) return -1;
5421
+ if (!atleast(retval,codep+2)) return -1;
5422
+ if(c->u.v.subject) {
5423
+ retval->data[codep++].len = strlen(c->u.v.subject);
5424
+ retval->data[codep++].str = c->u.v.subject;
5426
+ retval->data[codep++].len = -1;
5427
+ retval->data[codep++].str = NULL;
5430
+ if (!atleast(retval,codep+2)) return -1;
5431
+ if(c->u.v.message) {
5432
+ retval->data[codep++].len = strlen(c->u.v.message);
5433
+ retval->data[codep++].str = c->u.v.message;
5435
+ retval->data[codep++].len = -1;
5436
+ retval->data[codep++].str = NULL;
5439
+ if (!atleast(retval,codep+2)) return -1;
5440
+ retval->data[codep++].value = c->u.v.days;
5441
+ retval->data[codep++].value = c->u.v.mime;
5444
+ if(codep == -1) return -1;
5450
+ (int: begin then block)
5451
+ (int: end then block/begin else block)
5452
+ (int:end else block) (-1 if no else block)
5455
+ (else block)(optional)
5459
+ /* Allocate operator + jump table offsets */
5460
+ if(!atleast(retval,codep+4)) return -1;
5462
+ jumploc = codep+4;
5463
+ retval->data[codep++].op = B_IF;
5465
+ /* begining of then code */
5466
+ jumpVal= bc_test_generate(jumploc,retval,c->u.i.t);
5470
+ retval->data[codep].jump = jumpVal;
5474
+ /* find then code and offset to else code,
5475
+ * we want to write this code starting at the offset we
5478
+ jumpVal= bc_action_generate(jumpVal,retval, c->u.i.do_then);
5482
+ retval->data[codep].jump = jumpVal;
5485
+ /* write else code if its there*/
5486
+ if(c->u.i.do_else) {
5488
+ jumpVal= bc_action_generate(jumpVal,retval, c->u.i.do_else);
5494
+ retval->data[codep].jump = jumpVal;
5497
+ /* Update code pointer to end of else code */
5498
+ codep = retval->data[codep].jump;
5500
+ /*there is no else block, so its -1*/
5501
+ retval->data[codep].jump = -1;
5502
+ /* Update code pointer to end of then code */
5503
+ codep = retval->data[codep-1].jump;
5509
+ /* no such action known */
5513
+ /* generate from next command */
5517
+ /*scriptend may be updated before the end, but it will be updated at the end, which is what matters.*/
5518
+ retval->scriptend=codep;
5525
+/* Entry point to the bytecode emitter module */
5526
+int sieve_generate_bytecode(bytecode_info_t **retval, sieve_script_t *s)
5530
+ if(!retval) return -1;
5533
+ /* if c is NULL, it is handled in bc_action_generate and a script
5534
+ with only BC_NULL is returned
5538
+ *retval = xmalloc(sizeof(bytecode_info_t));
5539
+ if(!(*retval)) return -1;
5541
+ memset(*retval, 0, sizeof(bytecode_info_t));
5543
+ return bc_action_generate(0, *retval, c);
5547
+void sieve_free_bytecode(bytecode_info_t **p)
5549
+ if(!p || !*p) return;
5550
+ if((*p)->data) free((*p)->data);
5555
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/bytecode.h
5556
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
5557
+++ b/src/lib-sieve/cmu/libsieve/bytecode.h Sun May 04 16:00:59 2008 +0200
5559
+/* bytecode.h -- bytecode definition
5561
+/***********************************************************
5562
+ Copyright 1999 by Carnegie Mellon University
5564
+ All Rights Reserved
5566
+Permission to use, copy, modify, and distribute this software and its
5567
+documentation for any purpose and without fee is hereby granted,
5568
+provided that the above copyright notice appear in all copies and that
5569
+both that copyright notice and this permission notice appear in
5570
+supporting documentation, and that the name of Carnegie Mellon
5571
+University not be used in advertising or publicity pertaining to
5572
+distribution of the software without specific, written prior
5575
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
5576
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
5577
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
5578
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
5579
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
5580
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
5581
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
5582
+*****************************************************************/
5584
+#ifndef SIEVE_BYTECODE_H
5585
+#define SIEVE_BYTECODE_H
5592
+/*for finding correctly aligned bytes on strings*/
5593
+/* bump to the next multiple of 4 bytes */
5594
+#define ROUNDUP(num) (((num) + 3) & 0xFFFFFFFC)
5597
+/* yes, lots of these are superfluous, it's for clarity */
5600
+ int op; /* OPTYPE */
5607
+ /* store strings (need 2 consecutive bytecodes) */
5612
+/* For sanity during input on 64-bit platforms.
5613
+ * str should only be accessed as (char *)&str, but given the use of
5614
+ * unwrap_string, this should be OK */
5617
+ int op; /* OPTYPE */
5624
+ /* store strings (need 2 consecutive bytecodes) */
5627
+} bytecode_input_t;
5630
+ /*version 0x01 scripts were written in host byte order.
5631
+ we don't want to use this version number again and cause a mess
5632
+ this isn't a huge concern, since this is version ntohl(1), or 16777216*/
5633
+#define BYTECODE_VERSION 0x03
5634
+#define BYTECODE_MAGIC "CyrSBytecode"
5635
+#define BYTECODE_MAGIC_LEN 12 /* Should be multiple of 4 */
5637
+/* IMPORTANT: To maintain forward compatibility of bytecode, please only add
5638
+ * new instructions to the end of these enums. (The reason these values
5639
+ * are all duplicated here is to avoid silliness if this caveat is forgotten
5640
+ * about in the other tables.) */
5646
+ B_REJECT,/* require reject*/
5647
+ B_FILEINTO,/*require fileinto*/
5652
+ B_MARK,/* require imapflags */
5653
+ B_UNMARK,/* require imapflags */
5655
+ B_ADDFLAG,/* require imapflags */
5656
+ B_SETFLAG,/* require imapflags */
5657
+ B_REMOVEFLAG,/* require imapflags */
5659
+ B_NOTIFY,/* require notify */
5660
+ B_DENOTIFY,/* require notify */
5662
+ B_VACATION,/* require vacation*/
5667
+enum bytecode_comps {
5676
+ BC_ENVELOPE, /* require envelope*/
5680
+/* currently one enum so as to help determine where values are being misused.
5681
+ * we have left placeholders incase we need to add more later to the middle */
5682
+enum bytecode_tags {
5687
+ B_SIZE_PLACEHOLDER_1,
5688
+ B_SIZE_PLACEHOLDER_2,
5691
+ B_GT, /* require relational*/
5692
+ B_GE, /* require relational*/
5693
+ B_LT, /* require relational*/
5694
+ B_LE, /* require relational*/
5695
+ B_EQ, /* require relational*/
5696
+ B_NE, /* require relational*/
5698
+ B_RELATIONAL_PLACEHOLDER_1,
5699
+ B_RELATIONAL_PLACEHOLDER_2,
5707
+ B_PRIORITY_PLACEHOLDER_1,
5708
+ B_PRIORITY_PLACEHOLDER_2,
5709
+ B_PRIORITY_PLACEHOLDER_3,
5710
+ B_PRIORITY_PLACEHOLDER_4,
5712
+ /* Address Part Tags */
5716
+ B_USER, /* require subaddress */
5717
+ B_DETAIL, /* require subaddress */
5719
+ B_ADDRESS_PLACEHOLDER_1,
5720
+ B_ADDRESS_PLACEHOLDER_2,
5721
+ B_ADDRESS_PLACEHOLDER_3,
5722
+ B_ADDRESS_PLACEHOLDER_4,
5727
+ B_ASCIINUMERIC, /* require comparator-i;ascii-numeric */
5729
+ B_COMPARATOR_PLACEHOLDER_1,
5730
+ B_COMPARATOR_PLACEHOLDER_2,
5731
+ B_COMPARATOR_PLACEHOLDER_3,
5732
+ B_COMPARATOR_PLACEHOLDER_4,
5738
+ B_REGEX,/* require regex*/
5739
+ B_COUNT,/* require relational*/
5740
+ B_VALUE,/* require relational*/
5742
+ B_MATCH_PLACEHOLDER_1,
5743
+ B_MATCH_PLACEHOLDER_2,
5744
+ B_MATCH_PLACEHOLDER_3,
5745
+ B_MATCH_PLACEHOLDER_4
5749
+int unwrap_string(bytecode_input_t *bc, int pos, const char **str, int *len);
5752
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/comparator.c
5753
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
5754
+++ b/src/lib-sieve/cmu/libsieve/comparator.c Sun May 04 16:00:59 2008 +0200
5756
+/* comparator.c -- comparator functions
5757
+ * Larry Greenfield
5760
+/***********************************************************
5761
+ Copyright 1999 by Carnegie Mellon University
5763
+ All Rights Reserved
5765
+Permission to use, copy, modify, and distribute this software and its
5766
+documentation for any purpose and without fee is hereby granted,
5767
+provided that the above copyright notice appear in all copies and that
5768
+both that copyright notice and this permission notice appear in
5769
+supporting documentation, and that the name of Carnegie Mellon
5770
+University not be used in advertising or publicity pertaining to
5771
+distribution of the software without specific, written prior
5774
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
5775
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
5776
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
5777
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
5778
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
5779
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
5780
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
5781
+******************************************************************/
5783
+#ifdef HAVE_CONFIG_H
5784
+#include <config.h>
5787
+#include <stdlib.h>
5789
+#include <string.h>
5791
+#include "xmalloc.h"
5792
+#include "comparator.h"
5795
+#include "bytecode.h"
5797
+/*!!! uses B_CONTAINS not CONTAINS, etc, only works with bytecode*/
5799
+extern int strcasecmp(const char *, const char *);
5801
+typedef int (*compare_t)(const void *, const void *);
5803
+/* --- relational comparators --- */
5805
+/* these are generic wrappers in which 'rock' is the compare function */
5807
+static int rel_eq(const char *text, const char *pat, void *rock)
5809
+ compare_t compar = (compare_t) rock;
5811
+ return (compar(text, pat) == 0);
5814
+static int rel_ne(const char *text, const char *pat, void *rock)
5816
+ compare_t compar = (compare_t) rock;
5818
+ return (compar(text, pat) != 0);
5821
+static int rel_gt(const char *text, const char *pat, void *rock)
5823
+ compare_t compar = (compare_t) rock;
5825
+ return (compar(text, pat) > 0);
5828
+static int rel_ge(const char *text, const char *pat, void *rock)
5830
+ compare_t compar = (compare_t) rock;
5832
+ return (compar(text, pat) >= 0);
5835
+static int rel_lt(const char *text, const char *pat, void *rock)
5837
+ compare_t compar = (compare_t) rock;
5839
+ return (compar(text, pat) < 0);
5842
+static int rel_le(const char *text, const char *pat, void *rock)
5844
+ compare_t compar = (compare_t) rock;
5846
+ return (compar(text, pat) <= 0);
5849
+/* --- i;octet comparators --- */
5851
+/* just compare the two; these should be NULL terminated */
5852
+static int octet_cmp(const char *text, const char *pat)
5857
+ sl = strlen(text) < strlen(pat) ? strlen(text) : strlen(pat);
5859
+ r = memcmp(text, pat, sl);
5862
+ return (strlen(text) - strlen(pat));
5867
+/* we implement boyer-moore for hell of it, since this is probably
5868
+ not very useful for sieve */
5870
+int boyer_moore(char *text, char *pat)
5872
+ int i, j; /* indexes */
5873
+ int M = strlen(pat); /* length of pattern */
5874
+ int N = strlen(text); /* length of text */
5875
+ int skip[256]; /* table of how much to skip, based on each character */
5877
+ /* initialize skip table */
5878
+ for (i = 0; i < 256; i++)
5880
+ for (i = 0; i < M; i++)
5881
+ skip[(int) pat[i]] = M-i-1;
5883
+ /* look for pat in text */
5886
+ if (pat[j] == text[i]) {
5890
+ if (M-j > skip[(int) text[i]]) {
5893
+ i = i + skip[(int) text[i]];
5897
+ } while (!((j < 0) || (i >= N)));
5898
+ /* i+1 is the position of the match if i < N */
5899
+ return (i < N) ? 1 : 0;
5903
+/* we do a brute force attack */
5904
+static int octet_contains(const char *text, const char *pat,
5905
+ void *rock __attr_unused__)
5907
+ return (strstr(text, pat) != NULL);
5910
+static int octet_matches_(const char *text, const char *pat, int casemap)
5920
+ /* ran out of pattern */
5921
+ return (*t == '\0');
5932
+ while (*p == '*' || *p == '?') {
5934
+ /* eat the character now */
5940
+ /* coalesce into a single wildcard */
5944
+ /* wildcard at end of string, any remaining text is ok */
5948
+ while (*t != '\0') {
5950
+ if (octet_matches_(t, p, casemap)) return 1;
5955
+ /* falls through */
5957
+ if (casemap && (toupper(c) == toupper(*t))) {
5959
+ } else if (!casemap && (c == *t)) {
5962
+ /* literal char doesn't match */
5967
+ /* never reaches */
5971
+static int octet_matches(const char *text, const char *pat,
5972
+ void *rock __attr_unused__)
5974
+ return octet_matches_(text, pat, 0);
5978
+#ifdef ENABLE_REGEX
5979
+static int octet_regex(const char *text, const char *pat,
5980
+ void *rock __attr_unused__)
5982
+ return (!regexec((regex_t *) pat, text, 0, NULL, 0));
5987
+/* --- i;ascii-casemap comparators --- */
5990
+/* use strcasecmp() as the compare function */
5992
+/* sheer brute force */
5993
+static int ascii_casemap_contains(const char *text, const char *pat,
5994
+ void *rock __attr_unused__)
5996
+ int N = strlen(text);
5997
+ int M = strlen(pat);
6001
+ while ((j < M) && (i < N)) {
6002
+ if (toupper(text[i]) == toupper(pat[j])) {
6010
+ return (j == M); /* we found a match! */
6013
+static int ascii_casemap_matches(const char *text, const char *pat,
6014
+ void *rock __attr_unused__)
6016
+ return octet_matches_(text, pat, 1);
6019
+/* i;ascii-numeric; only supports relational tests
6021
+ * A \ B number not-num
6022
+ * number A ? B B > A
6023
+ * not-num A > B A == B
6028
+ * The i;ascii-numeric comparator interprets strings as decimal
6029
+ * positive integers represented as US-ASCII digits. All values
6030
+ * which do not begin with a US-ASCII digit are considered equal
6031
+ * with an ordinal value higher than all non-NIL single-valued
6032
+ * attributes. Otherwise, all US-ASCII digits (octet values
6033
+ * 0x30 to 0x39) are interpreted starting from the beginning of
6034
+ * the string to the first non-digit or the end of the string.
6037
+static int ascii_numeric_cmp(const char *text, const char *pat)
6039
+ unsigned text_digit_len;
6040
+ unsigned pat_digit_len;
6042
+ if (isdigit((int) *pat)) {
6043
+ if (isdigit((int) *text)) {
6044
+ /* Count how many digits each string has */
6045
+ for (text_digit_len = 0;
6046
+ isdigit((int) text[text_digit_len]);
6047
+ text_digit_len++);
6048
+ for (pat_digit_len = 0;
6049
+ isdigit((int) pat[pat_digit_len]);
6052
+ if (text_digit_len < pat_digit_len) {
6053
+ /* Pad "text" with leading 0s */
6054
+ while (pat_digit_len > text_digit_len) {
6055
+ /* "text" can only be less or equal to "pat" */
6062
+ } else if (text_digit_len > pat_digit_len) {
6063
+ /* Pad "pad" with leading 0s */
6064
+ while (text_digit_len > pat_digit_len) {
6065
+ /* "pad" can only be greater or equal to "text" */
6066
+ if (*text > '0') {
6074
+ /* CLAIM: If we here, we have two non-empty digital suffixes
6075
+ of equal length */
6076
+ while (text_digit_len > 0) {
6077
+ if (*text < *pat) {
6079
+ } else if (*text > *pat) {
6082
+ /* Characters are equal, carry on */
6092
+ } else if (isdigit((int) *text)) {
6095
+ return 0; /* both not digits */
6099
+static comparator_t *lookup_rel(int relation)
6101
+ comparator_t *ret;
6128
+comparator_t *lookup_comp(int comp, int mode, int relation,
6131
+ comparator_t *ret;
6136
+ printf("comp%d mode%d relat%d \n", comp, mode, relation);
6144
+ *comprock = (void **) &octet_cmp;
6147
+ ret = &octet_contains;
6150
+ ret = &octet_matches;
6152
+#ifdef ENABLE_REGEX
6154
+ ret = &octet_regex;
6158
+ ret = lookup_rel(relation);
6159
+ *comprock = (void **) &octet_cmp;
6162
+ break; /*end of octet */
6163
+ case B_ASCIICASEMAP:
6167
+ *comprock = (void **) &strcasecmp;
6170
+ ret = &ascii_casemap_contains;
6173
+ ret = &ascii_casemap_matches;
6175
+#ifdef ENABLE_REGEX
6177
+ /* the ascii-casemap destinction is made during
6178
+ the compilation of the regex in verify_regex() */
6179
+ ret = &octet_regex;
6183
+ ret = lookup_rel(relation);
6184
+ *comprock = &strcasecmp;
6187
+ break;/*end of ascii casemap */
6188
+ case B_ASCIINUMERIC:
6192
+ *comprock = (void **) &ascii_numeric_cmp;
6196
+ ret = lookup_rel(relation);
6197
+ *comprock = (void **) &ascii_numeric_cmp;
6204
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/comparator.h
6205
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
6206
+++ b/src/lib-sieve/cmu/libsieve/comparator.h Sun May 04 16:00:59 2008 +0200
6209
+ * Larry Greenfield
6212
+/***********************************************************
6213
+ Copyright 1999 by Carnegie Mellon University
6215
+ All Rights Reserved
6217
+Permission to use, copy, modify, and distribute this software and its
6218
+documentation for any purpose and without fee is hereby granted,
6219
+provided that the above copyright notice appear in all copies and that
6220
+both that copyright notice and this permission notice appear in
6221
+supporting documentation, and that the name of Carnegie Mellon
6222
+University not be used in advertising or publicity pertaining to
6223
+distribution of the software without specific, written prior
6226
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
6227
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
6228
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
6229
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
6230
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
6231
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
6232
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
6233
+******************************************************************/
6235
+#ifndef COMPARATOR_H
6236
+#define COMPARATOR_H
6238
+#ifdef ENABLE_REGEX
6240
+#include <rxposix.h>
6242
+#include <sys/types.h>
6247
+/* compares pat to text; returns 1 if it's true, 0 otherwise
6248
+ first arg is text, second arg is pat, third arg is rock */
6249
+typedef int comparator_t(const char *, const char *, void *);
6251
+/* returns a pointer to a comparator function given it's name */
6252
+comparator_t *lookup_comp(int comp, int mode,
6253
+ int relation, void **rock);
6256
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/interp.c
6257
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
6258
+++ b/src/lib-sieve/cmu/libsieve/interp.c Sun May 04 16:00:59 2008 +0200
6260
+/* interp.c -- sieve script interpretor builder
6261
+ * Larry Greenfield
6264
+/***********************************************************
6265
+ Copyright 1999 by Carnegie Mellon University
6267
+ All Rights Reserved
6269
+Permission to use, copy, modify, and distribute this software and its
6270
+documentation for any purpose and without fee is hereby granted,
6271
+provided that the above copyright notice appear in all copies and that
6272
+both that copyright notice and this permission notice appear in
6273
+supporting documentation, and that the name of Carnegie Mellon
6274
+University not be used in advertising or publicity pertaining to
6275
+distribution of the software without specific, written prior
6278
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
6279
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
6280
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
6281
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
6282
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
6283
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
6284
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
6285
+******************************************************************/
6287
+#ifdef HAVE_CONFIG_H
6288
+#include <config.h>
6291
+#include <stdlib.h>
6293
+#include "xmalloc.h"
6295
+#include "sieve_interface.h"
6296
+#include "interp.h"
6298
+/* build a sieve interpretor */
6299
+int sieve_interp_alloc(sieve_interp_t **interp, void *interp_context)
6301
+ sieve_interp_t *i;
6302
+ static int initonce;
6305
+ initialize_siev_error_table();
6310
+ i = (sieve_interp_t *) xmalloc(sizeof(sieve_interp_t));
6312
+ return SIEVE_NOMEM;
6315
+ i->redirect = i->discard = i->reject = i->fileinto = i->keep = NULL;
6316
+ i->getsize = NULL;
6317
+ i->getheader = NULL;
6318
+ i->getenvelope = NULL;
6319
+ i->vacation = NULL;
6322
+ i->markflags = NULL;
6324
+ i->interp_context = interp_context;
6331
+static const char *sieve_extensions = "fileinto reject envelope vacation"
6332
+ " imapflags notify subaddress relational"
6333
+ " comparator-i;ascii-numeric"
6334
+#ifdef ENABLE_REGEX
6338
+#endif /* ENABLE_REGEX */
6340
+const char *sieve_listextensions(void)
6342
+ return sieve_extensions;
6345
+int sieve_interp_free(sieve_interp_t **interp)
6352
+/* add the callbacks */
6353
+int sieve_register_redirect(sieve_interp_t *interp, sieve_callback *f)
6355
+ interp->redirect = f;
6360
+int sieve_register_discard(sieve_interp_t *interp, sieve_callback *f)
6362
+ interp->discard = f;
6367
+int sieve_register_reject(sieve_interp_t *interp, sieve_callback *f)
6369
+ interp->reject = f;
6374
+int sieve_register_fileinto(sieve_interp_t *interp, sieve_callback *f)
6376
+ interp->fileinto = f;
6381
+int sieve_register_keep(sieve_interp_t *interp, sieve_callback *f)
6388
+static char *default_markflags[] = { "\\flagged" };
6389
+static sieve_imapflags_t default_mark = { default_markflags, 1 };
6391
+int sieve_register_imapflags(sieve_interp_t *interp, sieve_imapflags_t *mark)
6393
+ interp->markflags =
6394
+ (mark && mark->flag && mark->nflags) ? mark : &default_mark;
6399
+int sieve_register_notify(sieve_interp_t *interp, sieve_callback *f)
6401
+ interp->notify = f;
6406
+/* add the callbacks for messages. again, undefined if used after
6407
+ sieve_script_parse */
6408
+int sieve_register_size(sieve_interp_t *interp, sieve_get_size *f)
6410
+ interp->getsize = f;
6414
+int sieve_register_header(sieve_interp_t *interp, sieve_get_header *f)
6416
+ interp->getheader = f;
6420
+int sieve_register_envelope(sieve_interp_t *interp, sieve_get_envelope *f)
6422
+ interp->getenvelope = f;
6426
+int sieve_register_vacation(sieve_interp_t *interp, sieve_vacation_t *v)
6428
+ if (!interp->getenvelope) {
6429
+ return SIEVE_NOT_FINALIZED; /* we need envelope for vacation! */
6432
+ if (v->min_response == 0) v->min_response = 3;
6433
+ if (v->max_response == 0) v->max_response = 90;
6434
+ if (v->min_response < 0 || v->max_response < 7 || !v->autorespond
6435
+ || !v->send_response) {
6436
+ return SIEVE_FAIL;
6439
+ interp->vacation = v;
6443
+int sieve_register_parse_error(sieve_interp_t *interp, sieve_parse_error *f)
6449
+int sieve_register_execute_error(sieve_interp_t *interp, sieve_execute_error *f)
6451
+ interp->execute_err = f;
6455
+int interp_verify(sieve_interp_t *i)
6457
+ if (i->redirect && i->keep && i->getsize && i->getheader) {
6460
+ return SIEVE_NOT_FINALIZED;
6463
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/interp.h
6464
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
6465
+++ b/src/lib-sieve/cmu/libsieve/interp.h Sun May 04 16:00:59 2008 +0200
6467
+/* interp.h -- interpretor definition
6468
+ * Larry Greenfield
6471
+/***********************************************************
6472
+ Copyright 1999 by Carnegie Mellon University
6474
+ All Rights Reserved
6476
+Permission to use, copy, modify, and distribute this software and its
6477
+documentation for any purpose and without fee is hereby granted,
6478
+provided that the above copyright notice appear in all copies and that
6479
+both that copyright notice and this permission notice appear in
6480
+supporting documentation, and that the name of Carnegie Mellon
6481
+University not be used in advertising or publicity pertaining to
6482
+distribution of the software without specific, written prior
6485
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
6486
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
6487
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
6488
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
6489
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
6490
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
6491
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
6492
+*****************************************************************/
6494
+#ifndef SIEVE_INTERP_H
6495
+#define SIEVE_INTERP_H
6497
+#include "sieve_interface.h"
6499
+struct sieve_interp {
6500
+ /* standard callbacks for actions */
6501
+ sieve_callback *redirect, *discard, *reject, *fileinto, *keep;
6502
+ sieve_callback *notify;
6503
+ sieve_vacation_t *vacation;
6505
+ sieve_get_size *getsize;
6506
+ sieve_get_header *getheader;
6507
+ sieve_get_envelope *getenvelope;
6509
+ sieve_parse_error *err;
6511
+ /* site-specific imapflags for mark/unmark */
6512
+ sieve_imapflags_t *markflags;
6514
+ sieve_execute_error *execute_err;
6516
+ /* context to pass along */
6517
+ void *interp_context;
6520
+int interp_verify(sieve_interp_t *interp);
6523
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/message.c
6524
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
6525
+++ b/src/lib-sieve/cmu/libsieve/message.c Sun May 04 16:00:59 2008 +0200
6527
+/* message.c -- message parsing functions
6528
+ * Larry Greenfield
6531
+/***********************************************************
6532
+ Copyright 1999 by Carnegie Mellon University
6534
+ All Rights Reserved
6536
+Permission to use, copy, modify, and distribute this software and its
6537
+documentation for any purpose and without fee is hereby granted,
6538
+provided that the above copyright notice appear in all copies and that
6539
+both that copyright notice and this permission notice appear in
6540
+supporting documentation, and that the name of Carnegie Mellon
6541
+University not be used in advertising or publicity pertaining to
6542
+distribution of the software without specific, written prior
6545
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
6546
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
6547
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
6548
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
6549
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
6550
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
6551
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
6552
+******************************************************************/
6554
+#ifdef HAVE_CONFIG_H
6555
+#include <config.h>
6558
+#include <stdlib.h>
6559
+#include <unistd.h>
6560
+#include <sys/mman.h>
6561
+#include <sys/stat.h>
6562
+#include <sys/types.h>
6563
+#include <sys/stat.h>
6565
+#include <string.h>
6567
+#include "sieve_interface.h"
6568
+#include "interp.h"
6569
+#include "message.h"
6570
+#include "parseaddr.h"
6571
+#include "xmalloc.h"
6573
+/* reject message m with message msg
6575
+ * incompatible with: fileinto, redirect
6577
+int do_reject(action_list_t *a, const char *msg)
6579
+ action_list_t *b = NULL;
6581
+ /* see if this conflicts with any previous actions taken on this message */
6582
+ while (a != NULL) {
6584
+ if (a->a == ACTION_FILEINTO ||
6585
+ a->a == ACTION_KEEP ||
6586
+ a->a == ACTION_REDIRECT ||
6587
+ a->a == ACTION_REJECT ||
6588
+ a->a == ACTION_VACATION ||
6589
+ a->a == ACTION_SETFLAG ||
6590
+ a->a == ACTION_ADDFLAG ||
6591
+ a->a == ACTION_REMOVEFLAG ||
6592
+ a->a == ACTION_MARK ||
6593
+ a->a == ACTION_UNMARK
6595
+ return SIEVE_RUN_ERROR;
6599
+ /* add to the action list */
6600
+ a = (action_list_t *) xmalloc(sizeof(action_list_t));
6602
+ return SIEVE_NOMEM;
6603
+ a->a = ACTION_REJECT;
6604
+ a->u.rej.msg = msg;
6610
+/* fileinto message m into mailbox
6612
+ * incompatible with: reject
6614
+int do_fileinto(action_list_t *a, const char *mbox,
6615
+ sieve_imapflags_t *imapflags)
6617
+ action_list_t *b = NULL;
6619
+ /* see if this conflicts with any previous actions taken on this message */
6620
+ while (a != NULL) {
6622
+ if (a->a == ACTION_REJECT)
6623
+ return SIEVE_RUN_ERROR;
6627
+ /* add to the action list */
6628
+ a = (action_list_t *) xmalloc(sizeof(action_list_t));
6630
+ return SIEVE_NOMEM;
6631
+ a->a = ACTION_FILEINTO;
6632
+ a->u.fil.mailbox = mbox;
6633
+ a->u.fil.imapflags = imapflags;
6639
+/* redirect message m to to addr
6641
+ * incompatible with: reject
6643
+int do_redirect(action_list_t *a, const char *addr)
6645
+ action_list_t *b = NULL;
6647
+ /* xxx we should validate addr */
6649
+ /* see if this conflicts with any previous actions taken on this message */
6650
+ while (a != NULL) {
6652
+ if (a->a == ACTION_REJECT)
6653
+ return SIEVE_RUN_ERROR;
6657
+ /* add to the action list */
6658
+ a = (action_list_t *) xmalloc(sizeof(action_list_t));
6660
+ return SIEVE_NOMEM;
6661
+ a->a = ACTION_REDIRECT;
6662
+ a->u.red.addr = addr;
6670
+ * incompatible with: reject
6672
+int do_keep(action_list_t *a, sieve_imapflags_t *imapflags)
6674
+ action_list_t *b = NULL;
6676
+ /* see if this conflicts with any previous actions taken on this message */
6677
+ while (a != NULL) {
6679
+ if (a->a == ACTION_REJECT)
6680
+ return SIEVE_RUN_ERROR;
6681
+ if (a->a == ACTION_KEEP) /* don't bother doing it twice */
6686
+ /* add to the action list */
6687
+ a = (action_list_t *) xmalloc(sizeof(action_list_t));
6689
+ return SIEVE_NOMEM;
6690
+ a->a = ACTION_KEEP;
6691
+ a->u.keep.imapflags = imapflags;
6697
+/* discard message m
6699
+ * incompatible with: nothing---it doesn't cancel any actions
6701
+int do_discard(action_list_t *a)
6703
+ action_list_t *b = NULL;
6705
+ /* see if this conflicts with any previous actions taken on this message */
6706
+ while (a != NULL) {
6708
+ if (a->a == ACTION_DISCARD) /* don't bother doing twice */
6713
+ /* add to the action list */
6714
+ a = (action_list_t *) xmalloc(sizeof(action_list_t));
6716
+ return SIEVE_NOMEM;
6717
+ a->a = ACTION_DISCARD;
6723
+int do_vacation(action_list_t *a, char *addr, char *fromaddr,
6724
+ char *subj, const char *msg, int days,
6727
+ action_list_t *b = NULL;
6729
+ /* see if this conflicts with any previous actions taken on this message */
6730
+ while (a != NULL) {
6732
+ if (a->a == ACTION_REJECT ||
6733
+ a->a == ACTION_VACATION) /* vacation can't be used twice */
6734
+ return SIEVE_RUN_ERROR;
6738
+ /* add to the action list */
6739
+ a = (action_list_t *) xmalloc(sizeof(action_list_t));
6741
+ return SIEVE_NOMEM;
6742
+ a->a = ACTION_VACATION;
6743
+ a->u.vac.send.addr = addr;
6744
+ a->u.vac.send.fromaddr = fromaddr;
6745
+ a->u.vac.send.subj = subj; /* user specified subject */
6746
+ a->u.vac.send.msg = msg;
6747
+ a->u.vac.send.mime = mime;
6748
+ a->u.vac.autoresp.days = days;
6754
+/* setflag f on message m
6756
+ * incompatible with: reject
6758
+int do_setflag(action_list_t *a, const char *flag)
6760
+ action_list_t *b = NULL;
6762
+ /* see if this conflicts with any previous actions taken on this message */
6763
+ while (a != NULL) {
6765
+ if (a->a == ACTION_REJECT)
6766
+ return SIEVE_RUN_ERROR;
6770
+ /* add to the action list */
6771
+ a = (action_list_t *) xmalloc(sizeof(action_list_t));
6773
+ return SIEVE_NOMEM;
6774
+ a->a = ACTION_SETFLAG;
6775
+ a->u.fla.flag = flag;
6781
+/* addflag f on message m
6783
+ * incompatible with: reject
6785
+int do_addflag(action_list_t *a, const char *flag)
6787
+ action_list_t *b = NULL;
6789
+ /* see if this conflicts with any previous actions taken on this message */
6790
+ while (a != NULL) {
6792
+ if (a->a == ACTION_REJECT)
6793
+ return SIEVE_RUN_ERROR;
6797
+ /* add to the action list */
6798
+ a = (action_list_t *) xmalloc(sizeof(action_list_t));
6800
+ return SIEVE_NOMEM;
6801
+ a->a = ACTION_ADDFLAG;
6802
+ a->u.fla.flag = flag;
6808
+/* removeflag f on message m
6810
+ * incompatible with: reject
6812
+int do_removeflag(action_list_t *a, const char *flag)
6814
+ action_list_t *b = NULL;
6816
+ /* see if this conflicts with any previous actions taken on this message */
6817
+ while (a != NULL) {
6819
+ if (a->a == ACTION_REJECT)
6820
+ return SIEVE_RUN_ERROR;
6824
+ /* add to the action list */
6825
+ a = (action_list_t *) xmalloc(sizeof(action_list_t));
6827
+ return SIEVE_NOMEM;
6828
+ a->a = ACTION_REMOVEFLAG;
6829
+ a->u.fla.flag = flag;
6838
+ * incompatible with: reject
6840
+int do_mark(action_list_t *a)
6842
+ action_list_t *b = NULL;
6844
+ /* see if this conflicts with any previous actions taken on this message */
6845
+ while (a != NULL) {
6847
+ if (a->a == ACTION_REJECT)
6848
+ return SIEVE_RUN_ERROR;
6852
+ /* add to the action list */
6853
+ a = (action_list_t *) xmalloc(sizeof(action_list_t));
6855
+ return SIEVE_NOMEM;
6856
+ a->a = ACTION_MARK;
6863
+/* unmark message m
6865
+ * incompatible with: reject
6867
+int do_unmark(action_list_t *a)
6870
+ action_list_t *b = NULL;
6871
+ /* see if this conflicts with any previous actions taken on this message */
6872
+ while (a != NULL) {
6874
+ if (a->a == ACTION_REJECT)
6875
+ return SIEVE_RUN_ERROR;
6879
+ /* add to the action list */
6880
+ a = (action_list_t *) xmalloc(sizeof(action_list_t));
6882
+ return SIEVE_NOMEM;
6883
+ a->a = ACTION_UNMARK;
6891
+ * incompatible with: none
6893
+int do_notify(notify_list_t *a, const char *id,
6894
+ const char *method, const char **options,
6895
+ const char *priority, const char *message)
6897
+ notify_list_t *b = NULL;
6899
+ /* find the end of the notify list */
6900
+ while (a != NULL) {
6905
+ /* add to the notify list */
6906
+ a = (notify_list_t *) xmalloc(sizeof(notify_list_t));
6908
+ return SIEVE_NOMEM;
6913
+ a->method = method;
6914
+ a->options = options;
6915
+ a->priority = priority;
6916
+ a->message = message;
6923
+ * incomaptible with: none
6925
+int do_denotify(notify_list_t *n, comparator_t *comp, const void *pat,
6926
+ void *comprock, const char *priority)
6928
+ while (n != NULL) {
6929
+ if (n->isactive &&
6930
+ (!priority || !strcasecmp(n->priority, priority)) &&
6931
+ (!comp || (n->id && comp(n->id, pat, comprock)))) {
6942
+/* given a header, extract an address out of it. if marker points to NULL,
6943
+ extract the first address. otherwise, it's an index into the header to
6944
+ say where to start extracting */
6945
+struct addr_marker {
6946
+ struct address *where;
6950
+int parse_address(const char *header, void **data, void **marker)
6952
+ struct addr_marker *am = (struct addr_marker *) *marker;
6954
+ parseaddr_list(header, (struct address **) data);
6955
+ am = (void *) xmalloc(sizeof(struct addr_marker));
6956
+ am->where = *data;
6957
+ am->freeme = NULL;
6962
+char *get_address(address_part_t addrpart,
6963
+ void **data __attr_unused__,
6968
+ struct address *a;
6969
+ struct addr_marker *am = *marker;
6974
+ am->freeme = NULL;
6980
+ if (canon_domain && a->domain)
6983
+ switch (addrpart) {
6985
+#define U_DOMAIN "unspecified-domain"
6986
+#define U_USER "unknown-user"
6987
+ if (a->mailbox || a->domain) {
6988
+ char *m = a->mailbox ? a->mailbox : U_USER;
6989
+ char *d = a->domain ? a->domain : U_DOMAIN;
6990
+ am->freeme = (char *) xmalloc(strlen(m) + strlen(d) + 2);
6992
+ sprintf(am->freeme, "%s@%s", m, d);
6999
+ case ADDRESS_LOCALPART:
7003
+ case ADDRESS_DOMAIN:
7007
+ case ADDRESS_USER:
7009
+ char *p = strchr(a->mailbox, '+');
7010
+ int len = p ? p - a->mailbox : (int)strlen(a->mailbox);
7012
+ am->freeme = (char *) xmalloc(len + 1);
7013
+ strncpy(am->freeme, a->mailbox, len);
7014
+ am->freeme[len] = '\0';
7021
+ case ADDRESS_DETAIL:
7024
+ char *p = strchr(a->mailbox, '+');
7025
+ ret = (p ? p + 1 : NULL);
7040
+int free_address(void **data, void **marker)
7042
+ struct addr_marker *am = (struct addr_marker *) *marker;
7045
+ parseaddr_free((struct address *) *data);
7047
+ if (am->freeme) free(am->freeme);
7053
+notify_list_t *new_notify_list(void)
7055
+ notify_list_t *ret = xmalloc(sizeof(notify_list_t));
7057
+ if (ret != NULL) {
7058
+ ret->isactive = 0;
7060
+ ret->method = NULL;
7061
+ ret->options = NULL;
7062
+ ret->priority = NULL;
7063
+ ret->message = NULL;
7069
+void free_notify_list(notify_list_t *n)
7072
+ notify_list_t *b = n->next;
7073
+ free(n->options); /* strings live in bytecode, only free the array */
7079
+action_list_t *new_action_list(void)
7081
+ action_list_t *ret = xmalloc(sizeof(action_list_t));
7083
+ if (ret != NULL) {
7084
+ ret->a = ACTION_NONE;
7085
+ ret->param = NULL;
7091
+void free_action_list(action_list_t *a)
7094
+ action_list_t *b = a->next;
7096
+ if(a->a == ACTION_VACATION) {
7097
+ if(a->u.vac.send.subj) free(a->u.vac.send.subj);
7098
+ if(a->u.vac.send.addr) free(a->u.vac.send.addr);
7099
+ if(a->u.vac.send.fromaddr) free(a->u.vac.send.fromaddr);
7107
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/message.h
7108
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
7109
+++ b/src/lib-sieve/cmu/libsieve/message.h Sun May 04 16:00:59 2008 +0200
7112
+ * Larry Greenfield
7115
+/***********************************************************
7116
+ Copyright 1999 by Carnegie Mellon University
7118
+ All Rights Reserved
7120
+Permission to use, copy, modify, and distribute this software and its
7121
+documentation for any purpose and without fee is hereby granted,
7122
+provided that the above copyright notice appear in all copies and that
7123
+both that copyright notice and this permission notice appear in
7124
+supporting documentation, and that the name of Carnegie Mellon
7125
+University not be used in advertising or publicity pertaining to
7126
+distribution of the software without specific, written prior
7129
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
7130
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
7131
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
7132
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
7133
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
7134
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
7135
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
7136
+******************************************************************/
7141
+#include "sieve_interface.h" /* for action contexts */
7142
+#include "tree.h" /* for stringlist_t */
7144
+typedef struct Action action_list_t;
7157
+ ACTION_REMOVEFLAG,
7165
+action_list_t *new_action_list(void);
7166
+void free_action_list(action_list_t *actions);
7168
+/* invariant: always have a dummy element when free_action_list, param
7169
+ and vac_subj are freed. none of the others are automatically freed.
7171
+ the do_action() functions should copy param */
7175
+ sieve_reject_context_t rej;
7176
+ sieve_fileinto_context_t fil;
7177
+ sieve_keep_context_t keep;
7178
+ sieve_redirect_context_t red;
7180
+ /* addr, fromaddr, subj - freed! */
7181
+ sieve_send_response_context_t send;
7182
+ sieve_autorespond_context_t autoresp;
7188
+ char *param; /* freed! */
7189
+ struct Action *next;
7190
+ char *vac_subj; /* freed! */
7195
+typedef struct notify_list_s {
7198
+ const char *method;
7199
+ const char **options;
7200
+ const char *priority;
7201
+ const char *message;
7202
+ struct notify_list_s *next;
7205
+/* header parsing */
7208
+ ADDRESS_LOCALPART,
7214
+int parse_address(const char *header, void **data, void **marker);
7215
+char *get_address(address_part_t addrpart, void **data, void **marker,
7216
+ int canon_domain);
7217
+int free_address(void **data, void **marker);
7218
+notify_list_t *new_notify_list(void);
7219
+void free_notify_list(notify_list_t *n);
7221
+/* actions; return negative on failure.
7222
+ * these don't actually perform the actions, they just add it to the
7224
+int do_reject(action_list_t *m, const char *msg);
7225
+int do_fileinto(action_list_t *m, const char *mbox,
7226
+ sieve_imapflags_t *imapflags);
7227
+int do_redirect(action_list_t *m, const char *addr);
7228
+int do_keep(action_list_t *m, sieve_imapflags_t *imapflags);
7229
+int do_discard(action_list_t *m);
7230
+int do_vacation(action_list_t *m, char *addr, char *fromaddr,
7231
+ char *subj, const char *msg, int days, int mime);
7232
+int do_setflag(action_list_t *m, const char *flag);
7233
+int do_addflag(action_list_t *m, const char *flag);
7234
+int do_removeflag(action_list_t *m, const char *flag);
7235
+int do_mark(action_list_t *m);
7236
+int do_unmark(action_list_t *m);
7237
+int do_notify(notify_list_t *n, const char *id,
7238
+ const char *method, const char **options,
7239
+ const char *priority, const char *message);
7240
+int do_denotify(notify_list_t *n, comparator_t *comp, const void *pat,
7241
+ void *comprock, const char *priority);
7243
+/* execute some bytecode */
7244
+int sieve_eval_bc(sieve_interp_t *i, const void *bc_in, unsigned int bc_len,
7245
+ void *m, sieve_imapflags_t * imapflags,
7246
+ action_list_t *actions,
7247
+ notify_list_t *notify_list,
7248
+ const char **errmsg);
7251
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/parseaddr.c
7252
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
7253
+++ b/src/lib-sieve/cmu/libsieve/parseaddr.c Sun May 04 16:00:59 2008 +0200
7255
+/* parseaddr.c -- RFC 822 address parser
7258
+ * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
7260
+ * Redistribution and use in source and binary forms, with or without
7261
+ * modification, are permitted provided that the following conditions
7264
+ * 1. Redistributions of source code must retain the above copyright
7265
+ * notice, this list of conditions and the following disclaimer.
7267
+ * 2. Redistributions in binary form must reproduce the above copyright
7268
+ * notice, this list of conditions and the following disclaimer in
7269
+ * the documentation and/or other materials provided with the
7272
+ * 3. The name "Carnegie Mellon University" must not be used to
7273
+ * endorse or promote products derived from this software without
7274
+ * prior written permission. For permission or any other legal
7275
+ * details, please contact
7276
+ * Office of Technology Transfer
7277
+ * Carnegie Mellon University
7278
+ * 5000 Forbes Avenue
7279
+ * Pittsburgh, PA 15213-3890
7280
+ * (412) 268-4387, fax: (412) 268-7395
7281
+ * tech-transfer@andrew.cmu.edu
7283
+ * 4. Redistributions of any form whatsoever must retain the following
7285
+ * "This product includes software developed by Computing Services
7286
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
7288
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
7289
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
7290
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
7291
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
7292
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
7293
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
7294
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
7298
+#include <config.h>
7300
+#include <stdlib.h>
7302
+#include <string.h>
7304
+#include "parseaddr.h"
7306
+#define xmalloc malloc
7307
+#define xstrdup strdup
7309
+static char parseaddr_unspecified_domain[] = "unspecified-domain";
7311
+static void parseaddr_append (struct address ***addrpp, char *name,
7312
+ char *route, char *mailbox, char *domain,
7314
+static int parseaddr_phrase (char **inp, char **phrasep, char *specials);
7315
+static int parseaddr_domain (char **inp, char **domainp, char **commmentp);
7316
+static int parseaddr_route (char **inp, char **routep);
7319
+ * Parse an address list in 's', appending address structures to
7320
+ * the list pointed to by 'addrp'.
7323
+parseaddr_list(str, addrp)
7325
+struct address **addrp;
7331
+ char *phrase, *route, *mailbox, *domain, *comment;
7333
+ /* Skip down to the tail */
7335
+ addrp = &(*addrp)->next;
7338
+ s = freeme = xstrdup(str);
7341
+ tok = parseaddr_phrase(&s, &phrase, ingroup ? ",@<;" : ",@<:");
7347
+ parseaddr_append(&addrp, 0, 0, phrase, "", &freeme);
7350
+ parseaddr_append(&addrp, 0, 0, 0, 0, &freeme);
7356
+ parseaddr_append(&addrp, 0, 0, phrase, 0, &freeme);
7361
+ tok = parseaddr_domain(&s, &domain, &comment);
7362
+ parseaddr_append(&addrp, comment, 0, phrase, domain, &freeme);
7366
+ tok = parseaddr_phrase(&s, &mailbox, "@>");
7371
+ tok = parseaddr_route(&s, &route);
7373
+ parseaddr_append(&addrp, phrase, route, "", "", &freeme);
7374
+ while (tok && tok != '>') tok = *s++;
7377
+ tok = parseaddr_phrase(&s, &mailbox, "@>");
7379
+ parseaddr_append(&addrp, phrase, route, mailbox, "",
7384
+ tok = parseaddr_domain(&s, &domain, 0);
7385
+ parseaddr_append(&addrp, phrase, route, mailbox, domain,
7387
+ while (tok && tok != '>') tok = *s++;
7388
+ continue; /* effectively auto-inserts a comma */
7391
+ parseaddr_append(&addrp, phrase, 0, mailbox, "", &freeme);
7395
+ if (ingroup) parseaddr_append(&addrp, 0, 0, 0, 0, &freeme);
7397
+ if (freeme) free(freeme);
7401
+ * Free the address list 'addr'
7404
+parseaddr_free(addr)
7405
+struct address *addr;
7407
+ struct address *next;
7410
+ if (addr->freeme) free(addr->freeme);
7411
+ next = addr->next;
7412
+ free((char *)addr);
7418
+ * Helper function to append a new address structure to and address list.
7421
+parseaddr_append(addrpp, name, route, mailbox, domain, freemep)
7422
+struct address ***addrpp;
7429
+ struct address *newaddr;
7431
+ newaddr = (struct address *)xmalloc(sizeof(struct address));
7432
+ if (name && *name) {
7433
+ newaddr->name = name;
7436
+ newaddr->name = 0;
7439
+ if (route && *route) {
7440
+ newaddr->route = route;
7443
+ newaddr->route = 0;
7446
+ newaddr->mailbox = mailbox;
7448
+ if (domain && !*domain) {
7449
+ domain = parseaddr_unspecified_domain;
7451
+ newaddr->domain = domain;
7453
+ newaddr->next = 0;
7454
+ newaddr->freeme = *freemep;
7457
+ **addrpp = newaddr;
7458
+ *addrpp = &newaddr->next;
7461
+/* Macro to skip white space and rfc822 comments */
7463
+#define SKIPWHITESPACE(s) \
7465
+ int _c, _comment = 0; \
7467
+ while ((_c = *(s))) { \
7468
+ if (_c == '(') { \
7471
+ while ((_comment && (_c = *(s)))) { \
7473
+ if (_c == '\\' && *(s)) (s)++; \
7474
+ else if (_c == '(') _comment++; \
7475
+ else if (_c == ')') _comment--; \
7479
+ else if (!isspace(_c)) break; \
7485
+ * Parse an RFC 822 "phrase", stopping at 'specials'
7487
+static int parseaddr_phrase(inp, phrasep, specials)
7496
+ SKIPWHITESPACE(src);
7498
+ *phrasep = dst = src;
7503
+ while ((c = *src)) {
7505
+ if (c == '\"') break;
7507
+ if (!(c = *src)) break;
7513
+ else if (isspace(c) || c == '(') {
7515
+ SKIPWHITESPACE(src);
7518
+ else if (!c || strchr(specials, c)) {
7519
+ if (dst > *phrasep && dst[-1] == ' ') dst--;
7531
+ * Parse a domain. If 'commentp' is non-nil, parses any trailing comment
7533
+static int parseaddr_domain(inp, domainp, commentp)
7544
+ if (commentp) *commentp = 0;
7545
+ SKIPWHITESPACE(src);
7547
+ *domainp = dst = src;
7551
+ if (isalnum(c) || c == '-' || c == '[' || c == ']' || c == ':') {
7553
+ if (commentp) *commentp = 0;
7555
+ else if (c == '.') {
7556
+ if (dst > *domainp && dst[-1] != '.') *dst++ = c;
7557
+ if (commentp) *commentp = 0;
7559
+ else if (c == '(') {
7561
+ *commentp = cdst = src;
7563
+ while (comment && (c = *src)) {
7565
+ if (c == '(') comment++;
7566
+ else if (c == ')') comment--;
7567
+ else if (c == '\\' && (c = *src)) src++;
7569
+ if (comment) *cdst++ = c;
7575
+ SKIPWHITESPACE(src);
7578
+ else if (!isspace(c)) {
7579
+ if (dst > *domainp && dst[-1] == '.') dst--;
7588
+ * Parse a source route (at-domain-list)
7590
+static int parseaddr_route(inp, routep)
7598
+ SKIPWHITESPACE(src);
7600
+ *routep = dst = src;
7604
+ if (isalnum(c) || c == '-' || c == '[' || c == ']' ||
7605
+ c == ',' || c == '@') {
7608
+ else if (c == '.') {
7609
+ if (dst > *routep && dst[-1] != '.') *dst++ = c;
7611
+ else if (isspace(c) || c == '(') {
7613
+ SKIPWHITESPACE(src);
7616
+ while (dst > *routep &&
7617
+ (dst[-1] == '.' || dst[-1] == ',' || dst[-1] == '@')) dst--;
7625
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/parseaddr.h
7626
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
7627
+++ b/src/lib-sieve/cmu/libsieve/parseaddr.h Sun May 04 16:00:59 2008 +0200
7629
+/* parseaddr.h -- RFC 822 address parser
7632
+ * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved.
7634
+ * Redistribution and use in source and binary forms, with or without
7635
+ * modification, are permitted provided that the following conditions
7638
+ * 1. Redistributions of source code must retain the above copyright
7639
+ * notice, this list of conditions and the following disclaimer.
7641
+ * 2. Redistributions in binary form must reproduce the above copyright
7642
+ * notice, this list of conditions and the following disclaimer in
7643
+ * the documentation and/or other materials provided with the
7646
+ * 3. The name "Carnegie Mellon University" must not be used to
7647
+ * endorse or promote products derived from this software without
7648
+ * prior written permission. For permission or any other legal
7649
+ * details, please contact
7650
+ * Office of Technology Transfer
7651
+ * Carnegie Mellon University
7652
+ * 5000 Forbes Avenue
7653
+ * Pittsburgh, PA 15213-3890
7654
+ * (412) 268-4387, fax: (412) 268-7395
7655
+ * tech-transfer@andrew.cmu.edu
7657
+ * 4. Redistributions of any form whatsoever must retain the following
7659
+ * "This product includes software developed by Computing Services
7660
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
7662
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
7663
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
7664
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
7665
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
7666
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
7667
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
7668
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
7673
+#ifndef INCLUDED_PARSEADDR_H
7674
+#define INCLUDED_PARSEADDR_H
7689
+ struct address *next;
7690
+ char *freeme; /* If non-nil, free */
7693
+extern void parseaddr_list P((const char *s, struct address **addrp));
7694
+extern void parseaddr_free P((struct address *addr));
7697
+#endif /* INCLUDED_PARSEADDR_H */
7698
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/script.c
7699
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
7700
+++ b/src/lib-sieve/cmu/libsieve/script.c Sun May 04 16:00:59 2008 +0200
7702
+/* script.c -- sieve script functions
7703
+ * Larry Greenfield
7706
+/***********************************************************
7707
+ Copyright 1999 by Carnegie Mellon University
7709
+ All Rights Reserved
7711
+Permission to use, copy, modify, and distribute this software and its
7712
+documentation for any purpose and without fee is hereby granted,
7713
+provided that the above copyright notice appear in all copies and that
7714
+both that copyright notice and this permission notice appear in
7715
+supporting documentation, and that the name of Carnegie Mellon
7716
+University not be used in advertising or publicity pertaining to
7717
+distribution of the software without specific, written prior
7720
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
7721
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
7722
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
7723
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
7724
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
7725
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
7726
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
7727
+******************************************************************/
7729
+#ifdef HAVE_CONFIG_H
7730
+#include <config.h>
7733
+#include <stdlib.h>
7734
+#include <string.h>
7736
+#include <sys/stat.h>
7737
+#include <sys/types.h>
7738
+#include <unistd.h>
7739
+#include <assert.h>
7741
+#include "xmalloc.h"
7744
+#include "sieve_interface.h"
7745
+#include "interp.h"
7746
+#include "script.h"
7750
+#include "message.h"
7751
+#include "bytecode.h"
7753
+/* does this interpretor support this requirement? */
7754
+int script_require(sieve_script_t *s, char *req)
7756
+ if (!strcmp("fileinto", req)) {
7757
+ if (s->interp.fileinto) {
7758
+ s->support.fileinto = 1;
7763
+ } else if (!strcmp("reject", req)) {
7764
+ if (s->interp.reject) {
7765
+ s->support.reject = 1;
7770
+ } else if (!strcmp("envelope", req)) {
7771
+ if (s->interp.getenvelope) {
7772
+ s->support.envelope = 1;
7777
+ } else if (!strcmp("vacation", req)) {
7778
+ if (s->interp.vacation) {
7779
+ s->support.vacation = 1;
7784
+ } else if (!strcmp("imapflags", req)) {
7785
+ if (s->interp.markflags->flag) {
7786
+ s->support.imapflags = 1;
7791
+ } else if (!strcmp("notify",req)) {
7792
+ if (s->interp.notify) {
7793
+ s->support.notify = 1;
7798
+#ifdef ENABLE_REGEX
7799
+ } else if (!strcmp("regex", req)) {
7800
+ s->support.regex = 1;
7803
+ } else if (!strcmp("subaddress", req)) {
7804
+ s->support.subaddress = 1;
7806
+ } else if (!strcmp("relational", req)) {
7807
+ s->support.relational = 1;
7809
+ } else if (!strcmp("comparator-i;octet", req)) {
7811
+ } else if (!strcmp("comparator-i;ascii-casemap", req)) {
7813
+ } else if (!strcmp("comparator-i;ascii-numeric", req)) {
7814
+ s->support.i_ascii_numeric = 1;
7820
+/* given an interpretor and a script, produce an executable script */
7821
+int sieve_script_parse(sieve_interp_t *interp, FILE *script,
7822
+ void *script_context, sieve_script_t **ret)
7824
+ sieve_script_t *s;
7825
+ int res = SIEVE_OK;
7826
+ extern int yylineno;
7828
+ res = interp_verify(interp);
7829
+ if (res != SIEVE_OK) {
7833
+ s = (sieve_script_t *) xmalloc(sizeof(sieve_script_t));
7834
+ s->interp = *interp;
7835
+ s->script_context = script_context;
7836
+ /* clear all support bits */
7837
+ memset(&s->support, 0, sizeof(struct sieve_support));
7841
+ yylineno = 1; /* reset line number */
7842
+ s->cmds = sieve_parse(s, script);
7845
+ free_tree(s->cmds);
7848
+ res = SIEVE_PARSE_ERROR;
7855
+static void free_imapflags(sieve_imapflags_t *imapflags)
7857
+ while (imapflags->nflags)
7858
+ free(imapflags->flag[--imapflags->nflags]);
7859
+ free(imapflags->flag);
7861
+ imapflags->flag = NULL;
7864
+int sieve_script_free(sieve_script_t **s)
7868
+ free_tree((*s)->cmds);
7876
+#define GROW_AMOUNT 100
7878
+static void add_header(sieve_interp_t *i, int isenv, char *header,
7879
+ void *message_context, char **out,
7880
+ int *outlen, int *outalloc)
7884
+ /* get header value */
7886
+ i->getenvelope(message_context, header, &h);
7888
+ i->getheader(message_context, header, &h);
7893
+ addlen = strlen(h[0]) + 1;
7895
+ /* realloc if necessary */
7896
+ if ( (*outlen) + addlen >= *outalloc)
7898
+ *outalloc = (*outlen) + addlen + GROW_AMOUNT;
7899
+ *out = xrealloc(*out, *outalloc);
7902
+ /* add header value */
7903
+ strcat(*out,h[0]);
7905
+ *outlen += addlen;
7908
+static int fillin_headers(sieve_interp_t *i, const char *msg,
7909
+ void *message_context, char **out, int *outlen)
7911
+ int allocsize = GROW_AMOUNT;
7915
+ *out = xmalloc(GROW_AMOUNT);
7919
+ if (msg == NULL) return SIEVE_OK;
7921
+ /* construct the message */
7924
+ /* expand variables */
7925
+ if (!strncasecmp(c, "$from$", 6)) {
7926
+ add_header(i, 0 ,"From", message_context, out, outlen, &allocsize);
7929
+ else if (!strncasecmp(c, "$env-from$", 10)) {
7930
+ add_header(i, 1, "From", message_context, out, outlen, &allocsize);
7933
+ else if (!strncasecmp(c, "$subject$", 9)) {
7934
+ add_header(i, 0, "Subject", message_context, out, outlen, &allocsize);
7937
+ /* XXX need to do $text$ variables */
7939
+ /* find length of plaintext up to next potential variable */
7940
+ n = strcspn(c+1, "$") + 1; /* skip opening '$' */
7941
+ /* realloc if necessary */
7942
+ if ( (*outlen) + n+1 >= allocsize) {
7943
+ allocsize = (*outlen) + n+1 + GROW_AMOUNT;
7944
+ *out = xrealloc(*out, allocsize);
7946
+ /* copy the plaintext */
7947
+ strncat(*out, c, n);
7948
+ (*out)[*outlen+n]='\0';
7957
+static int sieve_addflag(sieve_imapflags_t *imapflags, const char *flag)
7960
+ /* search for flag already in list */
7961
+ for (n = 0; n < imapflags->nflags; n++) {
7962
+ if (!strcmp(imapflags->flag[n], flag))
7966
+ /* add flag to list, iff not in list */
7967
+ if (n == imapflags->nflags) {
7968
+ imapflags->nflags++;
7970
+ (char **) xrealloc((char *)imapflags->flag,
7971
+ imapflags->nflags*sizeof(char *));
7972
+ imapflags->flag[imapflags->nflags-1] = xstrdup(flag);
7978
+static int sieve_removeflag(sieve_imapflags_t *imapflags, const char *flag)
7981
+ /* search for flag already in list */
7982
+ for (n = 0; n < imapflags->nflags; n++) {
7983
+ if (!strcmp(imapflags->flag[n], flag))
7987
+ /* remove flag from list, iff in list */
7988
+ if (n < imapflags->nflags)
7990
+ free(imapflags->flag[n]);
7991
+ imapflags->nflags--;
7993
+ for (; n < imapflags->nflags; n++)
7994
+ imapflags->flag[n] = imapflags->flag[n+1];
7996
+ if (imapflags->nflags)
7997
+ {imapflags->flag =
7998
+ (char **) xrealloc((char *)imapflags->flag,
7999
+ imapflags->nflags*sizeof(char *));}
8001
+ {free(imapflags->flag);
8002
+ imapflags->flag=NULL;}
8008
+static int send_notify_callback(sieve_interp_t *interp, void *message_context,
8009
+ void * script_context, notify_list_t *notify,
8010
+ char *actions_string, const char **errmsg)
8012
+ sieve_notify_context_t nc;
8013
+ char *out_msg, *build_msg;
8017
+ assert(notify->isactive);
8019
+ if (!notify->method || !notify->options ||
8020
+ !notify->priority || !notify->message) {
8021
+ return SIEVE_RUN_ERROR;
8024
+ nc.method = notify->method;
8025
+ nc.options = notify->options ? notify->options : NULL;
8026
+ nc.priority = notify->priority;
8028
+ fillin_headers(interp, notify->message, message_context,
8029
+ &out_msg, &out_msglen);
8031
+ build_msg = xmalloc(out_msglen + strlen(actions_string) + 30);
8033
+ strcpy(build_msg, out_msg);
8034
+ strcat(build_msg, "\n\n");
8035
+ strcat(build_msg, actions_string);
8037
+ nc.message = build_msg;
8041
+ ret = interp->notify(&nc,
8042
+ interp->interp_context,
8052
+static char *action_to_string(action_t action)
8056
+ case ACTION_REJECT: return "Reject";
8057
+ case ACTION_FILEINTO: return "Fileinto";
8058
+ case ACTION_KEEP: return "Keep";
8059
+ case ACTION_REDIRECT: return "Redirect";
8060
+ case ACTION_DISCARD: return "Discard";
8061
+ case ACTION_VACATION: return "Vacation";
8062
+ case ACTION_SETFLAG: return "Setflag";
8063
+ case ACTION_ADDFLAG: return "Addflag";
8064
+ case ACTION_REMOVEFLAG: return "Removeflag";
8065
+ case ACTION_MARK: return "Mark";
8066
+ case ACTION_UNMARK: return "Unmark";
8067
+ case ACTION_NOTIFY: return "Notify";
8068
+ case ACTION_DENOTIFY: return "Denotify";
8069
+ default: return "Unknown";
8075
+static char *sieve_errstr(int code)
8079
+ case SIEVE_FAIL: return "Generic Error";
8080
+ case SIEVE_NOT_FINALIZED: return "Sieve not finalized";
8081
+ case SIEVE_PARSE_ERROR: return "Parse error";
8082
+ case SIEVE_RUN_ERROR: return "Run error";
8083
+ case SIEVE_INTERNAL_ERROR: return "Internal Error";
8084
+ case SIEVE_NOMEM: return "No memory";
8085
+ default: return "Unknown error";
8091
+#define HASHSIZE 16
8093
+static int makehash(unsigned char hash[HASHSIZE],
8094
+ const char *s1, const char *s2)
8096
+ struct md5_context ctx;
8099
+ md5_update(&ctx, s1, strlen(s1));
8100
+ md5_update(&ctx, s2, strlen(s2));
8101
+ md5_final(&ctx, hash);
8107
+/******************************bytecode functions*****************************
8108
+ *****************************************************************************/
8110
+/* Load a compiled script */
8111
+int sieve_script_load(const char *fname, sieve_bytecode_t **ret)
8114
+ sieve_bytecode_t *r;
8117
+ if (!fname || !ret) return SIEVE_FAIL;
8119
+ fd = open(fname, O_RDONLY);
8121
+ if (errno != ENOENT)
8122
+ i_error("IOERROR: can not open sieve script %s: %m", fname);
8123
+ return SIEVE_FAIL;
8126
+ if (fstat(fd, &sbuf) == -1) {
8127
+ i_error("IOERROR: fstating sieve script %s: %m", fname);
8129
+ return SIEVE_FAIL;
8132
+ r = (sieve_bytecode_t *) xzmalloc(sizeof(sieve_bytecode_t));
8136
+ map_refresh(fd, 1, &r->data, &r->len, sbuf.st_size, fname, "sievescript");
8138
+ if ((r->len < (BYTECODE_MAGIC_LEN + 2*sizeof(bytecode_input_t))) ||
8139
+ memcmp(r->data, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN)) {
8140
+ i_error("IOERROR: not a sieve bytecode file %s", fname);
8141
+ sieve_script_unload(&r);
8142
+ return SIEVE_FAIL;
8151
+int sieve_script_unload(sieve_bytecode_t **s)
8154
+ map_free(&((*s)->data), &((*s)->len));
8159
+ /*i added this else, i'm not sure why, but this function always returned SIEVE_FAIL*/
8161
+ return SIEVE_FAIL;
8166
+#define ACTIONS_STRING_LEN 4096
8168
+static int do_sieve_error(int ret,
8169
+ sieve_interp_t *interp,
8170
+ void *script_context,
8171
+ void *message_context,
8172
+ sieve_imapflags_t * imapflags,
8173
+ action_list_t *actions,
8174
+ notify_list_t *notify_list,
8175
+ /* notify_action_t *notify_action,*/
8177
+ int implicit_keep,
8178
+ char *actions_string,
8179
+ const char *errmsg
8182
+ if (ret != SIEVE_OK) {
8183
+ if (lastaction == -1) /* we never executed an action */
8184
+ snprintf(actions_string+strlen(actions_string),
8185
+ ACTIONS_STRING_LEN-strlen(actions_string),
8186
+ "script execution failed: %s\n",
8187
+ errmsg ? errmsg : sieve_errstr(ret));
8189
+ snprintf(actions_string+strlen(actions_string),
8190
+ ACTIONS_STRING_LEN-strlen(actions_string),
8191
+ "%s action failed: %s\n",
8192
+ action_to_string(lastaction),
8193
+ errmsg ? errmsg : sieve_errstr(ret));
8197
+ /* Process notify actions */
8198
+ if (interp->notify && notify_list)
8200
+ notify_list_t *n = notify_list;
8201
+ int notify_ret = SIEVE_OK;
8207
+ lastaction = ACTION_NOTIFY;
8208
+ notify_ret = send_notify_callback(interp, message_context,
8210
+ actions_string, &errmsg);
8211
+ ret |= notify_ret;
8216
+ if (notify_list) free_notify_list(notify_list);
8217
+ notify_list = NULL; /* don't try any notifications again */
8220
+ if (notify_ret != SIEVE_OK)
8221
+ return do_sieve_error(ret, interp, script_context, message_context,
8222
+ imapflags, actions, notify_list, lastaction,
8223
+ implicit_keep, actions_string, errmsg);
8227
+ if ((ret != SIEVE_OK) && interp->err) {
8229
+ if (lastaction == -1) /* we never executed an action */
8230
+ sprintf(buf, "%s", errmsg ? errmsg : sieve_errstr(ret));
8232
+ sprintf(buf, "%s: %s", action_to_string(lastaction),
8233
+ errmsg ? errmsg : sieve_errstr(ret));
8235
+ ret |= interp->execute_err(buf, interp->interp_context,
8236
+ script_context, message_context);
8239
+ if (implicit_keep) {
8240
+ sieve_keep_context_t keep_context;
8242
+ keep_context.imapflags = imapflags;
8244
+ lastaction = ACTION_KEEP;
8245
+ keep_ret = interp->keep(&keep_context, interp->interp_context,
8246
+ script_context, message_context, &errmsg);
8248
+ if (keep_ret == SIEVE_OK)
8249
+ snprintf(actions_string+strlen(actions_string),
8250
+ sizeof(actions_string)-strlen(actions_string),
8253
+ implicit_keep = 0; /* don't try an implicit keep again */
8254
+ return do_sieve_error(ret, interp, script_context, message_context,
8255
+ imapflags, actions, notify_list, lastaction,
8256
+ implicit_keep, actions_string, errmsg);
8261
+ free_action_list(actions);
8267
+static int do_action_list(sieve_interp_t *interp,
8268
+ void *script_context,
8269
+ void *message_context,
8270
+ sieve_imapflags_t *imapflags,
8271
+ action_list_t *actions,
8272
+ notify_list_t *notify_list,
8273
+ /* notify_action_t *notify_action,*/
8274
+ char *actions_string,
8275
+ const char *errmsg)
8278
+ action_t lastaction = -1;
8280
+ int implicit_keep = 0;
8282
+ strcpy(actions_string,"Action(s) taken:\n");
8284
+ /* now perform actions attached to m */
8286
+ implicit_keep = 1;
8287
+ while (a != NULL) {
8288
+ lastaction = a->a;
8291
+ case ACTION_REJECT:
8292
+ implicit_keep = 0;
8293
+ if (!interp->reject)
8294
+ return SIEVE_INTERNAL_ERROR;
8295
+ ret = interp->reject(&a->u.rej,
8296
+ interp->interp_context,
8301
+ if (ret == SIEVE_OK)
8302
+ snprintf(actions_string+strlen(actions_string),
8303
+ sizeof(actions_string)-strlen(actions_string),
8304
+ "Rejected with: %s\n", a->u.rej.msg);
8307
+ case ACTION_FILEINTO:
8308
+ implicit_keep = 0;
8309
+ if (!interp->fileinto)
8310
+ return SIEVE_INTERNAL_ERROR;
8311
+ ret = interp->fileinto(&a->u.fil,
8312
+ interp->interp_context,
8317
+ if (ret == SIEVE_OK)
8318
+ snprintf(actions_string+strlen(actions_string),
8319
+ sizeof(actions_string)-strlen(actions_string),
8320
+ "Filed into: %s\n",a->u.fil.mailbox);
8323
+ implicit_keep = 0;
8324
+ if (!interp->keep)
8325
+ return SIEVE_INTERNAL_ERROR;
8326
+ ret = interp->keep(&a->u.keep,
8327
+ interp->interp_context,
8331
+ if (ret == SIEVE_OK)
8332
+ snprintf(actions_string+strlen(actions_string),
8333
+ sizeof(actions_string)-strlen(actions_string),
8336
+ case ACTION_REDIRECT:
8337
+ implicit_keep = 0;
8338
+ if (!interp->redirect)
8339
+ return SIEVE_INTERNAL_ERROR;
8340
+ ret = interp->redirect(&a->u.red,
8341
+ interp->interp_context,
8345
+ if (ret == SIEVE_OK)
8346
+ snprintf(actions_string+strlen(actions_string),
8347
+ sizeof(actions_string)-strlen(actions_string),
8348
+ "Redirected to %s\n", a->u.red.addr);
8350
+ case ACTION_DISCARD:
8351
+ implicit_keep = 0;
8352
+ if (interp->discard) /* discard is optional */
8353
+ ret = interp->discard(NULL, interp->interp_context,
8357
+ if (ret == SIEVE_OK)
8358
+ snprintf(actions_string+strlen(actions_string),
8359
+ sizeof(actions_string)-strlen(actions_string),
8363
+ case ACTION_VACATION:
8365
+ unsigned char hash[HASHSIZE];
8367
+ if (!interp->vacation)
8368
+ return SIEVE_INTERNAL_ERROR;
8370
+ /* first, let's figure out if we should respond to this */
8371
+ ret = makehash(hash, a->u.vac.send.addr,
8372
+ a->u.vac.send.msg);
8374
+ if (ret == SIEVE_OK) {
8375
+ a->u.vac.autoresp.hash = hash;
8376
+ a->u.vac.autoresp.len = HASHSIZE;
8377
+ ret = interp->vacation->autorespond(&a->u.vac.autoresp,
8378
+ interp->interp_context,
8383
+ if (ret == SIEVE_OK) {
8384
+ /* send the response */
8385
+ ret = interp->vacation->send_response(&a->u.vac.send,
8386
+ interp->interp_context,
8391
+ if (ret == SIEVE_OK)
8392
+ snprintf(actions_string+strlen(actions_string),
8393
+ sizeof(actions_string)-strlen(actions_string),
8394
+ "Sent vacation reply\n");
8396
+ } else if (ret == SIEVE_DONE) {
8397
+ snprintf(actions_string+strlen(actions_string),
8398
+ sizeof(actions_string)-strlen(actions_string),
8399
+ "Vacation reply suppressed\n");
8408
+ case ACTION_SETFLAG:
8409
+ free_imapflags(imapflags);
8410
+ ret = sieve_addflag(imapflags, a->u.fla.flag);
8412
+ case ACTION_ADDFLAG:
8413
+ ret = sieve_addflag(imapflags, a->u.fla.flag);
8415
+ case ACTION_REMOVEFLAG:
8416
+ ret = sieve_removeflag(imapflags, a->u.fla.flag);
8420
+ int n = interp->markflags->nflags;
8423
+ while (n && ret == SIEVE_OK) {
8424
+ ret = sieve_addflag(imapflags,
8425
+ interp->markflags->flag[--n]);
8429
+ case ACTION_UNMARK:
8432
+ int n = interp->markflags->nflags;
8434
+ while (n && ret == SIEVE_OK) {
8435
+ ret = sieve_removeflag(imapflags,
8436
+ interp->markflags->flag[--n]);
8445
+ ret = SIEVE_INTERNAL_ERROR;
8450
+ if (ret != SIEVE_OK) {
8451
+ /* uh oh! better bail! */
8456
+ return do_sieve_error(ret, interp, script_context, message_context,
8457
+ imapflags, actions, notify_list, lastaction,
8458
+ implicit_keep, actions_string, errmsg);
8462
+int sieve_execute_bytecode(sieve_bytecode_t *bc, sieve_interp_t *interp,
8463
+ void *script_context, void *message_context)
8465
+ action_list_t *actions = NULL;
8466
+ notify_list_t *notify_list = NULL;
8467
+ /* notify_action_t *notify_action;*/
8468
+ action_t lastaction = -1;
8470
+ char actions_string[ACTIONS_STRING_LEN] = "";
8471
+ const char *errmsg = NULL;
8472
+ sieve_imapflags_t imapflags;
8474
+ if (!interp) return SIEVE_FAIL;
8476
+ imapflags.flag = NULL;
8477
+ imapflags.nflags = 0;
8479
+ if (interp->notify)
8481
+ notify_list = new_notify_list();
8482
+ if (notify_list == NULL)
8484
+ ret = SIEVE_NOMEM;
8485
+ return do_sieve_error(ret, interp, script_context,
8486
+ message_context, &imapflags,
8487
+ actions, notify_list, lastaction, 0,
8488
+ actions_string, errmsg);
8492
+ actions = new_action_list();
8493
+ if (actions == NULL)
8495
+ ret = SIEVE_NOMEM;
8496
+ return do_sieve_error(ret, interp, script_context,
8497
+ message_context, &imapflags,
8498
+ actions, notify_list, lastaction, 0,
8499
+ actions_string, errmsg);
8502
+ if (sieve_eval_bc(interp, bc->data, bc->len, message_context,
8503
+ &imapflags, actions, notify_list, &errmsg) < 0)
8505
+ ret = SIEVE_RUN_ERROR;
8506
+ return do_sieve_error(ret, interp, script_context,
8507
+ message_context, &imapflags,
8508
+ actions, notify_list, lastaction, 0,
8509
+ actions_string, errmsg);
8512
+ return do_action_list(interp, script_context, message_context,
8513
+ &imapflags, actions, notify_list, actions_string,
8516
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/script.h
8517
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
8518
+++ b/src/lib-sieve/cmu/libsieve/script.h Sun May 04 16:00:59 2008 +0200
8520
+/* script.h -- script definition
8521
+ * Larry Greenfield
8524
+/***********************************************************
8525
+ Copyright 1999 by Carnegie Mellon University
8527
+ All Rights Reserved
8529
+Permission to use, copy, modify, and distribute this software and its
8530
+documentation for any purpose and without fee is hereby granted,
8531
+provided that the above copyright notice appear in all copies and that
8532
+both that copyright notice and this permission notice appear in
8533
+supporting documentation, and that the name of Carnegie Mellon
8534
+University not be used in advertising or publicity pertaining to
8535
+distribution of the software without specific, written prior
8538
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
8539
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
8540
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
8541
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
8542
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
8543
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
8544
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
8545
+******************************************************************/
8547
+#ifndef SIEVE_SCRIPT_H
8548
+#define SIEVE_SCRIPT_H
8550
+#include "sieve_interface.h"
8551
+#include "interp.h"
8554
+#define ADDRERR_SIZE 500
8556
+struct sieve_script {
8557
+ sieve_interp_t interp;
8559
+ /* was a "require" done for these? */
8560
+ struct sieve_support {
8565
+ int imapflags : 1;
8568
+ int subaddress : 1;
8569
+ int relational : 1;
8570
+ int i_ascii_numeric: 1;
8573
+ void *script_context;
8574
+ commandlist_t *cmds;
8579
+struct sieve_bytecode
8581
+ sieve_interp_t *interp;
8582
+ void *script_context;
8585
+ unsigned long len;
8589
+/* generated by the yacc script */
8590
+commandlist_t *sieve_parse(sieve_script_t *script, FILE *f);
8591
+int script_require(sieve_script_t *s, char *req);
8594
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sieve-lex.l
8595
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
8596
+++ b/src/lib-sieve/cmu/libsieve/sieve-lex.l Sun May 04 16:00:59 2008 +0200
8599
+/* sieve.l -- sieve lexer
8600
+ * Larry Greenfield
8603
+/***********************************************************
8604
+ Copyright 1999 by Carnegie Mellon University
8606
+ All Rights Reserved
8608
+Permission to use, copy, modify, and distribute this software and its
8609
+documentation for any purpose and without fee is hereby granted,
8610
+provided that the above copyright notice appear in all copies and that
8611
+both that copyright notice and this permission notice appear in
8612
+supporting documentation, and that the name of Carnegie Mellon
8613
+University not be used in advertising or publicity pertaining to
8614
+distribution of the software without specific, written prior
8617
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
8618
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
8619
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
8620
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
8621
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
8622
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
8623
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
8624
+******************************************************************/
8626
+#ifdef HAVE_CONFIG_H
8627
+#include <config.h>
8630
+#include <string.h> /* for strdup */
8631
+#include "xmalloc.h"
8636
+#define yylval sievelval
8637
+#define yylex sievelex
8638
+#define yyerror sieveerror
8640
+static int tonum(char *c);
8641
+static char *chkstr(char *);
8642
+static char *mlbuf;
8643
+static int mlbufsz, mlcur;
8644
+extern int yyerror(char *);
8652
+ident [a-zA-Z_][a-zA-Z_0-9]*
8659
+<MULTILINE>^\.{CRLF} { BEGIN INITIAL;
8660
+ if (mlbuf) mlbuf[mlcur] = '\0';
8661
+ yylval.sval = chkstr(mlbuf); return STRING; }
8662
+<MULTILINE>^\.\. { /* dot stuffing! we want one . */ yyless(1); }
8663
+<MULTILINE>(.|\n) { if (mlcur == mlbufsz)
8664
+ mlbuf = xrealloc(mlbuf, 1 + (mlbufsz+=1024));
8665
+ mlbuf[mlcur++] = yytext[0]; }
8666
+<MULTILINE><<EOF>> { yyerror("unexpected end of file in string");
8668
+<QSTRING>\" { BEGIN INITIAL;
8669
+ if (mlbuf) mlbuf[mlcur] = '\0';
8670
+ yylval.sval = chkstr(mlbuf); return STRING; }
8671
+<QSTRING>\\. { if (mlcur == mlbufsz)
8672
+ mlbuf = xrealloc(mlbuf, 1 + (mlbufsz+=1024));
8673
+ mlbuf[mlcur++] = yytext[1]; }
8674
+<QSTRING>(.|\n) { if (mlcur == mlbufsz)
8675
+ mlbuf = xrealloc(mlbuf, 1 + (mlbufsz+=1024));
8676
+ mlbuf[mlcur++] = yytext[0]; }
8677
+<INITIAL>text:{ws}?(#.*)?{CRLF} { BEGIN MULTILINE;
8678
+ mlcur = 0; mlbufsz = 0; mlbuf = NULL; }
8679
+<INITIAL>\" { BEGIN QSTRING;
8680
+ mlcur = 0; mlbufsz = 0; mlbuf = NULL; }
8681
+<INITIAL>[0-9]+[KMG]? { yylval.nval = tonum(yytext); return NUMBER; }
8682
+<INITIAL>if return IF;
8683
+<INITIAL>elsif return ELSIF;
8684
+<INITIAL>else return ELSE;
8685
+<INITIAL>anyof return ANYOF;
8686
+<INITIAL>allof return ALLOF;
8687
+<INITIAL>exists return EXISTS;
8688
+<INITIAL>false return SFALSE;
8689
+<INITIAL>true return STRUE;
8690
+<INITIAL>address return ADDRESS;
8691
+<INITIAL>envelope return ENVELOPE;
8692
+<INITIAL>header return HEADER;
8693
+<INITIAL>not return NOT;
8694
+<INITIAL>size return SIZE;
8695
+<INITIAL>reject return REJCT;
8696
+<INITIAL>fileinto return FILEINTO;
8697
+<INITIAL>redirect return REDIRECT;
8698
+<INITIAL>keep return KEEP;
8699
+<INITIAL>require return REQUIRE;
8700
+<INITIAL>stop return STOP;
8701
+<INITIAL>discard return DISCARD;
8702
+<INITIAL>setflag return SETFLAG;
8703
+<INITIAL>addflag return ADDFLAG;
8704
+<INITIAL>removeflag return REMOVEFLAG;
8705
+<INITIAL>mark return MARK;
8706
+<INITIAL>unmark return UNMARK;
8707
+<INITIAL>notify return NOTIFY;
8708
+<INITIAL>denotify return DENOTIFY;
8709
+<INITIAL>:id return ID;
8710
+<INITIAL>:method return METHOD;
8711
+<INITIAL>:options return OPTIONS;
8712
+<INITIAL>:low return LOW;
8713
+<INITIAL>:normal return NORMAL;
8714
+<INITIAL>:high return HIGH;
8715
+<INITIAL>:message return MESSAGE;
8716
+<INITIAL>vacation return VACATION;
8717
+<INITIAL>:days return DAYS;
8718
+<INITIAL>:addresses return ADDRESSES;
8719
+<INITIAL>:subject return SUBJECT;
8720
+<INITIAL>:mime return MIME;
8721
+<INITIAL>:comparator return COMPARATOR;
8722
+<INITIAL>:is return IS;
8723
+<INITIAL>:contains return CONTAINS;
8724
+<INITIAL>:matches return MATCHES;
8725
+<INITIAL>:regex return REGEX;
8726
+<INITIAL>:count return COUNT;
8727
+<INITIAL>:value return VALUE;
8728
+<INITIAL>:over return OVER;
8729
+<INITIAL>:under return UNDER;
8730
+<INITIAL>:all return ALL;
8731
+<INITIAL>:localpart return LOCALPART;
8732
+<INITIAL>:domain return DOMAIN;
8733
+<INITIAL>:user return USER;
8734
+<INITIAL>:detail return DETAIL;
8735
+<INITIAL>[ \t\n\r] ; /* ignore whitespace */
8736
+<INITIAL>#.* ; /* ignore hash comments */
8737
+<INITIAL>"/*"([^\*]|\*[^\/])*\*?"*/" ; /* ignore bracket comments */
8738
+. return yytext[0];
8742
+static int tonum(char *c)
8744
+ int val = atoi(c);
8745
+ switch (c[strlen(c)-1]) {
8746
+ case 'K': val *= (1 << 10); break;
8747
+ case 'M': val *= (1 << 20); break;
8748
+ case 'G': val *= (1 << 30); break;
8754
+/* convert NULL strings to "" */
8755
+static char *chkstr(char *str)
8757
+ if (!str) return xstrdup("");
8760
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sieve.y
8761
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
8762
+++ b/src/lib-sieve/cmu/libsieve/sieve.y Sun May 04 16:00:59 2008 +0200
8765
+/* sieve.y -- sieve parser
8766
+ * Larry Greenfield
8769
+/***********************************************************
8770
+ Copyright 1999 by Carnegie Mellon University
8772
+ All Rights Reserved
8774
+Permission to use, copy, modify, and distribute this software and its
8775
+documentation for any purpose and without fee is hereby granted,
8776
+provided that the above copyright notice appear in all copies and that
8777
+both that copyright notice and this permission notice appear in
8778
+supporting documentation, and that the name of Carnegie Mellon
8779
+University not be used in advertising or publicity pertaining to
8780
+distribution of the software without specific, written prior
8783
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
8784
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
8785
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
8786
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
8787
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
8788
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
8789
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
8790
+******************************************************************/
8792
+#ifdef HAVE_CONFIG_H
8793
+#include <config.h>
8796
+#include <stdlib.h>
8797
+#include <assert.h>
8798
+#include <string.h>
8800
+#include "xmalloc.h"
8801
+#include "comparator.h"
8802
+#include "interp.h"
8803
+#include "script.h"
8806
+#include "imparse.h"
8807
+#include "libconfig.h"
8810
+ extern int addrparse(void);
8814
+ stringlist_t *addresses;
8835
+ stringlist_t *options;
8847
+static commandlist_t *ret;
8848
+static sieve_script_t *parse_script;
8849
+static int check_reqs(stringlist_t *sl);
8850
+static test_t *build_address(int t, struct aetags *ae,
8851
+ stringlist_t *sl, stringlist_t *pl);
8852
+static test_t *build_header(int t, struct htags *h,
8853
+ stringlist_t *sl, stringlist_t *pl);
8854
+static commandlist_t *build_vacation(int t, struct vtags *h, char *s);
8855
+static commandlist_t *build_notify(int t, struct ntags *n);
8856
+static commandlist_t *build_denotify(int t, struct dtags *n);
8857
+static struct aetags *new_aetags(void);
8858
+static struct aetags *canon_aetags(struct aetags *ae);
8859
+static void free_aetags(struct aetags *ae);
8860
+static struct htags *new_htags(void);
8861
+static struct htags *canon_htags(struct htags *h);
8862
+static void free_htags(struct htags *h);
8863
+static struct vtags *new_vtags(void);
8864
+static struct vtags *canon_vtags(struct vtags *v);
8865
+static void free_vtags(struct vtags *v);
8866
+static struct ntags *new_ntags(void);
8867
+static struct ntags *canon_ntags(struct ntags *n);
8868
+static void free_ntags(struct ntags *n);
8869
+static struct dtags *new_dtags(void);
8870
+static struct dtags *canon_dtags(struct dtags *d);
8871
+static void free_dtags(struct dtags *d);
8873
+static int verify_stringlist(stringlist_t *sl, int (*verify)(char *));
8874
+static int verify_mailbox(char *s);
8875
+static int verify_address(char *s);
8876
+static int verify_header(char *s);
8877
+static int verify_addrheader(char *s);
8878
+static int verify_envelope(char *s);
8879
+static int verify_flag(char *s);
8880
+static int verify_relat(char *s);
8881
+#ifdef ENABLE_REGEX
8882
+static int verify_regex(char *s, int cflags);
8883
+static int verify_regexs(stringlist_t *sl, char *comp);
8885
+static int verify_utf8(char *s);
8887
+int yyerror(char *msg);
8888
+extern int yylex(void);
8889
+extern void yyrestart(FILE *f);
8891
+#define YYERROR_VERBOSE /* i want better error messages! */
8899
+ testlist_t *testl;
8900
+ commandlist_t *cl;
8901
+ struct vtags *vtag;
8902
+ struct aetags *aetag;
8903
+ struct htags *htag;
8904
+ struct ntags *ntag;
8905
+ struct dtags *dtag;
8908
+%token <nval> NUMBER
8909
+%token <sval> STRING
8910
+%token IF ELSIF ELSE
8911
+%token REJCT FILEINTO REDIRECT KEEP STOP DISCARD VACATION REQUIRE
8912
+%token SETFLAG ADDFLAG REMOVEFLAG MARK UNMARK
8913
+%token NOTIFY DENOTIFY
8914
+%token ANYOF ALLOF EXISTS SFALSE STRUE HEADER NOT SIZE ADDRESS ENVELOPE
8915
+%token COMPARATOR IS CONTAINS MATCHES REGEX COUNT VALUE OVER UNDER
8916
+%token GT GE LT LE EQ NE
8917
+%token ALL LOCALPART DOMAIN USER DETAIL
8918
+%token DAYS ADDRESSES SUBJECT MIME
8919
+%token METHOD ID OPTIONS LOW NORMAL HIGH ANY MESSAGE
8921
+%type <cl> commands command action elsif block
8922
+%type <sl> stringlist strings
8924
+%type <nval> comptag relcomp sizetag addrparttag addrorenv
8925
+%type <testl> testlist tests
8927
+%type <aetag> aetags
8931
+%type <nval> priority
8935
+start: reqs { ret = NULL; }
8936
+ | reqs commands { ret = $2; }
8943
+require: REQUIRE stringlist ';' { if (!check_reqs($2)) {
8944
+ yyerror("Unsupported features in require line");
8949
+commands: command { $$ = $1; }
8950
+ | command commands { $1->next = $2; $$ = $1; }
8953
+command: action ';' { $$ = $1; }
8954
+ | IF test block elsif { $$ = new_if($2, $3, $4); }
8955
+ | error ';' { $$ = new_command(STOP); }
8958
+elsif: /* empty */ { $$ = NULL; }
8959
+ | ELSIF test block elsif { $$ = new_if($2, $3, $4); }
8960
+ | ELSE block { $$ = $2; }
8963
+action: REJCT STRING { if (!parse_script->support.reject) {
8964
+ yyerror("reject require missing");
8967
+ if (!verify_utf8($2)) {
8968
+ YYERROR; /* vu should call yyerror() */
8970
+ $$ = new_command(REJCT);
8972
+ | FILEINTO STRING { if (!parse_script->support.fileinto) {
8973
+ yyerror("fileinto require missing");
8976
+ if (!verify_mailbox($2)) {
8977
+ YYERROR; /* vm should call yyerror() */
8979
+ $$ = new_command(FILEINTO);
8981
+ | REDIRECT STRING { $$ = new_command(REDIRECT);
8982
+ if (!verify_address($2)) {
8983
+ YYERROR; /* va should call yyerror() */
8986
+ | KEEP { $$ = new_command(KEEP); }
8987
+ | STOP { $$ = new_command(STOP); }
8988
+ | DISCARD { $$ = new_command(DISCARD); }
8989
+ | VACATION vtags STRING { if (!parse_script->support.vacation) {
8990
+ yyerror("vacation require missing");
8993
+ if (($2->mime == -1) && !verify_utf8($3)) {
8994
+ YYERROR; /* vu should call yyerror() */
8996
+ $$ = build_vacation(VACATION,
8997
+ canon_vtags($2), $3); }
8998
+ | SETFLAG stringlist { if (!parse_script->support.imapflags) {
8999
+ yyerror("imapflags require missing");
9002
+ if (!verify_stringlist($2, verify_flag)) {
9003
+ YYERROR; /* vf should call yyerror() */
9005
+ $$ = new_command(SETFLAG);
9007
+ | ADDFLAG stringlist { if (!parse_script->support.imapflags) {
9008
+ yyerror("imapflags require missing");
9011
+ if (!verify_stringlist($2, verify_flag)) {
9012
+ YYERROR; /* vf should call yyerror() */
9014
+ $$ = new_command(ADDFLAG);
9016
+ | REMOVEFLAG stringlist { if (!parse_script->support.imapflags) {
9017
+ yyerror("imapflags require missing");
9020
+ if (!verify_stringlist($2, verify_flag)) {
9021
+ YYERROR; /* vf should call yyerror() */
9023
+ $$ = new_command(REMOVEFLAG);
9025
+ | MARK { if (!parse_script->support.imapflags) {
9026
+ yyerror("imapflags require missing");
9029
+ $$ = new_command(MARK); }
9030
+ | UNMARK { if (!parse_script->support.imapflags) {
9031
+ yyerror("imapflags require missing");
9034
+ $$ = new_command(UNMARK); }
9036
+ | NOTIFY ntags { if (!parse_script->support.notify) {
9037
+ yyerror("notify require missing");
9038
+ $$ = new_command(NOTIFY);
9041
+ $$ = build_notify(NOTIFY,
9044
+ | DENOTIFY dtags { if (!parse_script->support.notify) {
9045
+ yyerror("notify require missing");
9046
+ $$ = new_command(DENOTIFY);
9049
+ $$ = build_denotify(DENOTIFY, canon_dtags($2));
9051
+ yyerror("unable to find a compatible comparator");
9055
+ntags: /* empty */ { $$ = new_ntags(); }
9056
+ | ntags ID STRING { if ($$->id != NULL) {
9057
+ yyerror("duplicate :method"); YYERROR; }
9058
+ else { $$->id = $3; } }
9059
+ | ntags METHOD STRING { if ($$->method != NULL) {
9060
+ yyerror("duplicate :method"); YYERROR; }
9061
+ else { $$->method = $3; } }
9062
+ | ntags OPTIONS stringlist { if ($$->options != NULL) {
9063
+ yyerror("duplicate :options"); YYERROR; }
9064
+ else { $$->options = $3; } }
9065
+ | ntags priority { if ($$->priority != -1) {
9066
+ yyerror("duplicate :priority"); YYERROR; }
9067
+ else { $$->priority = $2; } }
9068
+ | ntags MESSAGE STRING { if ($$->message != NULL) {
9069
+ yyerror("duplicate :message"); YYERROR; }
9070
+ else { $$->message = $3; } }
9073
+dtags: /* empty */ { $$ = new_dtags(); }
9074
+ | dtags priority { if ($$->priority != -1) {
9075
+ yyerror("duplicate priority level"); YYERROR; }
9076
+ else { $$->priority = $2; } }
9077
+ | dtags comptag STRING { if ($$->comptag != -1)
9079
+ yyerror("duplicate comparator type tag"); YYERROR;
9082
+#ifdef ENABLE_REGEX
9083
+ if ($$->comptag == REGEX)
9085
+ int cflags = REG_EXTENDED |
9086
+ REG_NOSUB | REG_ICASE;
9087
+ if (!verify_regex($3, cflags)) { YYERROR; }
9092
+ | dtags relcomp STRING { $$ = $1;
9093
+ if ($$->comptag != -1) {
9094
+ yyerror("duplicate comparator type tag"); YYERROR; }
9095
+ else { $$->comptag = $2;
9096
+ $$->relation = verify_relat($3);
9097
+ if ($$->relation==-1)
9098
+ {YYERROR; /*vr called yyerror()*/ }
9102
+priority: LOW { $$ = LOW; }
9103
+ | NORMAL { $$ = NORMAL; }
9104
+ | HIGH { $$ = HIGH; }
9107
+vtags: /* empty */ { $$ = new_vtags(); }
9108
+ | vtags DAYS NUMBER { if ($$->days != -1) {
9109
+ yyerror("duplicate :days"); YYERROR; }
9110
+ else { $$->days = $3; } }
9111
+ | vtags ADDRESSES stringlist { if ($$->addresses != NULL) {
9112
+ yyerror("duplicate :addresses");
9114
+ } else if (!verify_stringlist($3,
9115
+ verify_address)) {
9118
+ $$->addresses = $3; } }
9119
+ | vtags SUBJECT STRING { if ($$->subject != NULL) {
9120
+ yyerror("duplicate :subject");
9122
+ } else if (!verify_utf8($3)) {
9123
+ YYERROR; /* vu should call yyerror() */
9124
+ } else { $$->subject = $3; } }
9125
+ | vtags MIME { if ($$->mime != -1) {
9126
+ yyerror("duplicate :mime");
9128
+ else { $$->mime = MIME; } }
9131
+stringlist: '[' strings ']' { $$ = $2; }
9132
+ | STRING { $$ = new_sl($1, NULL); }
9135
+strings: STRING { $$ = new_sl($1, NULL); }
9136
+ | STRING ',' strings { $$ = new_sl($1, $3); }
9139
+block: '{' commands '}' { $$ = $2; }
9140
+ | '{' '}' { $$ = NULL; }
9143
+test: ANYOF testlist { $$ = new_test(ANYOF); $$->u.tl = $2; }
9144
+ | ALLOF testlist { $$ = new_test(ALLOF); $$->u.tl = $2; }
9145
+ | EXISTS stringlist { $$ = new_test(EXISTS); $$->u.sl = $2; }
9146
+ | SFALSE { $$ = new_test(SFALSE); }
9147
+ | STRUE { $$ = new_test(STRUE); }
9148
+ | HEADER htags stringlist stringlist
9150
+ if (!verify_stringlist($3, verify_header)) {
9151
+ YYERROR; /* vh should call yyerror() */
9153
+ if (!verify_stringlist($4, verify_utf8)) {
9154
+ YYERROR; /* vu should call yyerror() */
9157
+ $2 = canon_htags($2);
9158
+#ifdef ENABLE_REGEX
9159
+ if ($2->comptag == REGEX)
9161
+ if (!(verify_regexs($4, $2->comparator)))
9165
+ $$ = build_header(HEADER, $2, $3, $4);
9167
+ yyerror("unable to find a compatible comparator");
9172
+ | addrorenv aetags stringlist stringlist
9174
+ if (($1 == ADDRESS) &&
9175
+ !verify_stringlist($3, verify_addrheader))
9177
+ else if (($1 == ENVELOPE) &&
9178
+ !verify_stringlist($3, verify_envelope))
9180
+ $2 = canon_aetags($2);
9181
+#ifdef ENABLE_REGEX
9182
+ if ($2->comptag == REGEX)
9184
+ if (!( verify_regexs($4, $2->comparator)))
9188
+ $$ = build_address($1, $2, $3, $4);
9190
+ yyerror("unable to find a compatible comparator");
9194
+ | NOT test { $$ = new_test(NOT); $$->u.t = $2; }
9195
+ | SIZE sizetag NUMBER { $$ = new_test(SIZE); $$->u.sz.t = $2;
9196
+ $$->u.sz.n = $3; }
9197
+ | error { $$ = NULL; }
9200
+addrorenv: ADDRESS { $$ = ADDRESS; }
9201
+ | ENVELOPE {if (!parse_script->support.envelope)
9202
+ {yyerror("envelope require missing"); YYERROR;}
9203
+ else{$$ = ENVELOPE; }
9208
+aetags: /* empty */ { $$ = new_aetags(); }
9209
+ | aetags addrparttag { $$ = $1;
9210
+ if ($$->addrtag != -1) {
9211
+ yyerror("duplicate or conflicting address part tag");
9213
+ else { $$->addrtag = $2; } }
9214
+ | aetags comptag { $$ = $1;
9215
+ if ($$->comptag != -1) {
9216
+ yyerror("duplicate comparator type tag"); YYERROR; }
9217
+ else { $$->comptag = $2; } }
9218
+ | aetags relcomp STRING{ $$ = $1;
9219
+ if ($$->comptag != -1) {
9220
+ yyerror("duplicate comparator type tag"); YYERROR; }
9221
+ else { $$->comptag = $2;
9222
+ $$->relation = verify_relat($3);
9223
+ if ($$->relation==-1)
9224
+ {YYERROR; /*vr called yyerror()*/ }
9226
+ | aetags COMPARATOR STRING { $$ = $1;
9227
+ if ($$->comparator != NULL) {
9228
+ yyerror("duplicate comparator tag"); YYERROR; }
9229
+ else if (!strcmp($3, "i;ascii-numeric") &&
9230
+ !parse_script->support.i_ascii_numeric) {
9231
+ yyerror("comparator-i;ascii-numeric require missing");
9233
+ else { $$->comparator = $3; } }
9236
+htags: /* empty */ { $$ = new_htags(); }
9237
+ | htags comptag { $$ = $1;
9238
+ if ($$->comptag != -1) {
9239
+ yyerror("duplicate comparator type tag"); YYERROR; }
9240
+ else { $$->comptag = $2; } }
9241
+ | htags relcomp STRING { $$ = $1;
9242
+ if ($$->comptag != -1) {
9243
+ yyerror("duplicate comparator type tag"); YYERROR; }
9244
+ else { $$->comptag = $2;
9245
+ $$->relation = verify_relat($3);
9246
+ if ($$->relation==-1)
9247
+ {YYERROR; /*vr called yyerror()*/ }
9249
+ | htags COMPARATOR STRING { $$ = $1;
9250
+ if ($$->comparator != NULL) {
9251
+ yyerror("duplicate comparator tag"); YYERROR; }
9252
+ else if (!strcmp($3, "i;ascii-numeric") &&
9253
+ !parse_script->support.i_ascii_numeric) {
9254
+ yyerror("comparator-i;ascii-numeric require missing"); YYERROR; }
9256
+ $$->comparator = $3; } }
9260
+addrparttag: ALL { $$ = ALL; }
9261
+ | LOCALPART { $$ = LOCALPART; }
9262
+ | DOMAIN { $$ = DOMAIN; }
9263
+ | USER { if (!parse_script->support.subaddress) {
9264
+ yyerror("subaddress require missing");
9268
+ | DETAIL { if (!parse_script->support.subaddress) {
9269
+ yyerror("subaddress require missing");
9274
+comptag: IS { $$ = IS; }
9275
+ | CONTAINS { $$ = CONTAINS; }
9276
+ | MATCHES { $$ = MATCHES; }
9277
+ | REGEX { if (!parse_script->support.regex) {
9278
+ yyerror("regex require missing");
9284
+relcomp: COUNT { if (!parse_script->support.relational) {
9285
+ yyerror("relational require missing");
9289
+ | VALUE { if (!parse_script->support.relational) {
9290
+ yyerror("relational require missing");
9297
+sizetag: OVER { $$ = OVER; }
9298
+ | UNDER { $$ = UNDER; }
9301
+testlist: '(' tests ')' { $$ = $2; }
9304
+tests: test { $$ = new_testlist($1, NULL); }
9305
+ | test ',' tests { $$ = new_testlist($1, $3); }
9309
+commandlist_t *sieve_parse(sieve_script_t *script, FILE *f)
9313
+ parse_script = script;
9324
+int yyerror(char *msg)
9326
+ extern int yylineno;
9329
+ parse_script->err++;
9330
+ if (parse_script->interp.err) {
9331
+ ret = parse_script->interp.err(yylineno, msg,
9332
+ parse_script->interp.interp_context,
9333
+ parse_script->script_context);
9339
+static int check_reqs(stringlist_t *sl)
9344
+ while (sl != NULL) {
9348
+ i &= script_require(parse_script, s->s);
9350
+ if (s->s) free(s->s);
9356
+static test_t *build_address(int t, struct aetags *ae,
9357
+ stringlist_t *sl, stringlist_t *pl)
9359
+ test_t *ret = new_test(t); /* can be either ADDRESS or ENVELOPE */
9361
+ assert((t == ADDRESS) || (t == ENVELOPE));
9364
+ ret->u.ae.comptag = ae->comptag;
9365
+ ret->u.ae.relation=ae->relation;
9366
+ ret->u.ae.comparator=strdup(ae->comparator);
9367
+ ret->u.ae.sl = sl;
9368
+ ret->u.ae.pl = pl;
9369
+ ret->u.ae.addrpart = ae->addrtag;
9376
+static test_t *build_header(int t, struct htags *h,
9377
+ stringlist_t *sl, stringlist_t *pl)
9379
+ test_t *ret = new_test(t); /* can be HEADER */
9381
+ assert(t == HEADER);
9384
+ ret->u.h.comptag = h->comptag;
9385
+ ret->u.h.relation=h->relation;
9386
+ ret->u.h.comparator=strdup(h->comparator);
9394
+static commandlist_t *build_vacation(int t, struct vtags *v, char *reason)
9396
+ commandlist_t *ret = new_command(t);
9398
+ assert(t == VACATION);
9401
+ ret->u.v.subject = v->subject; v->subject = NULL;
9402
+ ret->u.v.days = v->days;
9403
+ ret->u.v.mime = v->mime;
9404
+ ret->u.v.addresses = v->addresses; v->addresses = NULL;
9406
+ ret->u.v.message = reason;
9411
+static commandlist_t *build_notify(int t, struct ntags *n)
9413
+ commandlist_t *ret = new_command(t);
9415
+ assert(t == NOTIFY);
9417
+ ret->u.n.method = n->method; n->method = NULL;
9418
+ ret->u.n.id = n->id; n->id = NULL;
9419
+ ret->u.n.options = n->options; n->options = NULL;
9420
+ ret->u.n.priority = n->priority;
9421
+ ret->u.n.message = n->message; n->message = NULL;
9427
+static commandlist_t *build_denotify(int t, struct dtags *d)
9429
+ commandlist_t *ret = new_command(t);
9431
+ assert(t == DENOTIFY);
9434
+ ret->u.d.comptag = d->comptag;
9435
+ ret->u.d.relation=d->relation;
9436
+ ret->u.d.pattern = d->pattern; d->pattern = NULL;
9437
+ ret->u.d.priority = d->priority;
9443
+static struct aetags *new_aetags(void)
9445
+ struct aetags *r = (struct aetags *) xmalloc(sizeof(struct aetags));
9447
+ r->addrtag = r->comptag = r->relation=-1;
9448
+ r->comparator=NULL;
9453
+static struct aetags *canon_aetags(struct aetags *ae)
9455
+ if (ae->addrtag == -1) { ae->addrtag = ALL; }
9456
+ if (ae->comparator == NULL) {
9457
+ ae->comparator = xstrdup("i;ascii-casemap");
9459
+ if (ae->comptag == -1) { ae->comptag = IS; }
9463
+static void free_aetags(struct aetags *ae)
9465
+ free(ae->comparator);
9469
+static struct htags *new_htags(void)
9471
+ struct htags *r = (struct htags *) xmalloc(sizeof(struct htags));
9473
+ r->comptag = r->relation= -1;
9475
+ r->comparator = NULL;
9480
+static struct htags *canon_htags(struct htags *h)
9482
+ if (h->comparator == NULL) {
9483
+ h->comparator = xstrdup("i;ascii-casemap");
9485
+ if (h->comptag == -1) { h->comptag = IS; }
9489
+static void free_htags(struct htags *h)
9491
+ free(h->comparator);
9495
+static struct vtags *new_vtags(void)
9497
+ struct vtags *r = (struct vtags *) xmalloc(sizeof(struct vtags));
9500
+ r->addresses = NULL;
9501
+ r->subject = NULL;
9507
+static struct vtags *canon_vtags(struct vtags *v)
9509
+ assert(parse_script->interp.vacation != NULL);
9511
+ if (v->days == -1) { v->days = 7; }
9512
+ if (v->days < parse_script->interp.vacation->min_response)
9513
+ { v->days = parse_script->interp.vacation->min_response; }
9514
+ if (v->days > parse_script->interp.vacation->max_response)
9515
+ { v->days = parse_script->interp.vacation->max_response; }
9516
+ if (v->mime == -1) { v->mime = 0; }
9521
+static void free_vtags(struct vtags *v)
9523
+ if (v->addresses) { free_sl(v->addresses); }
9524
+ if (v->subject) { free(v->subject); }
9528
+static struct ntags *new_ntags(void)
9530
+ struct ntags *r = (struct ntags *) xmalloc(sizeof(struct ntags));
9534
+ r->options = NULL;
9536
+ r->message = NULL;
9541
+static struct ntags *canon_ntags(struct ntags *n)
9543
+ if (n->priority == -1) { n->priority = NORMAL; }
9544
+ if (n->message == NULL) { n->message = strdup("$from$: $subject$"); }
9545
+ if (n->method == NULL) { n->method = strdup("default"); }
9548
+static struct dtags *canon_dtags(struct dtags *d)
9550
+ if (d->priority == -1) { d->priority = ANY; }
9551
+ if (d->comptag == -1) { d->comptag = ANY; }
9555
+static void free_ntags(struct ntags *n)
9557
+ if (n->method) { free(n->method); }
9558
+ if (n->id) { free(n->id); }
9559
+ if (n->options) { free_sl(n->options); }
9560
+ if (n->message) { free(n->message); }
9564
+static struct dtags *new_dtags(void)
9566
+ struct dtags *r = (struct dtags *) xmalloc(sizeof(struct dtags));
9568
+ r->comptag = r->priority= r->relation = -1;
9569
+ r->pattern = NULL;
9574
+static void free_dtags(struct dtags *d)
9576
+ if (d->pattern) free(d->pattern);
9580
+static int verify_stringlist(stringlist_t *sl, int (*verify)(char *))
9582
+ for (; sl != NULL && verify(sl->s); sl = sl->next) ;
9583
+ return (sl == NULL);
9586
+char *addrptr; /* pointer to address string for address lexer */
9587
+char addrerr[500]; /* buffer for address parser error messages */
9589
+static int verify_address(char *s)
9594
+ addrerr[0] = '\0'; /* paranoia */
9595
+ if (addrparse()) {
9596
+ snprintf(errbuf, sizeof(errbuf), "address '%s': %s", s, addrerr);
9603
+static int verify_mailbox(char *s)
9605
+ if (!verify_utf8(s)) return 0;
9607
+ /* xxx if not a mailbox, call yyerror */
9611
+static int verify_header(char *hdr)
9617
+ /* field-name = 1*ftext
9618
+ ftext = %d33-57 / %d59-126
9619
+ ; Any character except
9620
+ ; controls, SP, and
9622
+ if (!((*h >= 33 && *h <= 57) || (*h >= 59 && *h <= 126))) {
9623
+ snprintf(errbuf, sizeof(errbuf),
9624
+ "header '%s': not a valid header", hdr);
9633
+static int verify_addrheader(char *hdr)
9635
+ const char **h, *hdrs[] = {
9636
+ "from", "sender", "reply-to", /* RFC2822 originator fields */
9637
+ "to", "cc", "bcc", /* RFC2822 destination fields */
9638
+ "resent-from", "resent-sender", /* RFC2822 resent fields */
9639
+ "resent-to", "resent-cc", "resent-bcc",
9640
+ "return-path", /* RFC2822 trace fields */
9641
+ "disposition-notification-to", /* RFC2298 MDN request fields */
9642
+ "delivered-to", /* non-standard (loop detection) */
9643
+ "approved", /* RFC1036 moderator/control fields */
9648
+ if (!config_getswitch(IMAPOPT_RFC3028_STRICT))
9649
+ return verify_header(hdr);
9651
+ for (lcase(hdr), h = hdrs; *h; h++) {
9652
+ if (!strcmp(*h, hdr)) return 1;
9655
+ snprintf(errbuf, sizeof(errbuf),
9656
+ "header '%s': not a valid header for an address test", hdr);
9661
+static int verify_envelope(char *env)
9666
+ if (!config_getswitch(IMAPOPT_RFC3028_STRICT) ||
9667
+ !strcmp(env, "from") || !strcmp(env, "to") || !strcmp(env, "auth")) {
9671
+ snprintf(errbuf, sizeof(errbuf),
9672
+ "env-part '%s': not a valid part for an envelope test", env);
9677
+static int verify_relat(char *r)
9678
+{/* this really should have been a token to begin with.*/
9681
+ if (!strcmp(r, "gt")) {return GT;}
9682
+ else if (!strcmp(r, "ge")) {return GE;}
9683
+ else if (!strcmp(r, "lt")) {return LT;}
9684
+ else if (!strcmp(r, "le")) {return LE;}
9685
+ else if (!strcmp(r, "ne")) {return NE;}
9686
+ else if (!strcmp(r, "eq")) {return EQ;}
9688
+ sprintf(errbuf, "flag '%s': not a valid relational operation", r);
9698
+static int verify_flag(char *f)
9702
+ if (f[0] == '\\') {
9704
+ if (strcmp(f, "\\seen") && strcmp(f, "\\answered") &&
9705
+ strcmp(f, "\\flagged") && strcmp(f, "\\draft") &&
9706
+ strcmp(f, "\\deleted")) {
9707
+ snprintf(errbuf, sizeof(errbuf),
9708
+ "flag '%s': not a system flag", f);
9714
+ if (!imparse_isatom(f)) {
9715
+ snprintf(errbuf, sizeof(errbuf), "flag '%s': not a valid keyword", f);
9722
+#ifdef ENABLE_REGEX
9723
+static int verify_regex(char *s, int cflags)
9727
+ regex_t *reg = (regex_t *) xmalloc(sizeof(regex_t));
9729
+ if ((ret = regcomp(reg, s, cflags)) != 0) {
9730
+ (void) regerror(ret, reg, errbuf, sizeof(errbuf));
9739
+static int verify_regexs(stringlist_t *sl, char *comp)
9741
+ stringlist_t *sl2;
9742
+ int cflags = REG_EXTENDED | REG_NOSUB;
9745
+ if (!strcmp(comp, "i;ascii-casemap")) {
9746
+ cflags |= REG_ICASE;
9749
+ for (sl2 = sl; sl2 != NULL; sl2 = sl2->next) {
9750
+ if ((verify_regex(sl2->s, cflags)) == 0) {
9754
+ if (sl2 == NULL) {
9762
+ * Valid UTF-8 check (from RFC 2640 Annex B.1)
9764
+ * The following routine checks if a byte sequence is valid UTF-8. This
9765
+ * is done by checking for the proper tagging of the first and following
9766
+ * bytes to make sure they conform to the UTF-8 format. It then checks
9767
+ * to assure that the data part of the UTF-8 sequence conforms to the
9768
+ * proper range allowed by the encoding. Note: This routine will not
9769
+ * detect characters that have not been assigned and therefore do not
9772
+static int verify_utf8(char *s)
9774
+ const unsigned char *buf = (const unsigned char *)s;
9775
+ const unsigned char *endbuf = buf + strlen(s);
9776
+ unsigned char byte2mask = 0x00, c;
9777
+ int trailing = 0; /* trailing (continuation) bytes to follow */
9779
+ while (buf != endbuf) {
9782
+ if ((c & 0xC0) == 0x80) { /* Does trailing byte
9783
+ follow UTF-8 format? */
9784
+ if (byte2mask) { /* Need to check 2nd byte
9785
+ for proper range? */
9786
+ if (c & byte2mask) /* Are appropriate bits set? */
9797
+ if ((c & 0x80) == 0x00) /* valid 1 byte UTF-8 */
9799
+ else if ((c & 0xE0) == 0xC0) /* valid 2 byte UTF-8 */
9800
+ if (c & 0x1E) { /* Is UTF-8 byte
9801
+ in proper range? */
9806
+ else if ((c & 0xF0) == 0xE0) { /* valid 3 byte UTF-8 */
9807
+ if (!(c & 0x0F)) { /* Is UTF-8 byte
9808
+ in proper range? */
9809
+ byte2mask = 0x20; /* If not, set mask
9810
+ to check next byte */
9814
+ else if ((c & 0xF8) == 0xF0) { /* valid 4 byte UTF-8 */
9815
+ if (!(c & 0x07)) { /* Is UTF-8 byte
9816
+ in proper range? */
9817
+ byte2mask = 0x30; /* If not, set mask
9818
+ to check next byte */
9822
+ else if ((c & 0xFC) == 0xF8) { /* valid 5 byte UTF-8 */
9823
+ if (!(c & 0x03)) { /* Is UTF-8 byte
9824
+ in proper range? */
9825
+ byte2mask = 0x38; /* If not, set mask
9826
+ to check next byte */
9830
+ else if ((c & 0xFE) == 0xFC) { /* valid 6 byte UTF-8 */
9831
+ if (!(c & 0x01)) { /* Is UTF-8 byte
9832
+ in proper range? */
9833
+ byte2mask = 0x3C; /* If not, set mask
9834
+ to check next byte */
9843
+ if ((buf != endbuf) || trailing) {
9846
+ snprintf(errbuf, sizeof(errbuf),
9847
+ "string '%s': not valid utf8", s);
9854
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sieve_err.c
9855
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
9856
+++ b/src/lib-sieve/cmu/libsieve/sieve_err.c Sun May 04 16:00:59 2008 +0200
9860
+ * This file is automatically generated; please do not edit it.
9863
+#include <stdlib.h>
9865
+static const char * const text[] = {
9866
+ "Generic Sieve error",
9867
+ "Sieve interpretor not finalized",
9868
+ "Parse error in Sieve script",
9869
+ "Run-time error during Sieve execution",
9870
+ "Internal error in Sieve subsystem",
9871
+ "Memory exhausted in Sieve subsystem",
9872
+ "Sieve action already taken",
9876
+struct error_table {
9877
+ char const * const * msgs;
9882
+ struct et_list *next;
9883
+ const struct error_table * table;
9885
+extern struct et_list *_et_list;
9887
+const struct error_table et_siev_error_table = { text, -1237848064L, 7 };
9889
+static struct et_list link = { 0, 0 };
9891
+void initialize_siev_error_table(void);
9893
+void initialize_siev_error_table(void) {
9894
+ if (!link.table) {
9895
+ link.next = _et_list;
9896
+ link.table = &et_siev_error_table;
9901
+/* For Heimdal compatibility */
9902
+void initialize_siev_error_table_r(struct et_list **list);
9904
+void initialize_siev_error_table_r(struct et_list **list)
9906
+ struct et_list *et, **end;
9908
+ for (end = list, et = *list; et; end = &et->next, et = et->next)
9909
+ if (et->table->msgs == text)
9911
+ et = malloc(sizeof(struct et_list));
9914
+ et->table = &et_siev_error_table;
9918
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sieve_err.h
9919
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
9920
+++ b/src/lib-sieve/cmu/libsieve/sieve_err.h Sun May 04 16:00:59 2008 +0200
9924
+ * This file used to be automatically generated from sieve_er.et.
9928
+#define SIEVE_FAIL (-1237848064L)
9929
+#define SIEVE_NOT_FINALIZED (-1237848063L)
9930
+#define SIEVE_PARSE_ERROR (-1237848062L)
9931
+#define SIEVE_RUN_ERROR (-1237848061L)
9932
+#define SIEVE_INTERNAL_ERROR (-1237848060L)
9933
+#define SIEVE_NOMEM (-1237848059L)
9934
+#define SIEVE_DONE (-1237848058L)
9935
+extern const struct error_table et_siev_error_table;
9936
+extern void initialize_siev_error_table(void);
9938
+/* For compatibility with Heimdal */
9939
+extern void initialize_siev_error_table_r(struct et_list **list);
9941
+#define ERROR_TABLE_BASE_siev (-1237848064L)
9943
+/* for compatibility with older versions... */
9944
+#define init_siev_err_tbl initialize_siev_error_table
9945
+#define siev_err_base ERROR_TABLE_BASE_siev
9946
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sieve_interface.h
9947
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
9948
+++ b/src/lib-sieve/cmu/libsieve/sieve_interface.h Sun May 04 16:00:59 2008 +0200
9950
+/* sieve_interface.h -- interface for deliver
9953
+/***********************************************************
9954
+ Copyright 1999 by Carnegie Mellon University
9956
+ All Rights Reserved
9958
+Permission to use, copy, modify, and distribute this software and its
9959
+documentation for any purpose and without fee is hereby granted,
9960
+provided that the above copyright notice appear in all copies and that
9961
+both that copyright notice and this permission notice appear in
9962
+supporting documentation, and that the name of Carnegie Mellon
9963
+University not be used in advertising or publicity pertaining to
9964
+distribution of the software without specific, written prior
9967
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
9968
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
9969
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
9970
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
9971
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
9972
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
9973
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
9974
+******************************************************************/
9981
+#define SIEVE_VERSION "CMU Sieve 2.2"
9984
+#define SIEVE_OK (0)
9986
+#include "sieve_err.h"
9988
+/* external sieve types */
9989
+typedef struct sieve_interp sieve_interp_t;
9990
+typedef struct sieve_script sieve_script_t;
9991
+typedef struct sieve_bytecode sieve_bytecode_t;
9992
+typedef struct bytecode_info bytecode_info_t;
9994
+typedef int sieve_callback(void *action_context, void *interp_context,
9995
+ void *script_context,
9996
+ void *message_context, const char **errmsg);
9997
+typedef int sieve_get_size(void *message_context, int *size);
9998
+typedef int sieve_get_header(void *message_context,
9999
+ const char *header,
10000
+ const char ***contents);
10001
+typedef int sieve_get_envelope(void *message_context,
10002
+ const char *field,
10003
+ const char ***contents);
10005
+typedef struct sieve_vacation {
10006
+ int min_response; /* 0 -> defaults to 3 */
10007
+ int max_response; /* 0 -> defaults to 90 */
10009
+ /* given a hash, say whether we've already responded to it in the last
10010
+ days days. return SIEVE_OK if we SHOULD autorespond (have not already)
10011
+ or SIEVE_DONE if we SHOULD NOT. */
10012
+ sieve_callback *autorespond;
10014
+ /* mail the response */
10015
+ sieve_callback *send_response;
10016
+} sieve_vacation_t;
10018
+typedef struct sieve_imapflags {
10019
+ char **flag; /* NULL -> defaults to \flagged */
10021
+} sieve_imapflags_t;
10023
+typedef struct sieve_redirect_context {
10024
+ const char *addr;
10025
+} sieve_redirect_context_t;
10027
+typedef struct sieve_reject_context {
10029
+} sieve_reject_context_t;
10031
+typedef struct sieve_fileinto_context {
10032
+ const char *mailbox;
10033
+ sieve_imapflags_t *imapflags;
10034
+} sieve_fileinto_context_t;
10036
+typedef struct sieve_keep_context {
10037
+ sieve_imapflags_t *imapflags;
10038
+} sieve_keep_context_t;
10040
+typedef struct sieve_notify_context {
10041
+ const char *method;
10042
+ const char **options;
10043
+ const char *priority;
10044
+ const char *message;
10045
+} sieve_notify_context_t;
10047
+typedef struct sieve_autorespond_context {
10048
+ unsigned char *hash;
10051
+} sieve_autorespond_context_t;
10053
+typedef struct sieve_send_response_context {
10059
+} sieve_send_response_context_t;
10061
+/* build a sieve interpretor */
10062
+int sieve_interp_alloc(sieve_interp_t **interp, void *interp_context);
10063
+int sieve_interp_free(sieve_interp_t **interp);
10065
+/* add the callbacks for actions. undefined behavior results if these
10066
+ are called after sieve_script_parse is called! */
10067
+int sieve_register_redirect(sieve_interp_t *interp, sieve_callback *f);
10068
+int sieve_register_discard(sieve_interp_t *interp, sieve_callback *f);
10069
+int sieve_register_reject(sieve_interp_t *interp, sieve_callback *f);
10070
+int sieve_register_fileinto(sieve_interp_t *interp, sieve_callback *f);
10071
+int sieve_register_keep(sieve_interp_t *interp, sieve_callback *f);
10072
+int sieve_register_vacation(sieve_interp_t *interp, sieve_vacation_t *v);
10073
+int sieve_register_imapflags(sieve_interp_t *interp, sieve_imapflags_t *mark);
10074
+int sieve_register_notify(sieve_interp_t *interp, sieve_callback *f);
10076
+/* add the callbacks for messages. again, undefined if used after
10077
+ sieve_script_parse */
10078
+int sieve_register_size(sieve_interp_t *interp, sieve_get_size *f);
10079
+int sieve_register_header(sieve_interp_t *interp, sieve_get_header *f);
10080
+int sieve_register_envelope(sieve_interp_t *interp, sieve_get_envelope *f);
10082
+typedef int sieve_parse_error(int lineno, const char *msg,
10083
+ void *interp_context,
10084
+ void *script_context);
10085
+int sieve_register_parse_error(sieve_interp_t *interp, sieve_parse_error *f);
10087
+typedef int sieve_execute_error(const char *msg, void *interp_context,
10088
+ void *script_context, void *message_context);
10089
+int sieve_register_execute_error(sieve_interp_t *interp,
10090
+ sieve_execute_error *f);
10092
+/* given an interpretor and a script, produce an executable script */
10093
+int sieve_script_parse(sieve_interp_t *interp, FILE *script,
10094
+ void *script_context, sieve_script_t **ret);
10096
+/* given a bytecode file descriptor, setup the sieve_bytecode_t */
10097
+int sieve_script_load(const char *fname, sieve_bytecode_t **ret);
10099
+/* Unload a sieve_bytecode_t */
10100
+int sieve_script_unload(sieve_bytecode_t **s);
10102
+/* Free a sieve_script_t */
10103
+int sieve_script_free(sieve_script_t **s);
10105
+/* execute bytecode on a message */
10106
+int sieve_execute_bytecode(sieve_bytecode_t *script, sieve_interp_t *interp,
10107
+ void *script_context, void *message_context);
10109
+/* Get space separated list of extensions supported by the implementation */
10110
+const char *sieve_listextensions(void);
10112
+/* Create a bytecode structure given a parsed commandlist */
10113
+int sieve_generate_bytecode(bytecode_info_t **retval, sieve_script_t *s);
10115
+/* Emit bytecode to a file descriptor */
10116
+int sieve_emit_bytecode(int fd, bytecode_info_t *bc);
10118
+/* Free a bytecode_info_t */
10119
+void sieve_free_bytecode(bytecode_info_t **p);
10122
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sievec.c
10123
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
10124
+++ b/src/lib-sieve/cmu/libsieve/sievec.c Sun May 04 16:00:59 2008 +0200
10126
+/* sievec.c -- compile a sieve script to bytecode manually
10131
+ * Copyright (c) 1999-2000 Carnegie Mellon University. All rights reserved.
10133
+ * Redistribution and use in source and binary forms, with or without
10134
+ * modification, are permitted provided that the following conditions
10137
+ * 1. Redistributions of source code must retain the above copyright
10138
+ * notice, this list of conditions and the following disclaimer.
10140
+ * 2. Redistributions in binary form must reproduce the above copyright
10141
+ * notice, this list of conditions and the following disclaimer in
10142
+ * the documentation and/or other materials provided with the
10145
+ * 3. The name "Carnegie Mellon University" must not be used to
10146
+ * endorse or promote products derived from this software without
10147
+ * prior written permission. For permission or any other legal
10148
+ * details, please contact
10149
+ * Office of Technology Transfer
10150
+ * Carnegie Mellon University
10151
+ * 5000 Forbes Avenue
10152
+ * Pittsburgh, PA 15213-3890
10153
+ * (412) 268-4387, fax: (412) 268-7395
10154
+ * tech-transfer@andrew.cmu.edu
10156
+ * 4. Redistributions of any form whatsoever must retain the following
10157
+ * acknowledgment:
10158
+ * "This product includes software developed by Computing Services
10159
+ * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
10161
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
10162
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10163
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
10164
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
10165
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
10166
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
10167
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
10171
+#ifdef HAVE_CONFIG_H
10172
+#include <config.h>
10175
+#include "sieve_interface.h"
10177
+#include "libconfig.h"
10178
+#include "xmalloc.h"
10180
+#include "script.h"
10181
+#include <string.h>
10182
+#include <stdlib.h>
10183
+#include <sys/file.h>
10184
+#include <unistd.h>
10185
+#include <stdio.h>
10186
+#include <sys/types.h>
10187
+#include <sys/stat.h>
10188
+#include <fcntl.h>
10190
+struct et_list *_et_list = NULL;
10192
+int is_script_parsable(FILE *stream, char **errstr, sieve_script_t **ret);
10194
+#define TIMSIEVE_FAIL -1
10195
+#define TIMSIEVE_OK 0
10197
+int main(int argc, char **argv)
10200
+ char *err = NULL;
10201
+ sieve_script_t *s;
10202
+ bytecode_info_t *bc;
10203
+ int c, fd, usage_error = 0;
10205
+ while ((c = getopt(argc, argv, "C:")) != EOF)
10212
+ if (usage_error || (argc - optind) < 2) {
10213
+ printf("Syntax: %s <filename> <outputfile>\n",
10218
+ instream = fopen(argv[optind++],"r");
10219
+ if(instream == NULL) {
10220
+ printf("Unable to open %s for reading\n", argv[1]);
10224
+ if(is_script_parsable(instream, &err, &s) == TIMSIEVE_FAIL) {
10226
+ printf("Unable to parse script: %s\n", err);
10228
+ printf("Unable to parse script.\n");
10234
+ /* Now, generate the bytecode */
10235
+ if(sieve_generate_bytecode(&bc, s) == -1) {
10236
+ printf("bytecode generate failed\n");
10240
+ /* Now, open the new file */
10241
+ fd = open(argv[optind], O_CREAT | O_TRUNC | O_WRONLY, 0644);
10243
+ printf("couldn't open bytecode output file\n");
10247
+ /* Now, emit the bytecode */
10248
+ if(sieve_emit_bytecode(fd, bc) == -1) {
10249
+ printf("bytecode emit failed\n");
10258
+/* to make larry's stupid functions happy :) */
10259
+static void foo(void)
10261
+ i_fatal("stub function called");
10263
+sieve_vacation_t vacation = {
10264
+ 0, /* min response */
10265
+ 0, /* max response */
10266
+ (sieve_callback *) &foo, /* autorespond() */
10267
+ (sieve_callback *) &foo /* send_response() */
10270
+static int sieve_notify(void *ac __attr_unused__,
10271
+ void *interp_context __attr_unused__,
10272
+ void *script_context __attr_unused__,
10273
+ void *message_context __attr_unused__,
10274
+ const char **errmsg __attr_unused__)
10276
+ i_fatal("stub function called");
10277
+ return SIEVE_FAIL;
10280
+static int mysieve_error(int lineno, const char *msg,
10281
+ void *i __attr_unused__, void *s)
10284
+ char **errstr = (char **) s;
10286
+ snprintf(buf, 80, "line %d: %s\r\n", lineno, msg);
10287
+ *errstr = xrealloc(*errstr, strlen(*errstr) + strlen(buf) + 30);
10288
+ i_info("%s", buf);
10289
+ strcat(*errstr, buf);
10294
+/* end the boilerplate */
10296
+/* returns TRUE or FALSE */
10297
+int is_script_parsable(FILE *stream, char **errstr, sieve_script_t **ret)
10299
+ sieve_interp_t *i;
10300
+ sieve_script_t *s;
10303
+ res = sieve_interp_alloc(&i, NULL);
10304
+ if (res != SIEVE_OK) {
10305
+ i_error("sieve_interp_alloc() returns %d\n", res);
10306
+ return TIMSIEVE_FAIL;
10309
+ res = sieve_register_redirect(i, (sieve_callback *) &foo);
10310
+ if (res != SIEVE_OK) {
10311
+ i_error("sieve_register_redirect() returns %d\n", res);
10312
+ return TIMSIEVE_FAIL;
10314
+ res = sieve_register_discard(i, (sieve_callback *) &foo);
10315
+ if (res != SIEVE_OK) {
10316
+ i_error("sieve_register_discard() returns %d\n", res);
10317
+ return TIMSIEVE_FAIL;
10319
+ res = sieve_register_reject(i, (sieve_callback *) &foo);
10320
+ if (res != SIEVE_OK) {
10321
+ i_error("sieve_register_reject() returns %d\n", res);
10322
+ return TIMSIEVE_FAIL;
10324
+ res = sieve_register_fileinto(i, (sieve_callback *) &foo);
10325
+ if (res != SIEVE_OK) {
10326
+ i_error("sieve_register_fileinto() returns %d\n", res);
10327
+ return TIMSIEVE_FAIL;
10329
+ res = sieve_register_keep(i, (sieve_callback *) &foo);
10330
+ if (res != SIEVE_OK) {
10331
+ i_error("sieve_register_keep() returns %d\n", res);
10332
+ return TIMSIEVE_FAIL;
10335
+ res = sieve_register_imapflags(i, NULL);
10336
+ if (res != SIEVE_OK) {
10337
+ i_error("sieve_register_imapflags() returns %d\n", res);
10338
+ return TIMSIEVE_FAIL;
10341
+ res = sieve_register_size(i, (sieve_get_size *) &foo);
10342
+ if (res != SIEVE_OK) {
10343
+ i_error("sieve_register_size() returns %d\n", res);
10344
+ return TIMSIEVE_FAIL;
10347
+ res = sieve_register_header(i, (sieve_get_header *) &foo);
10348
+ if (res != SIEVE_OK) {
10349
+ i_error("sieve_register_header() returns %d\n", res);
10350
+ return TIMSIEVE_FAIL;
10353
+ res = sieve_register_envelope(i, (sieve_get_envelope *) &foo);
10354
+ if (res != SIEVE_OK) {
10355
+ i_error("sieve_register_envelope() returns %d\n", res);
10356
+ return TIMSIEVE_FAIL;
10359
+ res = sieve_register_vacation(i, &vacation);
10360
+ if (res != SIEVE_OK) {
10361
+ i_error("sieve_register_vacation() returns %d\n", res);
10362
+ return TIMSIEVE_FAIL;
10365
+ res = sieve_register_notify(i, &sieve_notify);
10366
+ if (res != SIEVE_OK) {
10367
+ i_error("sieve_register_notify() returns %d\n", res);
10368
+ return TIMSIEVE_FAIL;
10371
+ res = sieve_register_parse_error(i, &mysieve_error);
10372
+ if (res != SIEVE_OK) {
10373
+ i_error("sieve_register_parse_error() returns %d\n", res);
10374
+ return TIMSIEVE_FAIL;
10379
+ *errstr = (char *) xmalloc(20 * sizeof(char));
10380
+ strcpy(*errstr, "script errors:\r\n");
10382
+ res = sieve_script_parse(i, stream, errstr, &s);
10384
+ if (res == SIEVE_OK) {
10388
+ sieve_script_free(&s);
10394
+ /* free interpreter */
10395
+ sieve_interp_free(&i);
10397
+ return (res == SIEVE_OK) ? TIMSIEVE_OK : TIMSIEVE_FAIL;
10399
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/sieved.c
10400
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
10401
+++ b/src/lib-sieve/cmu/libsieve/sieved.c Sun May 04 16:00:59 2008 +0200
10403
+/* dump.c -- bytecode decompiler
10406
+/***********************************************************
10407
+ Copyright 1999 by Carnegie Mellon University
10409
+ All Rights Reserved
10411
+Permission to use, copy, modify, and distribute this software and its
10412
+documentation for any purpose and without fee is hereby granted,
10413
+provided that the above copyright notice appear in all copies and that
10414
+both that copyright notice and this permission notice appear in
10415
+supporting documentation, and that the name of Carnegie Mellon
10416
+University not be used in advertising or publicity pertaining to
10417
+distribution of the software without specific, written prior
10420
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
10421
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
10422
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
10423
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
10424
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
10425
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
10426
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
10427
+*****************************************************************/
10431
+#include "sieve_interface.h"
10433
+#include "bytecode.h"
10434
+#include "script.h"
10436
+#include "xmalloc.h"
10437
+#include <sys/types.h>
10438
+#include <sys/stat.h>
10439
+#include <fcntl.h>
10440
+#include <unistd.h>
10441
+#include <netinet/in.h>
10443
+#include <string.h>
10447
+static void dump2(bytecode_input_t *d, int len);
10448
+static int dump2_test(bytecode_input_t * d, int i);
10450
+static int load(int fd, bytecode_input_t ** d)
10452
+ const char * data=NULL;
10453
+ struct stat sbuf;
10454
+ unsigned long len=0;
10456
+ if (fstat(fd, &sbuf) == -1) {
10457
+ printf("IOERROR: fstating sieve script: %m");
10458
+ return SIEVE_FAIL;
10461
+ /*this reads in data and length from file*/
10462
+ map_refresh(fd, 1, &(data), &len, sbuf.st_size,
10463
+ "sievescript", "");
10464
+ *d=(bytecode_input_t *)data;
10468
+ return (len/sizeof(int));
10472
+int main(int argc, char * argv[])
10474
+ bytecode_input_t * bc;
10477
+ unsigned long len;
10480
+ fprintf(stderr, "usage:\n %s script\n", argv[0]);
10485
+ script_fd = open(argv[1], O_RDONLY);
10486
+ if (script_fd == -1)
10488
+ printf("can not open script '%s'\n", argv[1]);
10493
+ len=load(script_fd,&bc);
10494
+ close(script_fd);
10504
+static int write_list(int list_len, int i, bytecode_input_t * d)
10508
+ for (x=0; x<list_len; x++)
10510
+ const char *data;
10513
+ i = unwrap_string(d, i, &data, &len);
10515
+ printf("{%d}%s\n", len, data);
10520
+static int printComparison(bytecode_input_t *d ,int i)
10522
+ printf("Comparison: ");
10523
+ switch(ntohl(d[i].value))
10525
+ case B_IS: printf("Is"); break;
10526
+ case B_CONTAINS:printf("Contains"); break;
10527
+ case B_MATCHES: printf("Matches"); break;
10528
+ case B_REGEX: printf("Regex"); break;
10532
+ switch(ntohl(d[i+1].value))
10534
+ case B_GT: printf(" greater than "); break;
10535
+ case B_GE: printf(" greater than or equal "); break;
10536
+ case B_LT: printf(" less than "); break;
10537
+ case B_LE: printf(" less than or equal "); break;
10538
+ case B_NE: printf(" not equal "); break;
10539
+ case B_EQ: printf(" equal "); break;
10546
+ switch(ntohl(d[i+1].value))
10548
+ case B_GT: printf(" greater than "); break;
10549
+ case B_GE: printf(" greater than or equal ");break;
10550
+ case B_LT: printf(" less than "); break;
10551
+ case B_LE: printf(" less than or equal ");break;
10552
+ case B_NE: printf(" not equal "); break;
10553
+ case B_EQ: printf(" equal ");break;
10561
+ switch (ntohl(d[i+2].value))
10563
+ case B_ASCIICASEMAP: printf(" (ascii-casemap) "); break;
10564
+ case B_OCTET: printf(" (octet) "); break;
10565
+ case B_ASCIINUMERIC: printf(" (ascii-numeric) "); break;
10566
+ default: exit(1);
10574
+static int dump2_test(bytecode_input_t * d, int i)
10577
+ switch(ntohl(d[i].value)) {
10586
+ case BC_NOT:/*2*/
10588
+ there is a value being skipped in the second pass...
10589
+ no idea what it does, but it isn't carried to here...
10590
+ see bytecodee.c */
10592
+ i=dump2_test(d, i+1);
10596
+ printf("exists");
10597
+ i=write_list(ntohl(d[i+1].len), i+2, d);
10601
+ if (ntohl(d[i+1].value)==B_OVER) {
10603
+ printf("over %d", ntohl(d[i+2].value));
10606
+ printf("under %d", ntohl(d[i+2].value));
10610
+ case BC_ANYOF:/*5*/
10611
+ printf("any of \n(");
10612
+ l=ntohl(d[i+1].len);
10615
+ for (x=0; x<l; x++)
10617
+ i=dump2_test(d,i);
10624
+ case BC_ALLOF:/*6*/
10625
+ printf("all of \n(");
10626
+ l=ntohl(d[i+1].len);
10629
+ for (x=0; x<l; x++)
10631
+ i=dump2_test(d,i);
10638
+ case BC_ADDRESS:/*7*/
10639
+ printf("Address (");
10640
+ i=printComparison(d, i+1);
10641
+ printf(" type: ");
10642
+ switch(ntohl(d[i++].value))
10644
+ case B_ALL: printf("all"); break;
10645
+ case B_LOCALPART:printf("localpart"); break;
10646
+ case B_DOMAIN:printf("domain"); break;
10647
+ case B_USER:printf("user"); break;
10648
+ case B_DETAIL:printf("detail"); break;
10650
+ printf(" Headers:");
10651
+ i=write_list(ntohl(d[i].len), i+1, d);
10652
+ printf(" Data:");
10653
+ i=write_list(ntohl(d[i].len), i+1, d);
10656
+ case BC_ENVELOPE:/*8*/
10657
+ printf("Envelope (");
10658
+ i=printComparison(d, i+1);
10659
+ printf(" type: ");
10660
+ switch(ntohl(d[i++].value))
10662
+ case B_ALL: printf("all"); break;
10663
+ case B_LOCALPART:printf("localpart"); break;
10664
+ case B_DOMAIN:printf("domain"); break;
10665
+ case B_USER:printf("user"); break;
10666
+ case B_DETAIL:printf("detail"); break;
10668
+ printf(" Headers:");
10669
+ i=write_list(ntohl(d[i].len), i+1, d);
10670
+ printf(" Data:");
10671
+ i=write_list(ntohl(d[i].len), i+1, d);
10674
+ case BC_HEADER:/*9*/
10675
+ printf("Header [");
10676
+ i= printComparison(d, i+1);
10677
+ printf(" Headers: ");
10678
+ i=write_list(ntohl(d[i].len), i+1, d);
10679
+ printf(" Data: ");
10680
+ i=write_list(ntohl(d[i].len), i+1, d);
10684
+ printf("WERT %d ", ntohl(d[i].value));
10689
+static void dump2(bytecode_input_t *d, int bc_len)
10692
+ const char *data;
10695
+ if(memcmp(d, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN)) {
10696
+ printf("not a bytecode file [magic number test failed]\n");
10700
+ i = BYTECODE_MAGIC_LEN / sizeof(bytecode_input_t);
10702
+ printf("Sievecode version %d\n", ntohl(d[i].op));
10705
+ for(i++; i<bc_len;)
10707
+ switch(ntohl(d[i].op)) {
10709
+ case B_STOP:/*0*/
10710
+ printf("%d: STOP\n",i);
10714
+ case B_KEEP:/*1*/
10715
+ printf("%d: KEEP\n",i);
10719
+ case B_DISCARD:/*2*/
10720
+ printf("%d: DISCARD\n",i);
10724
+ case B_REJECT:/*3*/
10725
+ i = unwrap_string(d, i+1, &data, &len);
10726
+ printf("%d: REJECT {%d}%s\n", i, len, data);
10729
+ case B_FILEINTO: /*4*/
10730
+ i = unwrap_string(d, i+1, &data, &len);
10731
+ printf("%d: FILEINTO {%d}%s\n",i, len, data);
10734
+ case B_REDIRECT: /*5*/
10735
+ i = unwrap_string(d, i+1, &data, &len);
10736
+ printf("%d: REDIRECT {%d}%s\n",i,len,data);
10740
+ printf("%d: IF (ends at %d)",i, ntohl(d[i+1].value));
10742
+ /* there is no short circuiting involved here*/
10743
+ i = dump2_test(d,i+2);
10748
+ case B_MARK:/*7*/
10749
+ printf("%d: MARK\n",i);
10753
+ case B_UNMARK:/*8*/
10754
+ printf("%d: UNMARK\n",i);
10758
+ case B_ADDFLAG: /*9*/
10759
+ printf("%d: ADDFLAG {%d}\n",i,ntohl(d[i+1].len));
10760
+ i=write_list(ntohl(d[i+1].len),i+2,d);
10763
+ case B_SETFLAG: /*10*/
10764
+ printf("%d: SETFLAG {%d}\n",i,ntohl(d[i+1].len));
10765
+ i=write_list(ntohl(d[i+1].len),i+2,d);
10768
+ case B_REMOVEFLAG: /*11*/
10769
+ printf("%d: REMOVEFLAG {%d}\n",i,ntohl(d[i+1].len));
10770
+ i=write_list(ntohl(d[i+1].len),i+2,d);
10773
+ case B_DENOTIFY:/*12*/
10774
+ printf("%d: DENOTIFY\n",i);
10776
+ printf(" PRIORITY(%d) Comparison type %d (relat %d)\n",
10777
+ ntohl(d[i].value), ntohl(d[i+1].value), ntohl(d[i+2].value));
10780
+ i = unwrap_string(d, i+1, &data, &len);
10782
+ printf(" ({%d}%s)\n", len, (!data ? "[nil]" : data));
10785
+ case B_NOTIFY: /*13*/
10786
+ i = unwrap_string(d, i+1, &data, &len);
10788
+ printf("%d: NOTIFY METHOD({%d}%s)\n",i,len,data);
10790
+ i = unwrap_string(d, i, &data, &len);
10792
+ printf(" ID({%d}%s) OPTIONS ", len,
10793
+ (!data ? "[nil]" : data));
10795
+ i=write_list(ntohl(d[i].len),i+1,d);
10797
+ printf(" PRIORITY(%d)\n", ntohl(d[i].value));
10800
+ i = unwrap_string(d, i, &data, &len);
10802
+ printf(" MESSAGE({%d}%s)\n", len, data);
10806
+ case B_VACATION:/*14*/
10807
+ printf("%d: VACATION\n",i);
10808
+ /*add address list here!*/
10809
+ i=write_list(ntohl(d[i+1].len),i+2,d);
10811
+ i = unwrap_string(d, i, &data, &len);
10813
+ printf("%d SUBJ({%d}%s) \n",i, len, (!data ? "[nil]" : data));
10815
+ i = unwrap_string(d, i, &data, &len);
10817
+ printf("%d MESG({%d}%s) \n", i, len, (!data ? "[nil]" : data));
10819
+ printf("DAYS(%d) MIME(%d)\n", ntohl(d[i].value), ntohl(d[i+1].value));
10823
+ case B_NULL:/*15*/
10824
+ printf("%d:NULL\n",i);
10827
+ case B_JUMP:/*16*/
10828
+ printf("%d:JUMP %d\n",i, ntohl(d[i+1].jump));
10832
+ printf("%d: %d (NOT AN OP)\n",i,ntohl(d[i].op));
10836
+ printf("full len is: %d\n", bc_len);
10840
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/tree.c
10841
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
10842
+++ b/src/lib-sieve/cmu/libsieve/tree.c Sun May 04 16:00:59 2008 +0200
10844
+/* tree.c -- abstract syntax tree handling
10845
+ * Larry Greenfield
10848
+/***********************************************************
10849
+ Copyright 1999 by Carnegie Mellon University
10851
+ All Rights Reserved
10853
+Permission to use, copy, modify, and distribute this software and its
10854
+documentation for any purpose and without fee is hereby granted,
10855
+provided that the above copyright notice appear in all copies and that
10856
+both that copyright notice and this permission notice appear in
10857
+supporting documentation, and that the name of Carnegie Mellon
10858
+University not be used in advertising or publicity pertaining to
10859
+distribution of the software without specific, written prior
10862
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
10863
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
10864
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
10865
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
10866
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
10867
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
10868
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
10869
+******************************************************************/
10871
+#ifdef HAVE_CONFIG_H
10872
+#include <config.h>
10875
+#include <stdlib.h>
10876
+#include "xmalloc.h"
10879
+#include "sieve.h"
10881
+stringlist_t *new_sl(char *s, stringlist_t *n)
10883
+ stringlist_t *p = (stringlist_t *) xmalloc(sizeof(stringlist_t));
10890
+tag_t *new_tag(int type, char *s)
10892
+ tag_t *p = (tag_t *) xmalloc(sizeof(tag_t));
10898
+taglist_t *new_taglist(tag_t *t, taglist_t *n)
10900
+ taglist_t *p = (taglist_t *) xmalloc(sizeof(taglist_t));
10906
+test_t *new_test(int type)
10908
+ test_t *p = (test_t *) xmalloc(sizeof(test_t));
10913
+testlist_t *new_testlist(test_t *t, testlist_t *n)
10915
+ testlist_t *p = (testlist_t *) xmalloc(sizeof(testlist_t));
10921
+commandlist_t *new_command(int type)
10923
+ commandlist_t *p = (commandlist_t *) xmalloc(sizeof(commandlist_t));
10929
+commandlist_t *new_if(test_t *t, commandlist_t *y, commandlist_t *n)
10931
+ commandlist_t *p = (commandlist_t *) xmalloc(sizeof(commandlist_t));
10934
+ p->u.i.do_then = y;
10935
+ p->u.i.do_else = n;
10940
+void free_sl(stringlist_t *sl)
10942
+ stringlist_t *sl2;
10944
+ while (sl != NULL) {
10947
+ if (sl->s) free(sl->s);
10955
+void free_test(test_t *t);
10957
+static void free_tl(testlist_t *tl)
10964
+ if (tl->t) free_test(tl->t);
10971
+void free_test(test_t *t)
10973
+ if (t == NULL) return;
10975
+ switch (t->type) {
10978
+ free_tl(t->u.tl);
10982
+ free_sl(t->u.sl);
10991
+ free_sl(t->u.h.sl);
10992
+ free_sl(t->u.h.pl);
10997
+ free_sl(t->u.ae.sl);
10998
+ free_sl(t->u.ae.pl);
11002
+ free_test(t->u.t);
11009
+void free_tree(commandlist_t *cl)
11011
+ commandlist_t *cl2;
11013
+ while (cl != NULL) {
11015
+ switch (cl->type) {
11017
+ free_test(cl->u.i.t);
11018
+ free_tree(cl->u.i.do_then);
11019
+ free_tree(cl->u.i.do_else);
11025
+ if (cl->u.str) free(cl->u.str);
11029
+ if (cl->u.v.subject) free(cl->u.v.subject);
11030
+ if (cl->u.v.addresses) free_sl(cl->u.v.addresses);
11031
+ if (cl->u.v.message) free(cl->u.v.message);
11037
+ free_sl(cl->u.sl);
11046
+ if (cl->u.n.method) free(cl->u.n.method);
11047
+ if (cl->u.n.id) free(cl->u.n.id);
11048
+ if (cl->u.n.options) free_sl(cl->u.n.options);
11049
+ if (cl->u.n.message) free(cl->u.n.message);
11053
+ if (cl->u.d.pattern) {
11054
+#ifdef ENABLE_REGEX
11055
+ if (cl->u.d.comptag == REGEX) {
11056
+ regfree((regex_t *) cl->u.d.pattern);
11059
+ free(cl->u.d.pattern);
11068
diff -r 894f003d9f5f src/lib-sieve/cmu/libsieve/tree.h
11069
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
11070
+++ b/src/lib-sieve/cmu/libsieve/tree.h Sun May 04 16:00:59 2008 +0200
11072
+/* tree.h -- abstract syntax tree
11073
+ * Larry Greenfield
11076
+/***********************************************************
11077
+ Copyright 1999 by Carnegie Mellon University
11079
+ All Rights Reserved
11081
+Permission to use, copy, modify, and distribute this software and its
11082
+documentation for any purpose and without fee is hereby granted,
11083
+provided that the above copyright notice appear in all copies and that
11084
+both that copyright notice and this permission notice appear in
11085
+supporting documentation, and that the name of Carnegie Mellon
11086
+University not be used in advertising or publicity pertaining to
11087
+distribution of the software without specific, written prior
11090
+CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
11091
+THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
11092
+FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE FOR
11093
+ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11094
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
11095
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
11096
+OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
11097
+******************************************************************/
11102
+#include "comparator.h"
11104
+/* abstract syntax tree for sieve */
11105
+typedef struct Stringlist stringlist_t;
11106
+typedef struct Commandlist commandlist_t;
11107
+typedef struct Test test_t;
11108
+typedef struct Testlist testlist_t;
11109
+typedef struct Tag tag_t;
11110
+typedef struct Taglist taglist_t;
11112
+struct Stringlist {
11114
+ stringlist_t *next;
11131
+ testlist_t *tl; /* anyof, allof */
11132
+ stringlist_t *sl; /* exists */
11133
+ struct { /* it's a header test */
11135
+ char * comparator;
11138
+ stringlist_t *sl;
11139
+ stringlist_t *pl;
11141
+ struct { /* it's an address or envelope test */
11143
+ char * comparator;
11146
+ stringlist_t *sl;
11147
+ stringlist_t *pl;
11150
+ test_t *t; /* not */
11151
+ struct { /* size */
11153
+ int n; /* param */
11160
+ testlist_t *next;
11163
+struct Commandlist {
11167
+ stringlist_t *sl; /* the parameters */
11168
+ struct { /* it's an if statement */
11170
+ commandlist_t *do_then;
11171
+ commandlist_t *do_else;
11173
+ struct { /* it's a vacation action */
11176
+ stringlist_t *addresses;
11180
+ struct { /* it's a notify action */
11183
+ stringlist_t *options;
11187
+ struct { /* it's a denotify action */
11195
+ struct Commandlist *next;
11198
+stringlist_t *new_sl(char *s, stringlist_t *n);
11199
+tag_t *new_tag(int type, char *s);
11200
+taglist_t *new_taglist(tag_t *t, taglist_t *n);
11201
+test_t *new_test(int type);
11202
+testlist_t *new_testlist(test_t *t, testlist_t *n);
11203
+commandlist_t *new_command(int type);
11204
+commandlist_t *new_if(test_t *t, commandlist_t *y, commandlist_t *n);
11206
+void free_sl(stringlist_t *sl);
11207
+void free_test(test_t *t);
11208
+void free_tree(commandlist_t *cl);
11211
diff -r 894f003d9f5f src/lib-sieve/cmu/map.c
11212
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
11213
+++ b/src/lib-sieve/cmu/map.c Sun May 04 16:00:59 2008 +0200
11218
+#include <unistd.h>
11220
+static ssize_t read_full_n(int fd, void *data, size_t size)
11222
+ ssize_t ret, all_ret = 0;
11224
+ while (size > 0) {
11225
+ ret = read(fd, data, size);
11229
+ data = PTR_OFFSET(data, ret);
11237
+void map_refresh(int fd, int onceonly __attr_unused__, const char **base,
11238
+ unsigned long *len, unsigned long newlen,
11239
+ const char *name, const char *mboxname __attr_unused__)
11244
+ if (newlen == 0) {
11245
+ /* the file is a broken zero-byte file */
11250
+ *base = p = i_malloc(newlen);
11253
+ ret = read_full_n(fd, p, newlen);
11255
+ i_error("read_full_n(%s) failed: %m", name);
11262
+void map_free(const char **base, unsigned long *len __attr_unused__)
11264
+ char *x = (char *) *base;
11270
diff -r 894f003d9f5f src/lib-sieve/cmu/map.h
11271
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
11272
+++ b/src/lib-sieve/cmu/map.h Sun May 04 16:00:59 2008 +0200
11277
+extern void map_refresh(int fd, int onceonly, const char **base,
11278
+ unsigned long *len, unsigned long newlen,
11279
+ const char *name, const char *mboxname);
11281
+extern void map_free(const char **base, unsigned long *len);
11284
diff -r 894f003d9f5f src/lib-sieve/cmu/xmalloc.h
11285
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
11286
+++ b/src/lib-sieve/cmu/xmalloc.h Sun May 04 16:00:59 2008 +0200
11288
+#ifndef __XMALLOC_H
11289
+#define __XMALLOC_H
11291
+#include <stdlib.h>
11292
+#include <string.h>
11294
+#define xmalloc(n) malloc(n)
11295
+#define xrealloc(n, m) realloc(n, m)
11296
+#define xzmalloc(n) calloc(n, 1)
11297
+#define xstrdup(s) strdup(s)
11299
+/* missing headers.. */
11300
+#include <sys/types.h>
11301
+#include <netinet/in.h>
11302
+#include <regex.h>
11303
+#include <fcntl.h>
11305
+/* dovecot kludges */
11308
+/* we don't have strlcpy, but strocpy is the same except for return value */
11309
+#define strlcpy strocpy
11311
+#define lcase str_lcase
11314
diff -r 894f003d9f5f src/lib-sieve/sieve-implementation-private.h
11315
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
11316
+++ b/src/lib-sieve/sieve-implementation-private.h Sun May 04 16:00:59 2008 +0200
11318
+#ifndef __SIEVE_IMPLEMENTATION_PRIVATE_H
11319
+#define __SIEVE_IMPLEMENTATION_PRIVATE_H
11322
+#include "mail-storage.h"
11324
+struct sieve_script;
11326
+#define DUPLICATE_DEFAULT_KEEP (3600 * 24)
11328
+struct sieve_implementation_vfuncs {
11330
+ (struct sieve_script *script, bool verify_only);
11332
+ (struct sieve_script *script, void *context);
11333
+ const char *(*get_capabilities) ();
11336
+struct sieve_implementation {
11337
+ const char *name;
11338
+ struct sieve_implementation_vfuncs v;
11341
+/* Error management */
11342
+void sieve_clear_error(void);
11343
+void sieve_set_error(const char *fmt, ...);
11344
+void sieve_set_internal_error(void);
11345
+void sieve_set_critical(const char *fmt, ...);
11347
+void sieve_implementation_register(struct sieve_implementation *sieveimpl);
11349
+void sieve_implementation_unregister(struct sieve_implementation *sieveimpl);
11351
+void sieve_register_implementations(void);
11353
+const char *const *sieve_runenv_get_mail_headers
11354
+ (void *context, const char *field);
11355
+const char *sieve_runenv_get_mail_first_header
11356
+ (void *context, const char *field);
11357
+uoff_t sieve_runenv_get_mail_size
11359
+int sieve_runenv_get_envelope
11360
+ (void *context, const char *field, array_t *contents);
11362
+int sieve_runenv_is_duplicate
11363
+ (void *context, const void *id, size_t id_size);
11364
+void sieve_runenv_mark_duplicate
11365
+ (void *context, const void *id, size_t id_size, time_t interval);
11367
+int sieve_runenv_send_message
11368
+ (void *context, const char *from, const char *to, const char *subject,
11369
+ bool mime, const char *msg, const char **outmsgid);
11370
+int sieve_runenv_send_rejection
11371
+ (void *context, const char *reason);
11372
+int sieve_runenv_send_forward
11373
+ (void *context, const char *forwardto);
11374
+int sieve_runenv_mail_save
11375
+ (void *context, const char *mailbox, char **flags, unsigned int nflags);
11377
+#include "sieve-implementation.h"
11380
diff -r 894f003d9f5f src/lib-sieve/sieve-implementation.c
11381
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
11382
+++ b/src/lib-sieve/sieve-implementation.c Sun May 04 16:00:59 2008 +0200
11385
+#include "array.h"
11386
+#include "ioloop.h"
11387
+#include "sieve-implementation-private.h"
11388
+#include "sieve-implementation.h"
11392
+/* Message to show to users when critical error occurs */
11393
+#define CRITICAL_MSG \
11394
+ "Internal error occurred. Refer to server log for more information."
11395
+#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
11397
+static array_t ARRAY_DEFINE(implementations, struct sieve_implementation *);
11399
+static struct sieve_implementation *cursieveimpl = NULL;
11400
+static struct sieve_runtime_environment *runenv = NULL;
11401
+static char *sieve_error;
11403
+void sieve_deinit(void)
11405
+ if (array_is_created(&implementations))
11406
+ array_free(&implementations);
11409
+void sieve_implementation_register(struct sieve_implementation *sieveimpl)
11411
+ /* append it after the list, so the autodetection order is correct */
11412
+ array_append(&implementations, &sieveimpl, 1);
11415
+void sieve_implementation_unregister(struct sieve_implementation *sieveimpl)
11417
+ struct sieve_implementation *const *impls;
11418
+ unsigned int i, count;
11420
+ impls = array_get(&implementations, &count);
11421
+ for (i = 0; i < count; i++) {
11422
+ if (impls[i] == sieveimpl) {
11423
+ array_delete(&implementations, i, 1);
11429
+/* FIXME: Make this function dependent on ./configure
11431
+extern struct sieve_implementation cmu_sieve;
11432
+void sieve_register_implementations()
11434
+ sieve_implementation_register(&cmu_sieve);
11437
+void sieve_set_runtime_environment(struct sieve_runtime_environment *env)
11442
+void sieve_init(void)
11444
+ ARRAY_CREATE(&implementations, default_pool, struct sieve_implementation *, 8);
11446
+ sieve_register_implementations();
11449
+/* Sets the active implementation */
11450
+int sieve_set_implementation(const char *name)
11452
+ struct sieve_implementation *const *impls;
11453
+ unsigned int i, count;
11455
+ i_assert(name != NULL);
11457
+ impls = array_get(&implementations, &count);
11458
+ for (i = 0; i < count; i++) {
11459
+ if (strcasecmp(impls[i]->name, name) == 0) {
11460
+ cursieveimpl = impls[i];
11468
+void sieve_clear_error(void)
11470
+ i_free(sieve_error);
11471
+ sieve_error = NULL;
11474
+void sieve_set_error(const char *fmt, ...)
11478
+ sieve_clear_error();
11480
+ if (fmt != NULL) {
11481
+ va_start(va, fmt);
11482
+ sieve_error = i_strdup_vprintf(fmt, va);
11487
+void sieve_set_internal_error(void)
11492
+ tm = localtime(&ioloop_time);
11494
+ i_free(sieve_error);
11496
+ strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
11497
+ i_strdup(str) : i_strdup(CRITICAL_MSG);
11500
+void sieve_set_critical(const char *fmt, ...)
11504
+ sieve_clear_error();
11505
+ if (fmt != NULL) {
11506
+ va_start(va, fmt);
11507
+ i_error("%s", t_strdup_vprintf(fmt, va));
11510
+ /* critical errors may contain sensitive data, so let user
11511
+ see only "Internal error" with a timestamp to make it
11512
+ easier to look from log files the actual error message. */
11513
+ sieve_set_internal_error();
11517
+const char *sieve_get_last_error(void)
11519
+ /* We get here only in error situations, so we have to return some
11520
+ error. If storage->error is NULL, it means we forgot to set it at
11522
+ return sieve_error != NULL ? sieve_error : "Unknown error";
11525
+int sieve_compile(struct sieve_script *script, bool verify_only)
11527
+ i_assert(cursieveimpl != NULL);
11528
+ return cursieveimpl->v.compile(script, verify_only);
11531
+int sieve_run(struct sieve_script *script, void *context)
11533
+ i_assert(cursieveimpl != NULL);
11534
+ return cursieveimpl->v.run(script, context);
11537
+const char *sieve_get_capabilities(void)
11539
+ i_assert(cursieveimpl != NULL);
11540
+ return cursieveimpl->v.get_capabilities();
11543
+/* Runtime environment */
11545
+const char *const *sieve_runenv_get_mail_headers
11546
+ (void *context, const char *field)
11548
+ i_assert(runenv != NULL);
11549
+ return runenv->get_mail_headers(context, field);
11552
+const char *sieve_runenv_get_mail_first_header(void *context, const char *field)
11554
+ const char *const *list = sieve_runenv_get_mail_headers(context, field);
11555
+ return list == NULL ? NULL : list[0];
11558
+uoff_t sieve_runenv_get_mail_size
11561
+ i_assert(runenv != NULL);
11562
+ return runenv->get_mail_size(context);
11565
+int sieve_runenv_is_duplicate
11566
+ (void *context, const void *id, size_t id_size)
11568
+ i_assert(runenv != NULL);
11569
+ return runenv->is_duplicate(context, id, id_size);
11572
+void sieve_runenv_mark_duplicate
11573
+ (void *context, const void *id, size_t id_size, time_t interval)
11575
+ i_assert(runenv != NULL);
11576
+ runenv->mark_duplicate(context, id, id_size, interval);
11579
+int sieve_runenv_send_message
11580
+ (void *context, const char *from, const char *to, const char *subject,
11581
+ bool mime, const char *msg, const char **outmsgid)
11583
+ i_assert(runenv != NULL);
11585
+ return runenv->send_message
11586
+ (context, from, to, subject, mime, msg, outmsgid);
11589
+int sieve_runenv_get_envelope
11590
+ (void *context, const char *field, array_t *contents)
11592
+ i_assert(runenv != NULL);
11594
+ return runenv->get_envelope(context, field, contents);
11597
+int sieve_runenv_send_rejection
11598
+ (void *context, const char *reason)
11600
+ i_assert(runenv != NULL);
11602
+ return runenv->send_rejection(context, reason);
11605
+int sieve_runenv_send_forward(void *context, const char *forwardto)
11607
+ i_assert(runenv != NULL);
11609
+ return runenv->send_forward(context, forwardto);
11612
+int sieve_runenv_mail_save
11613
+ (void *context, const char *mailbox, char **flags, unsigned int nflags)
11615
+ i_assert(runenv != NULL);
11617
+ return runenv->mail_save(context, mailbox, flags, nflags);
11619
diff -r 894f003d9f5f src/lib-sieve/sieve-implementation.h
11620
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
11621
+++ b/src/lib-sieve/sieve-implementation.h Sun May 04 16:00:59 2008 +0200
11623
+#ifndef __SIEVE_IMPLEMENTATION_H
11624
+#define __SIEVE_IMPLEMENTATION_H
11626
+struct sieve_script;
11628
+struct sieve_runtime_environment {
11629
+ const char *const *(*get_mail_headers)
11630
+ (void *context, const char *field);
11631
+ uoff_t (*get_mail_size)
11633
+ int (*get_envelope)
11634
+ (void *context, const char *field, array_t *contents);
11636
+ int (*is_duplicate)
11637
+ (void *context, const void *id, size_t id_size);
11638
+ void (*mark_duplicate)
11639
+ (void *context, const void *id, size_t id_size, time_t interval);
11641
+ int (*send_message)
11642
+ (void *context, const char *from, const char *to, const char *subject,
11643
+ bool mime, const char *msg, const char **outmsgid);
11644
+ int (*send_rejection)
11645
+ (void *context, const char *reason);
11646
+ int (*send_forward)
11647
+ (void *context, const char *forwardto);
11649
+ (void *context, const char *mailbox, char **flags, unsigned int nflags);
11652
+void sieve_init(void);
11653
+void sieve_deinit(void);
11655
+int sieve_set_implementation(const char *name);
11657
+void sieve_set_runtime_environment(struct sieve_runtime_environment *env);
11659
+const char *sieve_get_last_error(void);
11661
+int sieve_compile(struct sieve_script *script, bool verify_only);
11663
+int sieve_run(struct sieve_script *script, void *context);
11665
+const char *sieve_get_capabilities(void);
11669
diff -r 894f003d9f5f src/lib-sievestorage/Makefile.am
11670
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
11671
+++ b/src/lib-sievestorage/Makefile.am Sun May 04 16:00:59 2008 +0200
11673
+noinst_LIBRARIES = libsievestorage.a
11676
+ -I$(top_srcdir)/src/lib \
11677
+ -I$(top_srcdir)/src/lib-mail \
11678
+ -I$(dovecotsievedir)/src/libsieve
11680
+libsievestorage_a_SOURCES = \
11686
+noinst_HEADERS = \
11690
+ sieve-storage.h \
11691
+ sieve-storage-private.h
11692
diff -r 894f003d9f5f src/lib-sievestorage/sieve-list.c
11693
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
11694
+++ b/src/lib-sievestorage/sieve-list.c Sun May 04 16:00:59 2008 +0200
11698
+#include "sieve-storage-private.h"
11699
+#include "sieve-script.h"
11700
+#include "sieve-list.h"
11702
+#include <stdio.h>
11703
+#include <stdlib.h>
11704
+#include <sys/types.h>
11705
+#include <dirent.h>
11706
+#include <sys/stat.h>
11708
+struct sieve_list_context {
11710
+ struct sieve_storage *storage;
11712
+ const char *active;
11716
+ unsigned int seen_active:1; // Just present for assertions
11719
+struct sieve_list_context *sieve_storage_list_init
11720
+ (struct sieve_storage *storage)
11722
+ struct sieve_list_context *ctx;
11723
+ const char *active;
11727
+ /* Open the directory */
11728
+ if ( (dirp = opendir(storage->dir)) == NULL ) {
11729
+ sieve_storage_set_critical(storage, "opendir(%s) failed: %m",
11736
+ /* Get the name of the active script */
11737
+ if ( (active = sieve_storage_get_active_scriptname(storage))
11743
+ pool = pool_alloconly_create("sieve_list_context", 4096);
11744
+ ctx = p_new(pool, struct sieve_list_context, 1);
11745
+ ctx->pool = pool;
11746
+ ctx->storage = storage;
11747
+ ctx->dirp = dirp;
11748
+ ctx->active = p_strdup(pool, active);
11749
+ ctx->seen_active = FALSE;
11756
+const char *sieve_storage_list_next
11757
+ (struct sieve_list_context *ctx, bool *active)
11759
+ const struct sieve_storage *storage = ctx->storage;
11760
+ struct dirent *dp;
11761
+ const char *scriptname;
11766
+ if ( (dp = readdir(ctx->dirp)) == NULL )
11769
+ scriptname = sieve_storage_file_get_scriptname
11770
+ (storage, dp->d_name);
11772
+ if (scriptname != NULL ) {
11773
+ /* Don't list our active sieve script link if the link
11774
+ * resides in the script dir (generally a bad idea).
11776
+ if ( *(storage->link_path) == '\0' &&
11777
+ strcmp(storage->active_fname, dp->d_name) == 0 )
11784
+ if ( ctx->active != NULL &&
11785
+ strcmp(scriptname, ctx->active) == 0 ) {
11787
+ ctx->active = NULL;
11790
+ return scriptname;
11793
+int sieve_storage_list_deinit(struct sieve_list_context **ctx)
11795
+ if (closedir((*ctx)->dirp) < 0) {
11796
+ sieve_storage_set_critical((*ctx)->storage, "closedir(%s) failed: %m",
11797
+ (*ctx)->storage->dir);
11799
+ pool_unref((*ctx)->pool);
11804
+ pool_unref((*ctx)->pool);
11812
diff -r 894f003d9f5f src/lib-sievestorage/sieve-list.h
11813
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
11814
+++ b/src/lib-sievestorage/sieve-list.h Sun May 04 16:00:59 2008 +0200
11816
+#ifndef __SIEVE_LIST_H
11817
+#define __SIEVE_LIST_H
11821
+#include "sieve-storage.h"
11822
+#include "sieve-list.h"
11824
+#include <stdio.h>
11825
+#include <stdlib.h>
11826
+#include <dirent.h>
11827
+#include <sys/stat.h>
11829
+struct sieve_list_context;
11831
+/* Create a context for listing the scripts in the storage */
11832
+struct sieve_list_context *sieve_storage_list_init
11833
+ (struct sieve_storage *storage);
11835
+/* Get the next script in the storage. */
11836
+const char *sieve_storage_list_next(struct sieve_list_context *ctx, bool *active);
11838
+/* Destroy the listing context */
11839
+int sieve_storage_list_deinit(struct sieve_list_context **ctx);
11846
diff -r 894f003d9f5f src/lib-sievestorage/sieve-save.c
11847
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
11848
+++ b/src/lib-sievestorage/sieve-save.c Sun May 04 16:00:59 2008 +0200
11851
+#include "hostpid.h"
11852
+#include "ioloop.h"
11853
+#include "array.h"
11854
+#include "buffer.h"
11855
+#include "ostream.h"
11856
+#include "ostream-crlf.h"
11858
+#include "sieve-storage-private.h"
11859
+#include "sieve-save.h"
11860
+#include "sieve-script.h"
11862
+#include <stdio.h>
11863
+#include <stdlib.h>
11864
+#include <unistd.h>
11865
+#include <fcntl.h>
11866
+#include <utime.h>
11867
+#include <sys/stat.h>
11869
+struct sieve_save_context {
11872
+ struct sieve_storage *storage;
11873
+ const char *scriptname;
11874
+ struct sieve_script *scriptobject;
11876
+ struct istream *input;
11877
+ struct ostream *output;
11879
+ const char *tmp_path;
11881
+ unsigned int failed:1;
11882
+ unsigned int moving:1;
11883
+ unsigned int finished:1;
11886
+static const char *sieve_generate_tmp_filename(const char *scriptname, const struct timeval *tv)
11888
+ static unsigned int create_count = 0;
11889
+ static time_t first_stamp = 0;
11891
+ if (first_stamp == 0 || first_stamp == ioloop_time) {
11892
+ /* it's possible that within last second another process had
11893
+ the same PID as us. Use usecs to make sure we don't create
11894
+ duplicate base name. */
11895
+ first_stamp = ioloop_time;
11896
+ return t_strdup_printf
11897
+ ("%s-%s.P%sQ%uM%s.%s",
11899
+ dec2str(tv->tv_sec), my_pid,
11901
+ dec2str(tv->tv_usec), my_hostname);
11903
+ /* Don't bother with usecs. Saves a bit space :) */
11904
+ return t_strdup_printf
11905
+ ("%s-%s.P%sQ%u.%s",
11907
+ dec2str(tv->tv_sec), my_pid,
11908
+ create_count++, my_hostname);
11912
+static int sieve_create_tmp
11913
+(struct sieve_storage *storage, const char *scriptname, const char **fpath_r)
11915
+ const char *path, *tmp_fname;
11917
+ struct timeval *tv, tv_now;
11921
+ tv = &ioloop_timeval;
11922
+ pool = pool_alloconly_create("script_tmp", 4096);
11925
+ tmp_fname = sieve_generate_tmp_filename(scriptname, tv);
11927
+ path = p_strconcat(pool, storage->dir, "/tmp/", tmp_fname, ".sieve", NULL);
11928
+ if ( stat(path, &st) < 0 ) {
11929
+ if ( errno == ENOENT) {
11930
+ /* NICE! doesn't exist */
11931
+ fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
11933
+ /* Something went wrong */
11934
+ if (fd != -1 || errno != EEXIST)
11937
+ sieve_storage_set_critical(storage,
11938
+ "stat(%s) failed: %m", path);
11943
+ /* Wait and try again - very unlikely */
11946
+ if (gettimeofday(&tv_now, NULL) < 0)
11947
+ i_fatal("gettimeofday(): %m");
11950
+ *fpath_r = t_strdup(path);
11952
+ if (ENOSPACE(errno)) {
11953
+ sieve_storage_set_error(storage,
11954
+ "Not enough disk space");
11956
+ sieve_storage_set_critical(storage,
11957
+ "open(%s) failed: %m", path);
11961
+ pool_unref(pool);
11965
+static int sieve_script_move(struct sieve_save_context *ctx,
11972
+ /* Using rename() to ensure existing files are replaced
11973
+ * without conflicts with other processes using the same
11974
+ * file. The kernel wont fully delete the original until
11975
+ * all processes have closed the file.
11977
+ if (rename(ctx->tmp_path, dst) == 0)
11981
+ if (ENOSPACE(errno)) {
11982
+ sieve_storage_set_error
11983
+ (ctx->storage, "Not enough disk space");
11985
+ sieve_storage_set_critical
11986
+ (ctx->storage, "link(%s, %s) failed: %m", ctx->tmp_path, dst);
11990
+ /* Always destroy temp file */
11991
+ (void)unlink(ctx->tmp_path);
11997
+struct sieve_save_context *
11998
+sieve_storage_save_init(struct sieve_storage *storage,
11999
+ const char *scriptname, struct istream *input)
12001
+ struct sieve_save_context *ctx;
12003
+ struct ostream *output;
12004
+ const char *path;
12006
+ /* Prevent overwriting the active script link when it resides in the
12007
+ * sieve storage directory.
12009
+ if ( *(storage->link_path) == '\0' ) {
12010
+ const char *svext;
12013
+ svext = strrchr(storage->active_fname, '.');
12014
+ namelen = svext - storage->active_fname;
12015
+ if ( svext != NULL && strncmp(svext+1, "sieve", 5) == 0 &&
12016
+ strlen(scriptname) == namelen &&
12017
+ strncmp(scriptname, storage->active_fname, namelen) == 0 )
12019
+ sieve_storage_set_error(
12020
+ storage, "Script name '%s' is reserved for internal use.",
12026
+ pool = pool_alloconly_create("sieve_save_context", 4096);
12027
+ ctx = p_new(pool, struct sieve_save_context, 1);
12028
+ ctx->pool = pool;
12029
+ ctx->storage = storage;
12030
+ ctx->scriptname = scriptname;
12031
+ ctx->scriptobject = NULL;
12035
+ ctx->fd = sieve_create_tmp(storage, scriptname, &path);
12036
+ if (ctx->fd == -1) {
12037
+ ctx->failed = TRUE;
12039
+ pool_unref(pool);
12043
+ ctx->input = input;
12045
+ output = o_stream_create_file(ctx->fd, system_pool, 0, FALSE);
12046
+ /*ctx->output = (storage->flags & SIEVE_STORAGE_FLAG_SAVE_CRLF) != 0 ?
12047
+ o_stream_create_crlf(default_pool, output) :
12048
+ o_stream_create_lf(default_pool, output);
12049
+ o_stream_unref(&output);*/
12050
+ ctx->output = output;
12052
+ ctx->tmp_path = p_strdup(pool, path);
12053
+ ctx->failed = FALSE;
12059
+int sieve_storage_save_continue(struct sieve_save_context *ctx)
12061
+ if (o_stream_send_istream(ctx->output, ctx->input) < 0) {
12062
+ sieve_storage_set_critical(ctx->storage,
12063
+ "o_stream_send_istream(%s) failed: %m", ctx->tmp_path);
12064
+ ctx->failed = TRUE;
12070
+int sieve_storage_save_finish(struct sieve_save_context *ctx)
12072
+ int output_errno;
12074
+ ctx->finished = TRUE;
12075
+ if (ctx->failed && ctx->fd == -1) {
12076
+ /* tmp file creation failed */
12081
+ output_errno = ctx->output->stream_errno;
12082
+ o_stream_destroy(&ctx->output);
12084
+ if (fsync(ctx->fd) < 0) {
12085
+ sieve_storage_set_critical(ctx->storage,
12086
+ "fsync(%s) failed: %m", ctx->tmp_path);
12087
+ ctx->failed = TRUE;
12089
+ if (close(ctx->fd) < 0) {
12090
+ sieve_storage_set_critical(ctx->storage,
12091
+ "close(%s) failed: %m", ctx->tmp_path);
12092
+ ctx->failed = TRUE;
12096
+ if (ctx->failed) {
12097
+ /* delete the tmp file */
12098
+ if (unlink(ctx->tmp_path) < 0 && errno != ENOENT)
12099
+ i_warning("Unlink(%s) failed: %m", ctx->tmp_path);
12101
+ errno = output_errno;
12102
+ if (ENOSPACE(errno)) {
12103
+ sieve_storage_set_error(ctx->storage,
12104
+ "Not enough disk space");
12105
+ } else if (errno != 0) {
12106
+ sieve_storage_set_critical(ctx->storage,
12107
+ "write(%s) failed: %m", ctx->tmp_path);
12118
+static void sieve_storage_save_destroy(struct sieve_save_context *ctx)
12120
+ if (ctx->scriptobject != NULL)
12121
+ sieve_script_unref(&(ctx->scriptobject));
12123
+ pool_unref(ctx->pool);
12126
+struct sieve_script *sieve_storage_save_get_tempscript
12127
+ (struct sieve_save_context *ctx)
12132
+ ctx->scriptobject = sieve_script_init_from_file(ctx->storage, ctx->scriptname,
12133
+ ctx->tmp_path, NULL);
12135
+ return ctx->scriptobject;
12138
+int sieve_storage_save_commit(struct sieve_save_context *ctx)
12140
+ const char *dest_path;
12141
+ bool failed = FALSE;
12143
+ i_assert(ctx->output == NULL);
12144
+ i_assert(ctx->finished);
12148
+ dest_path = t_strconcat(ctx->storage->dir, "/", ctx->scriptname, ".sieve", NULL);
12150
+ failed = !sieve_script_move(ctx, dest_path);
12154
+ sieve_storage_save_destroy(ctx);
12159
+void sieve_storage_save_abort(struct sieve_save_context *ctx)
12161
+ ctx->failed = TRUE;
12163
+ if (!ctx->finished)
12164
+ (void)sieve_storage_save_finish(ctx);
12166
+ (void)unlink(ctx->tmp_path);
12168
+ i_assert(ctx->output == NULL);
12170
+ sieve_storage_save_destroy(ctx);
12172
diff -r 894f003d9f5f src/lib-sievestorage/sieve-save.h
12173
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
12174
+++ b/src/lib-sievestorage/sieve-save.h Sun May 04 16:00:59 2008 +0200
12176
+#ifndef __SIEVE_SAVE_H
12177
+#define __SIEVE_SAVE_H
12179
+#include "sieve-storage.h"
12181
+struct sieve_save_context;
12183
+struct sieve_save_context *
12184
+sieve_storage_save_init(struct sieve_storage *storage,
12185
+ const char *scriptname, struct istream *input);
12187
+int sieve_storage_save_continue(struct sieve_save_context *ctx);
12189
+int sieve_storage_save_finish(struct sieve_save_context *ctx);
12191
+struct sieve_script *sieve_storage_save_get_tempscript
12192
+ (struct sieve_save_context *ctx);
12194
+void sieve_storage_save_abort(struct sieve_save_context *ctx);
12196
+int sieve_storage_save_commit(struct sieve_save_context *ctx);
12200
diff -r 894f003d9f5f src/lib-sievestorage/sieve-script.c
12201
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
12202
+++ b/src/lib-sievestorage/sieve-script.c Sun May 04 16:00:59 2008 +0200
12205
+#include "mempool.h"
12206
+#include "hostpid.h"
12207
+#include "ioloop.h"
12208
+#include "istream.h"
12209
+#include "file-copy.h"
12211
+#include "sieve-storage.h"
12212
+#include "sieve-storage-private.h"
12213
+#include "sieve-script.h"
12215
+#include <stdio.h>
12216
+#include <stdlib.h>
12217
+#include <unistd.h>
12218
+#include <sys/stat.h>
12219
+#include <ctype.h>
12221
+#include <fcntl.h>
12223
+struct sieve_script *sieve_script_init_from_file
12224
+ (struct sieve_storage *storage, const char *scriptname,
12225
+ const char *filename, bool *exists)
12230
+ struct sieve_script *script = NULL;
12232
+ /* Prevent initializing the active script link as a script when it
12233
+ * resides in the sieve storage directory.
12235
+ if ( *(storage->link_path) == '\0' ) {
12236
+ const char *fname;
12238
+ fname = strrchr(filename, '/');
12239
+ if ( fname == NULL )
12240
+ fname = filename;
12244
+ if ( strcmp(fname, storage->active_fname) == 0 ) {
12245
+ if ( exists != NULL )
12251
+ /* Perform existance check if required */
12252
+ if (exists != NULL) {
12255
+ ret = stat(filename, &st);
12258
+ if ( errno == ENOENT ) {
12262
+ sieve_storage_set_critical
12264
+ "Performing stat() on sieve file '%s' failed: %m", filename);
12268
+ if ( !S_ISREG(st.st_mode) ) {
12269
+ sieve_storage_set_critical
12270
+ (storage, "Sieve file '%s' is not a regular file.", filename);
12274
+ /* Script found */
12279
+ pool = pool_alloconly_create("sieve_script", 4096);
12280
+ script = p_new(pool, struct sieve_script, 1);
12281
+ script->pool = pool;
12282
+ script->refcount = 1;
12283
+ script->name = p_strdup(pool, scriptname);
12284
+ script->storage = storage;
12285
+ script->size = st.st_size;
12287
+ script->filename = p_strdup(pool, filename);
12288
+ script->istream = NULL;
12293
+struct sieve_script *sieve_script_init
12294
+ (struct sieve_storage *storage, const char *scriptname, bool *exists)
12296
+ struct sieve_script *script;
12297
+ const char *filename;
12301
+ filename = t_strconcat
12302
+ ( storage->dir, "/", scriptname, ".sieve", NULL );
12304
+ script = sieve_script_init_from_file(storage, scriptname, filename, exists);
12311
+void sieve_script_ref(struct sieve_script *script)
12313
+ script->refcount++;
12316
+void sieve_script_unref(struct sieve_script **script)
12318
+ i_assert((*script)->refcount > 0);
12320
+ if (--(*script)->refcount != 0)
12323
+ if ((*script)->istream != NULL )
12324
+ i_stream_unref(&(*script)->istream);
12326
+ pool_unref((*script)->pool);
12331
+const char *sieve_script_name(struct sieve_script *script)
12333
+ return script->name;
12336
+/* sieve_script_filename():
12337
+ * Returns the filename of the script. If the implemented storage
12338
+ * does not actually store scripts as a file a copy must be put
12339
+ * somewhere in /tmp or so. Don't use this function if you can
12340
+ * also work with a stream.
12342
+const char *sieve_script_filename(struct sieve_script *script)
12344
+ return script->filename;
12348
+sieve_storage_open_fd(struct sieve_storage *storage, const char *path, int *fd)
12350
+ *fd = open(path, O_RDONLY);
12353
+ if (errno == ENOENT)
12356
+ sieve_storage_set_critical(storage,
12357
+ "open(%s) failed: %m", path);
12361
+int sieve_script_get_size(struct sieve_script *script, uoff_t *size)
12367
+ if (script->size == 0) {
12370
+ ret = stat(script->filename, &st);
12373
+ if ( errno == ENOENT ) {
12377
+ sieve_storage_set_critical
12378
+ (script->storage,
12379
+ "Performing stat() on sieve file '%s' failed: %m", script->filename);
12384
+ script->size = st.st_size;
12387
+ *size = script->size;
12393
+sieve_script_open(struct sieve_script *script, bool *deleted_r)
12395
+ const struct stat *statbuf;
12396
+ int ret = 0, fd = -1;
12398
+ if ( deleted_r != NULL )
12399
+ *deleted_r = FALSE;
12401
+ if (script->istream == NULL) {
12402
+ if ( (ret=sieve_storage_open_fd
12403
+ (script->storage, script->filename, &fd)) < 0)
12407
+ if ( deleted_r != NULL )
12408
+ *deleted_r = TRUE;
12412
+ script->istream =
12413
+ i_stream_create_file(fd, default_pool,
12414
+ SIEVE_READ_BLOCK_SIZE, TRUE);
12416
+ if ( script->istream != NULL ) {
12417
+ statbuf = i_stream_stat(script->istream, 0);
12418
+ script->size = statbuf->st_size;
12422
+ return script->istream;
12425
+const char *sieve_storage_file_get_scriptname
12426
+ (const struct sieve_storage *storage __attr_unused__, const char *filename)
12430
+ ext = strrchr(filename, '.');
12432
+ if ( ext == NULL || ext == filename || strncmp(ext,".sieve",6) != 0 )
12435
+ return t_strdup_until(filename, ext);
12438
+static const char *sieve_storage_read_active_link
12439
+ (struct sieve_storage *storage, bool *not_link)
12441
+ char linkbuf[PATH_MAX];
12444
+ if ( not_link != NULL )
12445
+ *not_link = FALSE;
12447
+ ret = readlink(storage->active_path, linkbuf, sizeof(linkbuf));
12450
+ if (errno == EINVAL) {
12451
+ /* Our symlink is no symlink. Report 'no active script'.
12452
+ * Activating a script will automatically resolve this, so
12453
+ * there is no need to panic on this one.
12456
+ ("Active sieve script symlink %s is no symlink.",
12457
+ storage->active_path);
12458
+ if ( not_link != NULL )
12459
+ *not_link = TRUE;
12463
+ if (errno != ENOENT ) {
12464
+ /* We do need to panic otherwise */
12465
+ sieve_storage_set_critical
12467
+ "Performing readlink() on active sieve symlink '%s' failed: %m",
12468
+ storage->active_path);
12475
+ /* ret is now assured to be valid, i.e. > 0 */
12476
+ return t_strndup(linkbuf, ret);
12479
+static const char *sieve_storage_parse_link
12480
+ (struct sieve_storage *storage, const char *link)
12482
+ const char *fname, *scriptname, *scriptpath;
12484
+ /* Split link into path and filename */
12485
+ fname = strrchr(link, '/');
12486
+ if ( fname != NULL ) {
12487
+ scriptpath = t_strdup_until(link, fname+1);
12494
+ /* Check the script name */
12495
+ scriptname = sieve_storage_file_get_scriptname(storage, fname);
12497
+ /* Warn if link is deemed to be invalid */
12498
+ if ( scriptname == NULL ) {
12500
+ ("Active sieve script symlink %s is broken: "
12501
+ "invalid scriptname (points to %s).",
12502
+ storage->active_path, link);
12506
+ /* Check whether the path is any good */
12507
+ if ( strcmp(scriptpath, storage->link_path) != 0 &&
12508
+ strcmp(scriptpath, storage->dir) != 0 ) {
12510
+ ("Active sieve script symlink %s is broken: "
12511
+ "invalid/unknown path to storage (points to %s).",
12512
+ storage->active_path, link);
12516
+ return scriptname;
12519
+const char *sieve_storage_get_active_scriptname
12520
+ (struct sieve_storage *storage)
12522
+ const char *link, *scriptname;
12524
+ /* Read the active link */
12525
+ link = sieve_storage_read_active_link(storage, NULL);
12527
+ if ( link == NULL || *link == '\0' )
12530
+ /* Parse the link */
12531
+ scriptname = sieve_storage_parse_link(storage, link);
12533
+ if (scriptname == NULL) {
12534
+ /* Obviously someone has been playing with our symlink,
12535
+ * ignore this situation and report 'no active script'.
12536
+ * Activation should fix this situation.
12541
+ return scriptname;
12544
+struct sieve_script *
12545
+ sieve_storage_get_active_script(struct sieve_storage *storage, bool *no_active)
12547
+ bool exists, no_link;
12548
+ struct sieve_script *script;
12549
+ const char *scriptname, *link;
12551
+ *no_active = FALSE;
12553
+ /* Read the active link */
12554
+ link = sieve_storage_read_active_link(storage, &no_link);
12556
+ if ( link == NULL )
12560
+ if ( *link == '\0' )
12563
+ /* Try to open the active_path as a regular file */
12564
+ return sieve_script_init_from_file
12565
+ (storage, ".dovecot", storage->active_path, NULL);
12568
+ *no_active = TRUE;
12572
+ /* Parse the link */
12573
+ scriptname = sieve_storage_parse_link(storage, link);
12575
+ if (scriptname == NULL) {
12576
+ /* Obviously someone has been playing with our symlink,
12577
+ * ignore this situation and report 'no active script'.
12579
+ *no_active = TRUE;
12584
+ script = sieve_script_init(storage, scriptname, &exists);
12588
+ ("Active sieve script symlink %s "
12589
+ "points to non-existent script (points to %s).",
12590
+ storage->active_path, link);
12593
+ *no_active = !exists;
12597
+int sieve_script_is_active(struct sieve_script *script)
12599
+ const char *aname;
12603
+ aname = sieve_storage_get_active_scriptname(script->storage);
12605
+ if (aname == NULL) {
12606
+ /* Critical error */
12611
+ /* Is the requested script active? */
12612
+ if ( strcmp(script->name, aname) == 0 ) {
12621
+int sieve_script_delete(struct sieve_script **script)
12623
+ struct sieve_storage *storage = (*script)->storage;
12627
+ /* Is the requested script active? */
12628
+ if ( sieve_script_is_active(*script) ) {
12629
+ sieve_storage_set_error(storage, "Cannot delete the active sieve script.");
12632
+ ret = unlink((*script)->filename);
12635
+ if ( errno == ENOENT )
12636
+ sieve_storage_set_error(storage, "Sieve script does not exist.");
12638
+ sieve_storage_set_critical(
12639
+ storage, "Performing unlink() failed on sieve file '%s': %m",
12640
+ (*script)->filename);
12644
+ /* Always deinitialize the script object */
12645
+ sieve_script_unref(script);
12650
+static bool sieve_storage_rescue_regular_file(struct sieve_storage *storage)
12654
+ /* Stat the file */
12655
+ if ( lstat(storage->active_path, &st) != 0 ) {
12656
+ if ( errno != ENOENT ) {
12657
+ sieve_storage_set_critical(storage,
12658
+ "Failed to stat active sieve script symlink (%s): %m.",
12659
+ storage->active_path);
12665
+ if ( S_ISLNK( st.st_mode ) ) {
12666
+ if ( getenv("DEBUG") != NULL )
12667
+ i_info( "sieve-storage: nothing to rescue %s.", storage->active_path);
12668
+ return TRUE; /* Nothing to rescue */
12671
+ /* Only regular files can be rescued */
12672
+ if ( S_ISREG( st.st_mode ) ) {
12673
+ const char *dstpath;
12677
+ dstpath = t_strconcat
12678
+ ( storage->dir, "/dovecot.orig.sieve", NULL );
12679
+ if ( file_copy(storage->active_path, dstpath, 1) < 1 ) {
12680
+ sieve_storage_set_critical(storage,
12681
+ "Active sieve script file '%s' is a regular file and copying it to the "
12682
+ "script storage as '%s' failed. This needs to be fixed manually.",
12683
+ storage->active_path, dstpath);
12687
+ i_info("Moved active sieve script file '%s' to script storage as '%s'.",
12688
+ storage->active_path, dstpath);
12695
+ sieve_storage_set_critical( storage,
12696
+ "Active sieve script file '%s' is no symlink nor a regular file. "
12697
+ "This needs to be fixed manually.", storage->active_path );
12701
+int sieve_storage_deactivate(struct sieve_storage *storage)
12705
+ if ( !sieve_storage_rescue_regular_file(storage) )
12708
+ /* Delete the symlink, so no script is active */
12709
+ ret = unlink(storage->active_path);
12712
+ if ( errno != ENOENT ) {
12713
+ sieve_storage_set_error(storage, "sieve_storage_deactivate(): "
12714
+ "error on unlink(%s): %m", storage->active_path);
12724
+sieve_script_activate(struct sieve_script *script)
12727
+ const char *active_path_new, *script_path;
12728
+ struct timeval *tv, tv_now;
12729
+ const char *aname;
12730
+ int activated = 0;
12735
+ /* Find out whether there is an active script, but recreate
12736
+ * the symlink either way. This way, any possible error in the symlink
12737
+ * resolves automatically. This step is only necessary to provide a
12738
+ * proper return value indicating whether the script was already active.
12740
+ aname = sieve_storage_get_active_scriptname(script->storage);
12742
+ /* Is the requested script already active? */
12743
+ if ( aname == NULL || strcmp(script->name, aname) != 0 )
12746
+ /* Check the scriptfile we are trying to activate */
12747
+ if ( lstat(script->filename, &st) != 0 ) {
12748
+ sieve_storage_set_critical(script->storage,
12749
+ "Stat on sieve script %s failed, but it is to be activated: %m.", script->name);
12754
+ /* Rescue a possible .dovecot.sieve regular file remaining from old
12757
+ if ( !sieve_storage_rescue_regular_file(script->storage) ) {
12758
+ /* Rescue failed, manual intervention is necessary */
12763
+ /* Just try to create the symlink first */
12764
+ script_path = t_strconcat
12765
+ ( script->storage->link_path, script->name, ".sieve", NULL );
12767
+ ret = symlink(script_path, script->storage->active_path);
12770
+ if ( errno == EEXIST ) {
12771
+ /* The symlink already exists, try to replace it */
12772
+ tv = &ioloop_timeval;
12775
+ /* First the new symlink is created with a different filename */
12776
+ active_path_new = t_strdup_printf
12777
+ ("%s-new.%s.P%sM%s.%s.sieve",
12778
+ script->storage->active_path,
12779
+ dec2str(tv->tv_sec), my_pid,
12780
+ dec2str(tv->tv_usec), my_hostname);
12782
+ ret = symlink(script_path, active_path_new);
12785
+ /* If link exists we try again later */
12786
+ if ( errno == EEXIST ) {
12787
+ /* Wait and try again - very unlikely */
12790
+ if (gettimeofday(&tv_now, NULL) < 0)
12791
+ i_fatal("gettimeofday(): %m");
12795
+ /* Other error, critical */
12796
+ sieve_storage_set_critical
12797
+ (script->storage,
12798
+ "Creating symlink() %s to %s failed: %m",
12799
+ active_path_new, script_path);
12804
+ /* Link created */
12808
+ /* Replace the existing link and thus activating the new script */
12809
+ ret = rename(active_path_new, script->storage->active_path);
12812
+ /* Failed; created symlink must be deleted */
12813
+ (void)unlink(active_path_new);
12814
+ sieve_storage_set_critical
12815
+ (script->storage,
12816
+ "Performing rename() %s to %s failed: %m",
12817
+ active_path_new, script->storage->active_path);
12822
+ /* Other error, critical */
12823
+ sieve_storage_set_critical
12824
+ (script->storage,
12825
+ "Creating symlink() %s to %s failed: %m",
12826
+ script->storage->active_path, script_path);
12833
+ return activated;
12838
diff -r 894f003d9f5f src/lib-sievestorage/sieve-script.h
12839
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
12840
+++ b/src/lib-sievestorage/sieve-script.h Sun May 04 16:00:59 2008 +0200
12842
+#ifndef __SIEVE_FILE_H
12843
+#define __SIEVE_FILE_H
12845
+#include "sieve-storage.h"
12847
+struct sieve_script;
12849
+struct sieve_script *sieve_script_init
12850
+ (struct sieve_storage *storage, const char *scriptname, bool *exists);
12852
+void sieve_script_ref(struct sieve_script *script);
12854
+void sieve_script_unref(struct sieve_script **script);
12856
+const char *sieve_script_name(struct sieve_script *script);
12858
+/* sieve_script_filename():
12859
+ * Returns the filename of the script. If the implemented storage
12860
+ * does not actually store scripts as a file it must temporarily be saved
12861
+ * somewhere in /tmp or so. Don't use this function if you can
12862
+ * also work with a stream. (currently not an issue)
12864
+const char *sieve_script_filename(struct sieve_script *script);
12866
+int sieve_script_get_size(struct sieve_script *script, uoff_t *size);
12869
+sieve_script_open(struct sieve_script *script, bool *deleted_r);
12871
+const char *sieve_storage_file_get_scriptname
12872
+ (const struct sieve_storage *storage, const char *filename);
12875
+ sieve_storage_get_active_scriptname(struct sieve_storage *storage);
12877
+struct sieve_script *
12878
+ sieve_storage_get_active_script(struct sieve_storage *storage, bool *no_active);
12880
+int sieve_script_is_active(struct sieve_script *script);
12882
+int sieve_script_delete(struct sieve_script **script);
12884
+int sieve_storage_deactivate(struct sieve_storage *storage);
12887
+sieve_script_activate(struct sieve_script *script);
12891
diff -r 894f003d9f5f src/lib-sievestorage/sieve-storage-private.h
12892
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
12893
+++ b/src/lib-sievestorage/sieve-storage-private.h Sun May 04 16:00:59 2008 +0200
12895
+#ifndef __SIEVE_STORAGE_PRIVATE_H
12896
+#define __SIEVE_STORAGE_PRIVATE_H
12898
+#include "sieve-storage.h"
12900
+enum sieve_storage_flags {
12901
+ /* Print debugging information while initializing the storage */
12902
+ SIEVE_STORAGE_FLAG_DEBUG = 0x01,
12903
+ /* Use CRLF linefeeds when saving mails. */
12904
+ SIEVE_STORAGE_FLAG_SAVE_CRLF = 0x02,
12907
+#define SIEVE_READ_BLOCK_SIZE (1024*8)
12909
+/* All methods returning int return either TRUE or FALSE. */
12910
+struct sieve_storage {
12916
+ char *active_path;
12917
+ char *active_fname;
12920
+ char *user; /* name of user accessing the storage */
12922
+ enum sieve_storage_flags flags;
12925
+struct sieve_script {
12929
+ struct sieve_storage *storage;
12930
+ const char *name;
12931
+ struct istream *istream;
12932
+ const char *filename;
12936
+struct sieve_script *sieve_script_init_from_file
12937
+ (struct sieve_storage *storage, const char *scriptname,
12938
+ const char *filename, bool *exists);
12942
diff -r 894f003d9f5f src/lib-sievestorage/sieve-storage.c
12943
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
12944
+++ b/src/lib-sievestorage/sieve-storage.c Sun May 04 16:00:59 2008 +0200
12947
+#include "home-expand.h"
12948
+#include "ioloop.h"
12949
+#include "mkdir-parents.h"
12950
+#include "sieve-storage-private.h"
12952
+#include <stdio.h>
12953
+#include <stdlib.h>
12954
+#include <unistd.h>
12955
+#include <sys/stat.h>
12956
+#include <ctype.h>
12959
+#define SIEVE_SCRIPT_PATH "~/.dovecot.sieve"
12961
+#define CREATE_MODE 0770 /* umask() should limit it more */
12963
+#define CRITICAL_MSG \
12964
+ "Internal error occured. Refer to server log for more information."
12965
+#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
12967
+static const char *sieve_get_active_script_path(void)
12969
+ const char *script_path, *home;
12971
+ home = getenv("HOME");
12973
+ /* userdb may specify Sieve path */
12974
+ script_path = getenv("SIEVE");
12975
+ if (script_path != NULL) {
12976
+ if (*script_path == '\0') {
12981
+ if ( *script_path != '/' && *script_path != '~') {
12982
+ /* relative path. change to absolute. */
12983
+ script_path = t_strconcat(getenv("HOME"), "/",
12984
+ script_path, NULL);
12987
+ if (home == NULL) {
12988
+ /* we must have a home directory */
12989
+ i_error("sieve-storage: userdb(%s) didn't return a home directory or "
12990
+ "sieve script location, can't find it",
12995
+ script_path = SIEVE_SCRIPT_PATH;
12998
+ /* No need to check for existance here */
13000
+ return script_path;
13003
+/* Obtain the directory for script storage from the mail location
13005
+static const char *sieve_storage_get_dir_from_mail(const char *data)
13007
+ bool debug = (getenv("DEBUG") != NULL);
13010
+ const char *root_dir, *dir, *p, *d;
13012
+ root_dir = dir = d = NULL;
13015
+ i_info("sieve-storage: using mail-data: %s", data);
13017
+ /* check if we're in the form of mailformat:data
13018
+ (eg. maildir:Maildir) */
13020
+ while (i_isalnum(*p)) p++;
13028
+ if (d == NULL || *d == '\0') {
13029
+ /* Ok, this is bad. Check whether we might be chrooted, bail out otherwise */
13030
+ if (access("/sieve", R_OK|W_OK|X_OK) == 0)
13033
+ i_error("sieve-storage: sieve storage directory not given "
13034
+ "and mail root provides no alternative.");
13038
+ /* <scriptdir> */
13039
+ p = strchr(d, ':');
13041
+ /* No additional parameters */
13044
+ dir = t_strdup_until(d, p);
13048
+ /* Use control dir as script dir if specified */
13049
+ if (strncmp(p, "CONTROL=", 8) == 0)
13050
+ root_dir = t_strcut(p+8, ':');
13051
+ p = strchr(p, ':');
13052
+ } while (p != NULL);
13054
+ if ( root_dir == NULL || *root_dir == '\0' )
13060
+ if ( root_dir == NULL || *root_dir == '\0' ) {
13062
+ i_info("sieve-storage: couldn't find root dir from mail-data.");
13066
+ /* Strip trailing '/' */
13067
+ len = strlen(root_dir);
13068
+ if (root_dir[len-1] == '/')
13069
+ root_dir = t_strndup(root_dir, len-1);
13071
+ /* Superior mail directory must exist; it is never auto-created by the
13074
+ if (stat(root_dir, &st) < 0 ) {
13075
+ if ( errno != ENOENT ) {
13076
+ i_error("sieve-storage: stat(%s) failed: %m", root_dir);
13079
+ i_error("sieve-storage: root directory specified by "
13080
+ "mail data does not exist: %s", root_dir);
13085
+ /* Never store scripts directly in the root of the mail or mail:CONTROl directory.
13087
+ root_dir = t_strconcat( root_dir, "/sieve", NULL );
13092
+static const char *sieve_storage_get_relative_link_path
13093
+ (const char *active_path, const char *storage_dir)
13095
+ const char *link_path, *p;
13098
+ /* Determine to what extent the sieve storage and active script
13099
+ * paths match up. This enables the managed symlink to be short and the
13100
+ * sieve storages can be moved around without trouble (if the active
13101
+ * script path is common to the script storage).
13103
+ p = strrchr(active_path, '/');
13104
+ if ( p == NULL ) {
13105
+ link_path = storage_dir;
13107
+ pathlen = p - active_path;
13109
+ if ( strncmp( active_path, storage_dir, pathlen ) == 0 &&
13110
+ (storage_dir[pathlen] == '/' || storage_dir[pathlen] == '\0') )
13112
+ if ( storage_dir[pathlen] == '\0' )
13115
+ link_path = storage_dir + pathlen + 1;
13117
+ link_path = storage_dir;
13120
+ /* Add trailing '/' when link path is not empty
13122
+ pathlen = strlen(link_path);
13123
+ if ( pathlen != 0 && link_path[pathlen-1] != '/')
13124
+ return t_strconcat(link_path, "/", NULL);
13126
+ return t_strdup(link_path);
13129
+struct sieve_storage *sieve_storage_create_from_mail(const char *data, const char *user)
13131
+ struct sieve_storage *storage;
13132
+ const char *storage_dir;
13136
+ storage_dir = sieve_storage_get_dir_from_mail(data);
13137
+ if (storage_dir == NULL) {
13138
+ if (getenv("DEBUG") != NULL)
13139
+ i_info("sieve-storage: failed to obtain storage directory from mail-data.");
13144
+ storage = sieve_storage_create(storage_dir, user);
13151
+struct sieve_storage *sieve_storage_create(const char *data, const char *user)
13153
+ bool debug = (getenv("DEBUG") != NULL);
13155
+ struct sieve_storage *storage;
13156
+ const char *home, *tmp_dir, *link_path, *path;
13157
+ const char *active_path, *active_fname, *storage_dir;
13161
+ /* Find out where the active script is stored (e.g. ~/.dovecot.sieve) */
13163
+ active_path = sieve_get_active_script_path();
13164
+ if (active_path == NULL) {
13170
+ i_info("sieve-storage: using active sieve script path: %s", active_path);
13172
+ /* Get the filename for the active script link */
13173
+ active_fname = strrchr(active_path, '/');
13174
+ if ( active_fname == NULL )
13175
+ active_fname = active_path;
13179
+ if ( *active_fname == '\0' ) {
13180
+ /* Link cannot be just a path */
13181
+ i_error("sieve-storage: Path to active symlink must include "
13182
+ "the link's filename. Path is: %s", active_path);
13189
+ i_info("sieve-storage: using active sieve script path: %s", active_path);
13191
+ /* Find out where to put the script storage */
13193
+ storage_dir = NULL;
13195
+ if ( data == NULL || *data == '\0' ) {
13196
+ /* We'll need to figure out the storage location ourself.
13198
+ * It's $HOME/sieve or /sieve when (presumed to be) chrooted.
13200
+ home = getenv("HOME");
13201
+ if ( home != NULL && *home != '\0' ) {
13204
+ if (access(home, R_OK|W_OK|X_OK) == 0) {
13206
+ i_info("sieve-storage: root exists (%s)",
13210
+ /* Check for trailing '/' */
13211
+ len = strlen(home);
13212
+ if (home[len-1] == '/')
13213
+ path = t_strconcat(home, "sieve", NULL);
13215
+ path = t_strconcat(home, "/sieve", NULL);
13217
+ storage_dir = path;
13220
+ i_info("sieve-storage: access(%s, rwx): "
13221
+ "failed: %m", home);
13226
+ i_info("maildir: HOME not set");
13229
+ if (access("/sieve", R_OK|W_OK|X_OK) == 0) {
13230
+ storage_dir = "/sieve";
13232
+ i_info("sieve-storage: /sieve exists, assuming chroot");
13235
+ storage_dir = data;
13238
+ if (storage_dir == NULL || *storage_dir == '\0') {
13240
+ i_info("sieve-storage: couldn't find storage dir");
13245
+ i_info("sieve-storage: using sieve script storage directory: %s", storage_dir);
13247
+ /* Expand home directoties in path */
13248
+ storage_dir = home_expand(storage_dir);
13249
+ active_path = home_expand(active_path);
13251
+ /* Ensure sieve local directory structure exists (full autocreate):
13252
+ * This currently currently only consists of a ./tmp direcory
13254
+ tmp_dir = t_strconcat( storage_dir, "/tmp", NULL );
13255
+ if (mkdir_parents(tmp_dir, CREATE_MODE) < 0 && errno != EEXIST) {
13256
+ i_error("sieve-storage: mkdir_parents(%s, CREATE_MODE) failed: %m", tmp_dir);
13261
+ /* Create storage object */
13262
+ pool = pool_alloconly_create("sieve-storage", 512+256);
13263
+ storage = p_new(pool, struct sieve_storage, 1);
13264
+ storage->pool = pool;
13265
+ storage->dir = p_strdup(pool, storage_dir);
13266
+ storage->user = p_strdup(pool, user);
13267
+ storage->active_path = p_strdup(pool, active_path);
13268
+ storage->active_fname = p_strdup(pool, active_fname);
13270
+ /* Get the path to be prefixed to the script name in the symlink pointing
13271
+ * to the active script.
13273
+ link_path = sieve_storage_get_relative_link_path
13274
+ (storage->active_path, storage->dir);
13276
+ i_info("sieve-storage: relative path to sieve storage in active link: %s", link_path);
13278
+ storage->link_path = p_strdup(pool, link_path);
13284
+void sieve_storage_free(struct sieve_storage *storage)
13286
+ pool_unref(storage->pool);
13289
+void sieve_storage_clear_error(struct sieve_storage *storage)
13291
+ i_free(storage->error);
13292
+ storage->error = NULL;
13295
+void sieve_storage_set_error(struct sieve_storage *storage, const char *fmt, ...)
13299
+ sieve_storage_clear_error(storage);
13301
+ if (fmt != NULL) {
13302
+ va_start(va, fmt);
13303
+ storage->error = i_strdup_vprintf(fmt, va);
13308
+void sieve_storage_set_internal_error(struct sieve_storage *storage)
13313
+ tm = localtime(&ioloop_time);
13315
+ i_free(storage->error);
13317
+ strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
13318
+ i_strdup(str) : i_strdup(CRITICAL_MSG);
13321
+void sieve_storage_set_critical(struct sieve_storage *storage,
13322
+ const char *fmt, ...)
13326
+ sieve_storage_clear_error(storage);
13327
+ if (fmt != NULL) {
13328
+ va_start(va, fmt);
13329
+ i_error("sieve-storage: %s", t_strdup_vprintf(fmt, va));
13332
+ /* critical errors may contain sensitive data, so let user
13333
+ see only "Internal error" with a timestamp to make it
13334
+ easier to look from log files the actual error message. */
13335
+ sieve_storage_set_internal_error(storage);
13339
+const char *sieve_storage_get_last_error(struct sieve_storage *storage)
13341
+ /* We get here only in error situations, so we have to return some
13342
+ error. If storage->error is NULL, it means we forgot to set it at
13344
+ return storage->error != NULL ? storage->error : "Unknown error";
13348
diff -r 894f003d9f5f src/lib-sievestorage/sieve-storage.h
13349
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
13350
+++ b/src/lib-sievestorage/sieve-storage.h Sun May 04 16:00:59 2008 +0200
13352
+#ifndef __SIEVE_STORAGE_H
13353
+#define __SIEVE_STORAGE_H
13355
+struct sieve_storage *sieve_storage_create_from_mail(const char *data, const char *user);
13356
+struct sieve_storage *sieve_storage_create(const char *data, const char *user);
13357
+void sieve_storage_free(struct sieve_storage *storage);
13359
+/* Set error message in storage. Critical errors are logged with i_error(),
13360
+ but user sees only "internal error" message. */
13361
+void sieve_storage_clear_error(struct sieve_storage *storage);
13362
+void sieve_storage_set_error(struct sieve_storage *storage,
13363
+ const char *fmt, ...) __attr_format__(2, 3);
13364
+void sieve_storage_set_critical(struct sieve_storage *storage,
13365
+ const char *fmt, ...) __attr_format__(2, 3);
13366
+void sieve_storage_set_internal_error(struct sieve_storage *storage);
13368
+const char *sieve_storage_get_last_error(struct sieve_storage *storage);
13371
diff -r 894f003d9f5f src/managesieve-login/Makefile.am
13372
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
13373
+++ b/src/managesieve-login/Makefile.am Sun May 04 16:00:59 2008 +0200
13375
+pkglibexecdir = $(libexecdir)/dovecot
13377
+pkglibexec_PROGRAMS = managesieve-login
13380
+ -I$(top_srcdir)/src/lib \
13381
+ -I$(top_srcdir)/src/lib-auth \
13382
+ -I$(top_srcdir)/src/lib-managesieve \
13383
+ -I$(top_srcdir)/src/login-common \
13384
+ -I$(top_srcdir)/src/lib-sieve
13386
+managesieve_login_LDADD = \
13387
+ ../login-common/liblogin-common.a \
13388
+ ../lib-managesieve/libmanagesieve.a \
13389
+ ../lib-sieve/libsieve.la \
13390
+ ../lib-sievestorage/libsievestorage.a \
13391
+ ../lib-auth/libauth.a \
13392
+ ../lib/liblib.a \
13395
+managesieve_login_SOURCES = \
13397
+ client-authenticate.c \
13398
+ managesieve-proxy.c
13400
+noinst_HEADERS = \
13402
+ client-authenticate.h \
13403
+ managesieve-proxy.h
13404
diff -r 894f003d9f5f src/managesieve-login/client-authenticate.c
13405
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
13406
+++ b/src/managesieve-login/client-authenticate.c Sun May 04 16:00:59 2008 +0200
13408
+#include "common.h"
13409
+#include "base64.h"
13410
+#include "buffer.h"
13411
+#include "ioloop.h"
13412
+#include "istream.h"
13413
+#include "ostream.h"
13414
+#include "safe-memset.h"
13416
+#include "str-sanitize.h"
13418
+#include <unistd.h>
13420
+#include "managesieve-parser.h"
13421
+#include "managesieve-quote.h"
13422
+#include "auth-client.h"
13423
+#include "client.h"
13424
+#include "client-authenticate.h"
13425
+#include "managesieve-proxy.h"
13427
+#include <stdlib.h>
13429
+/* FIXME: The use of the ANONYMOUS mechanism is currently denied
13431
+static bool _sasl_mechanism_acceptable
13432
+ (const struct auth_mech_desc *mech, bool secured) {
13434
+ /* a) transport is secured
13435
+ b) auth mechanism isn't plaintext
13436
+ c) we allow insecure authentication
13439
+ if ((mech->flags & MECH_SEC_PRIVATE) == 0 &&
13440
+ (mech->flags & MECH_SEC_ANONYMOUS) == 0 &&
13441
+ (secured || !disable_plaintext_auth ||
13442
+ (mech->flags & MECH_SEC_PLAINTEXT) == 0)) {
13449
+const char *client_authenticate_get_capabilities(bool secured)
13451
+ const struct auth_mech_desc *mech;
13452
+ unsigned int i, count;
13455
+ str = t_str_new(128);
13456
+ mech = auth_client_get_available_mechs(auth_client, &count);
13458
+ if ( count > 0 ) {
13459
+ if ( _sasl_mechanism_acceptable(&(mech[0]), secured) ) {
13460
+ str_append(str, mech[0].name);
13463
+ for (i = 1; i < count; i++) {
13464
+ if ( _sasl_mechanism_acceptable(&(mech[i]), secured) ) {
13465
+ str_append_c(str, ' ');
13466
+ str_append(str, mech[i].name);
13471
+ return str_c(str);
13474
+static void client_auth_input(void *context)
13476
+ struct managesieve_client *client = context;
13477
+ struct managesieve_arg *args;
13482
+ if (client->destroyed)
13485
+ if (!client_read(client))
13488
+ if (client->skip_line) {
13489
+ if (i_stream_next_line(client->input) == NULL)
13492
+ client->skip_line = FALSE;
13495
+ switch (managesieve_parser_read_args(client->parser, 0, 0, &args)) {
13498
+ msg = managesieve_parser_get_error(client->parser, &fatal);
13500
+ /* FIXME: What to do? */
13503
+ sasl_server_auth_client_error(&client->common, msg);
13506
+ /* not enough data */
13510
+ if (args[0].type != MANAGESIEVE_ARG_STRING ||
13511
+ args[1].type != MANAGESIEVE_ARG_EOL) {
13512
+ sasl_server_auth_client_error(&client->common, "Invalid AUTHENTICATE client response.");
13516
+ line = MANAGESIEVE_ARG_STR(&args[0]);
13518
+ /* Disable input for now */
13519
+ if (client->io != NULL)
13520
+ io_remove(&client->io);
13522
+ auth_client_request_continue(client->common.auth_request, line);
13524
+ /* clear sensitive data */
13525
+ safe_memset(line, 0, strlen(line));
13528
+static bool client_handle_args(struct managesieve_client *client,
13529
+ const char *const *args, bool success)
13531
+ const char *reason = NULL, *host = NULL, *destuser = NULL, *pass = NULL;
13532
+ string_t *resp_code;
13533
+ unsigned int port = 2000;
13534
+ bool proxy = FALSE, temp = FALSE, nologin = !success;
13536
+ for (; *args != NULL; args++) {
13537
+ if (strcmp(*args, "nologin") == 0)
13539
+ else if (strcmp(*args, "proxy") == 0)
13541
+ else if (strcmp(*args, "temp") == 0)
13543
+ else if (strncmp(*args, "reason=", 7) == 0)
13544
+ reason = *args + 7;
13545
+ else if (strncmp(*args, "host=", 5) == 0)
13546
+ host = *args + 5;
13547
+ else if (strncmp(*args, "port=", 5) == 0)
13548
+ port = atoi(*args + 5);
13549
+ else if (strncmp(*args, "destuser=", 9) == 0)
13550
+ destuser = *args + 9;
13551
+ else if (strncmp(*args, "pass=", 5) == 0)
13552
+ pass = *args + 5;
13555
+ if (destuser == NULL)
13556
+ destuser = client->common.virtual_user;
13559
+ /* we want to proxy the connection to another server.
13560
+ don't do this unless authentication succeeded. with
13561
+ master user proxying we can get FAIL with proxy still set.
13563
+ proxy host=.. [port=..] [destuser=..] pass=.. */
13567
+ if (managesieve_proxy_new(client, host, port, destuser, pass) < 0)
13568
+ client_destroy_internal_failure(client);
13572
+ if (host != NULL) {
13573
+ /* MANAGESIEVE referral
13575
+ [nologin] referral host=.. [port=..] [destuser=..]
13578
+ NO (REFERRAL sieve://user;AUTH=mech@host:port/) Can't login.
13579
+ OK (...) Logged in, but you should use this server instead.
13580
+ .. [REFERRAL ..] (Reason from auth server)
13582
+ resp_code = t_str_new(128);
13583
+ str_printfa(resp_code, "REFERRAL sieve://%s;AUTH=%s@%s",
13584
+ destuser, client->common.auth_mech_name, host);
13585
+ if (port != 2000)
13586
+ str_printfa(resp_code, ":%u", port);
13588
+ if (reason == NULL) {
13590
+ reason = "Try this server instead.";
13592
+ reason = "Logged in, but you should use "
13593
+ "this server instead.";
13597
+ client_send_okresp(client, str_c(resp_code), reason);
13598
+ client_destroy(client, "Login with referral");
13601
+ client_send_noresp(client, str_c(resp_code), reason);
13602
+ } else if (nologin) {
13603
+ /* Authentication went ok, but for some reason user isn't
13604
+ allowed to log in. Shouldn't probably happen. */
13605
+ if (reason != NULL)
13606
+ client_send_no(client, reason);
13608
+ client_send_no(client, AUTH_TEMP_FAILED_MSG);
13610
+ client_send_no(client, AUTH_FAILED_MSG);
13612
+ /* normal login/failure */
13616
+ i_assert(nologin);
13618
+ managesieve_parser_reset(client->parser);
13620
+ if (!client->destroyed) {
13621
+ /* get back to normal client input. */
13622
+ if (client->io != NULL)
13623
+ io_remove(&client->io);
13624
+ client->io = io_add(client->common.fd, IO_READ,
13625
+ client_input, client);
13631
+static void sasl_callback(struct client *_client, enum sasl_server_reply reply,
13632
+ const char *data, const char *const *args)
13634
+ struct managesieve_client *client = (struct managesieve_client *)_client;
13637
+ i_assert(!client->destroyed ||
13638
+ reply == SASL_SERVER_REPLY_CLIENT_ERROR ||
13639
+ reply == SASL_SERVER_REPLY_MASTER_FAILED);
13641
+ client->skip_line = TRUE;
13644
+ case SASL_SERVER_REPLY_SUCCESS:
13645
+ if (args != NULL) {
13646
+ if (client_handle_args(client, args, TRUE))
13650
+ client_destroy(client, "Login");
13652
+ case SASL_SERVER_REPLY_AUTH_FAILED:
13653
+ case SASL_SERVER_REPLY_CLIENT_ERROR:
13654
+ if (args != NULL) {
13655
+ if (client_handle_args(client, args, FALSE))
13659
+ client_send_no(client, data != NULL ? data : AUTH_FAILED_MSG);
13661
+ managesieve_parser_reset(client->parser);
13663
+ if (!client->destroyed) {
13664
+ /* get back to normal client input. */
13665
+ if (client->io != NULL)
13666
+ io_remove(&client->io);
13667
+ client->io = io_add(client->common.fd, IO_READ,
13668
+ client_input, client);
13671
+ case SASL_SERVER_REPLY_MASTER_FAILED:
13672
+ client_destroy_internal_failure(client);
13674
+ case SASL_SERVER_REPLY_CONTINUE:
13676
+ str = t_str_new(256);
13677
+ managesieve_quote_append_string(str, data, TRUE);
13678
+ str_append(str, "\r\n");
13680
+ /* don't check return value here. it gets tricky if we try
13681
+ to call client_destroy() in here. */
13682
+ (void)o_stream_send(client->output, str_c(str), str_len(str));
13685
+ managesieve_parser_reset(client->parser);
13687
+ i_assert(client->io == NULL);
13688
+ client->io = io_add(client->common.fd, IO_READ,
13689
+ client_auth_input, client);
13690
+ client_auth_input(client);
13695
+ client_unref(client);
13698
+int cmd_authenticate(struct managesieve_client *client, struct managesieve_arg *args)
13700
+ const char *mech_name, *init_resp = NULL;
13702
+ /* one mandatory argument: authentication mechanism name */
13703
+ if (args[0].type != MANAGESIEVE_ARG_STRING)
13705
+ if (args[1].type != MANAGESIEVE_ARG_EOL) {
13706
+ /* optional SASL initial response */
13707
+ if (args[1].type != MANAGESIEVE_ARG_STRING ||
13708
+ args[2].type != MANAGESIEVE_ARG_EOL)
13710
+ init_resp = MANAGESIEVE_ARG_STR(&args[1]);
13713
+ mech_name = MANAGESIEVE_ARG_STR(&args[0]);
13714
+ if (*mech_name == '\0')
13717
+ /* FIXME: This refuses the ANONYMOUS mechanism.
13718
+ * This can be removed once anonymous login is implemented according to the
13719
+ * draft RFC. - Stephan
13721
+ if ( strncasecmp(mech_name, "ANONYMOUS", 9) == 0 ) {
13722
+ client_send_no(client, "ANONYMOUS mechanism is not implemented.");
13726
+ client_ref(client);
13727
+ sasl_server_auth_begin(&client->common, "MANAGESIEVE", mech_name,
13728
+ init_resp, sasl_callback);
13729
+ if (!client->common.authenticating)
13732
+ /* don't handle input until we get the initial auth reply */
13733
+ if (client->io != NULL)
13734
+ io_remove(&client->io);
13736
+ managesieve_parser_reset(client->parser);
13741
diff -r 894f003d9f5f src/managesieve-login/client-authenticate.h
13742
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
13743
+++ b/src/managesieve-login/client-authenticate.h Sun May 04 16:00:59 2008 +0200
13745
+#ifndef __CLIENT_AUTHENTICATE_H
13746
+#define __CLIENT_AUTHENTICATE_H
13748
+const char *client_authenticate_get_capabilities(bool secured);
13750
+int cmd_login(struct managesieve_client *client, struct managesieve_arg *args);
13751
+int cmd_authenticate(struct managesieve_client *client, struct managesieve_arg *args);
13754
diff -r 894f003d9f5f src/managesieve-login/client.c
13755
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
13756
+++ b/src/managesieve-login/client.c Sun May 04 16:00:59 2008 +0200
13758
+#include "common.h"
13759
+#include "buffer.h"
13761
+#include "ioloop.h"
13762
+#include "istream.h"
13763
+#include "ostream.h"
13764
+#include "process-title.h"
13765
+#include "safe-memset.h"
13767
+#include "strfuncs.h"
13768
+#include "strescape.h"
13769
+#include "managesieve-parser.h"
13770
+#include "managesieve-quote.h"
13771
+#include "sieve-implementation.h"
13773
+#include "client.h"
13774
+#include "client-authenticate.h"
13775
+#include "auth-client.h"
13776
+#include "ssl-proxy.h"
13777
+#include "managesieve-proxy.h"
13779
+#include <stdlib.h>
13781
+/* max. size of one parameter in line, or max reply length in SASL
13782
+ authentication */
13783
+#define MAX_INBUF_SIZE 4096
13785
+/* max. size of output buffer. if it gets full, the client is disconnected.
13786
+ SASL authentication gives the largest output. */
13787
+#define MAX_OUTBUF_SIZE 4096
13789
+/* Disconnect client after idling this many seconds */
13790
+#define CLIENT_LOGIN_IDLE_TIMEOUT 60
13792
+/* Disconnect client when it sends too many bad commands */
13793
+#define CLIENT_MAX_BAD_COMMANDS 10
13795
+/* When max. number of simultaneous connections is reached, few of the
13796
+ oldest connections are disconnected. Since we have to go through the whole
13797
+ client hash, it's faster if we disconnect multiple clients. */
13798
+#define CLIENT_DESTROY_OLDEST_COUNT 16
13800
+#if CLIENT_LOGIN_IDLE_TIMEOUT >= AUTH_REQUEST_TIMEOUT
13801
+# error client idle timeout must be smaller than authentication timeout
13804
+const char *login_protocol = "MANAGESIEVE";
13805
+const char *capability_string = CAPABILITY_STRING;
13807
+const char *managesieve_implementation_string;
13809
+static struct hash_table *clients;
13810
+static struct timeout *to_idle;
13812
+static void client_set_title(struct managesieve_client *client)
13814
+ const char *addr;
13816
+ if (!verbose_proctitle || !process_per_connection)
13819
+ addr = net_ip2addr(&client->common.ip);
13820
+ if (addr == NULL)
13823
+ process_title_set(t_strdup_printf(client->common.tls ?
13824
+ "[%s TLS]" : "[%s]", addr));
13827
+static void client_open_streams(struct managesieve_client *client, int fd)
13829
+ client->input = i_stream_create_file(fd, default_pool,
13830
+ MAX_INBUF_SIZE, FALSE);
13831
+ client->output = o_stream_create_file(fd, default_pool, MAX_OUTBUF_SIZE,
13833
+ client->parser = managesieve_parser_create(client->input, client->output,
13834
+ MAX_MANAGESIEVE_LINE);
13837
+/* Skip incoming data until newline is found,
13838
+ returns TRUE if newline was found. */
13839
+bool client_skip_line(struct managesieve_client *client)
13841
+ const unsigned char *data;
13842
+ size_t i, data_size;
13844
+ data = i_stream_get_data(client->input, &data_size);
13846
+ for (i = 0; i < data_size; i++) {
13847
+ if (data[i] == '\n') {
13848
+ i_stream_skip(client->input, i+1);
13856
+static void client_send_capabilities(struct managesieve_client *client)
13858
+ const char *auths;
13859
+ const char *sievecap, *sieveimpl;
13861
+ sievecap = sieve_get_capabilities();
13862
+ if (sievecap == NULL)
13866
+ sievecap = t_strconcat("\"SIEVE\" \"", sievecap, "\"", NULL);
13867
+ sieveimpl = t_strconcat("\"IMPLEMENTATION\" \"",
13868
+ managesieve_implementation_string, "\"", NULL);
13870
+ auths = client_authenticate_get_capabilities(client->common.secured);
13872
+ /* We assume no MANAGESIEVE-string incompatible values are produced here */
13873
+ client_send_line(client, sieveimpl);
13874
+ client_send_line(client, t_strconcat("\"SASL\" \"", auths, "\"", NULL) );
13875
+ client_send_line(client, sievecap);
13877
+ if (ssl_initialized && !client->common.tls)
13878
+ client_send_line(client, "\"STARTTLS\"" );
13883
+static int cmd_capability(struct managesieve_client *client)
13885
+ client_send_capabilities(client);
13886
+ client_send_ok(client, "Capability completed.");
13890
+static void client_start_tls(struct managesieve_client *client)
13894
+ client_ref(client);
13895
+ connection_queue_add(1);
13896
+ if (!client_unref(client) || client->destroyed)
13899
+ fd_ssl = ssl_proxy_new(client->common.fd, &client->common.ip,
13900
+ &client->common.proxy);
13901
+ if (fd_ssl == -1) {
13902
+ client_send_bye(client, "TLS initialization failed.");
13903
+ client_destroy(client, "TLS initialization failed.");
13907
+ client->common.tls = TRUE;
13908
+ client->common.secured = TRUE;
13909
+ client_set_title(client);
13911
+ client->common.fd = fd_ssl;
13912
+ i_stream_unref(&client->input);
13913
+ o_stream_unref(&client->output);
13914
+ managesieve_parser_destroy(&client->parser);
13916
+ /* CRLF is lost from buffer when streams are reopened. */
13917
+ client->skip_line = FALSE;
13919
+ client_open_streams(client, fd_ssl);
13920
+ client->io = io_add(client->common.fd, IO_READ, client_input, client);
13923
+static int client_output_starttls(void *context)
13925
+ struct managesieve_client *client = context;
13928
+ if ((ret = o_stream_flush(client->output)) < 0) {
13929
+ client_destroy(client, "Disconnected");
13934
+ o_stream_set_flush_callback(client->output, NULL, NULL);
13935
+ client_start_tls(client);
13940
+static int cmd_starttls(struct managesieve_client *client)
13942
+ if (client->common.tls) {
13943
+ client_send_no(client, "TLS is already active.");
13947
+ if (!ssl_initialized) {
13948
+ client_send_no(client, "TLS support isn't enabled.");
13952
+ /* remove input handler, SSL proxy gives us a new fd. we also have to
13953
+ remove it in case we have to wait for buffer to be flushed */
13954
+ if (client->io != NULL)
13955
+ io_remove(&client->io);
13957
+ client_send_ok(client, "Begin TLS negotiation now.");
13959
+ /* uncork the old fd */
13960
+ o_stream_uncork(client->output);
13962
+ if (o_stream_flush(client->output) <= 0) {
13963
+ /* the buffer has to be flushed */
13964
+ o_stream_set_flush_pending(client->output, TRUE);
13965
+ o_stream_set_flush_callback(client->output,
13966
+ client_output_starttls, client);
13968
+ client_start_tls(client);
13971
+ /* Cork the stream to send the capability data as a single tcp frame
13972
+ * Some naive clients break if we don't.
13974
+ o_stream_cork(client->output);
13976
+ client_send_capabilities(client);
13977
+ client_send_ok(client, "TLS negotiation successful.");
13979
+ o_stream_uncork(client->output);
13984
+static int cmd_logout(struct managesieve_client *client)
13986
+ client_send_ok(client, "Logout completed.");
13987
+ client_destroy(client, "Aborted login (logout command)");
13991
+static int client_command_execute(struct managesieve_client *client, const char *cmd,
13992
+ struct managesieve_arg *args)
13994
+ cmd = t_str_ucase(cmd);
13995
+ if (strcmp(cmd, "AUTHENTICATE") == 0)
13996
+ return cmd_authenticate(client, args);
13997
+ if (strcmp(cmd, "CAPABILITY") == 0)
13998
+ return cmd_capability(client);
13999
+ if (strcmp(cmd, "STARTTLS") == 0)
14000
+ return cmd_starttls(client);
14001
+ if (strcmp(cmd, "LOGOUT") == 0)
14002
+ return cmd_logout(client);
14007
+static bool client_handle_input(struct managesieve_client *client)
14009
+ struct managesieve_arg *args;
14014
+ i_assert(!client->common.authenticating);
14016
+ if (client->cmd_finished) {
14017
+ /* clear the previous command from memory. don't do this
14018
+ immediately after handling command since we need the
14019
+ cmd_tag to stay some time after authentication commands. */
14020
+ client->cmd_name = NULL;
14021
+ managesieve_parser_reset(client->parser);
14023
+ /* remove \r\n */
14024
+ if (client->skip_line) {
14025
+ if (!client_skip_line(client))
14027
+ client->skip_line = FALSE;
14030
+ client->cmd_finished = FALSE;
14033
+ if (client->cmd_name == NULL) {
14034
+ client->cmd_name = managesieve_parser_read_word(client->parser);
14035
+ if (client->cmd_name == NULL)
14036
+ return FALSE; /* need more data */
14039
+ switch (managesieve_parser_read_args(client->parser, 0, 0, &args)) {
14042
+ msg = managesieve_parser_get_error(client->parser, &fatal);
14044
+ client_send_bye(client, msg);
14045
+ client_destroy(client, t_strconcat("Disconnected: ",
14050
+ client_send_no(client, msg);
14051
+ client->cmd_finished = TRUE;
14052
+ client->skip_line = TRUE;
14055
+ /* not enough data */
14058
+ client->skip_line = TRUE;
14060
+ ret = client_command_execute(client, client->cmd_name, args);
14062
+ client->cmd_finished = TRUE;
14064
+ if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
14065
+ client_send_bye(client,
14066
+ "Too many invalid MANAGESIEVE commands.");
14067
+ client_destroy(client, "Disconnected: "
14068
+ "Too many invalid commands.");
14071
+ client_send_no(client,
14072
+ "Error in MANAGESIEVE command received by server.");
14078
+bool client_read(struct managesieve_client *client)
14080
+ switch (i_stream_read(client->input)) {
14082
+ /* buffer full */
14083
+ client_send_bye(client, "Input buffer full, aborting");
14084
+ client_destroy(client, "Disconnected: Input buffer full");
14087
+ /* disconnected */
14088
+ client_destroy(client, "Disconnected");
14091
+ /* something was read */
14096
+void client_input(void *context)
14098
+ struct managesieve_client *client = context;
14100
+ client->last_input = ioloop_time;
14102
+ if (!client_read(client))
14105
+ client_ref(client);
14107
+ if (!auth_client_is_connected(auth_client)) {
14108
+ /* we're not yet connected to auth process -
14109
+ don't allow any commands */
14110
+ /* FIXME: Can't do this with managesieve. Any other ways?
14111
+ client_send_ok(client,
14112
+ "Waiting for authentication process to respond.");
14114
+ client->input_blocked = TRUE;
14116
+ o_stream_cork(client->output);
14117
+ while (client_handle_input(client)) ;
14118
+ o_stream_uncork(client->output);
14121
+ client_unref(client);
14124
+void client_destroy_oldest(void)
14126
+ struct hash_iterate_context *iter;
14127
+ void *key, *value;
14128
+ struct managesieve_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
14129
+ int i, destroy_count;
14131
+ /* find the oldest clients and put them to destroy-buffer */
14132
+ memset(destroy_buf, 0, sizeof(destroy_buf));
14134
+ destroy_count = max_connections > CLIENT_DESTROY_OLDEST_COUNT*2 ?
14135
+ CLIENT_DESTROY_OLDEST_COUNT : I_MIN(max_connections/2, 1);
14136
+ iter = hash_iterate_init(clients);
14137
+ while (hash_iterate(iter, &key, &value)) {
14138
+ struct managesieve_client *client = key;
14140
+ for (i = 0; i < destroy_count; i++) {
14141
+ if (destroy_buf[i] == NULL ||
14142
+ destroy_buf[i]->created > client->created) {
14144
+ memmove(destroy_buf+i+1, destroy_buf+i,
14145
+ sizeof(destroy_buf) -
14146
+ (i+1) * sizeof(struct managesieve_client *));
14147
+ destroy_buf[i] = client;
14152
+ hash_iterate_deinit(iter);
14154
+ /* then kill them */
14155
+ for (i = 0; i < destroy_count; i++) {
14156
+ if (destroy_buf[i] == NULL)
14159
+ client_destroy(destroy_buf[i],
14160
+ "Disconnected: Connection queue full");
14164
+static void client_send_greeting(struct managesieve_client *client)
14166
+ /* Cork the stream to send the capability data as a single tcp frame
14167
+ * Some naive clients break if we don't.
14169
+ o_stream_cork(client->output);
14171
+ /* Send initial capabilities */
14172
+ client_send_capabilities(client);
14173
+ client_send_ok(client, greeting);
14174
+ client->greeting_sent = TRUE;
14176
+ o_stream_uncork(client->output);
14179
+struct client *client_create(int fd, bool ssl, const struct ip_addr *local_ip,
14180
+ const struct ip_addr *ip)
14182
+ struct managesieve_client *client;
14184
+ i_assert(fd != -1);
14186
+ connection_queue_add(1);
14188
+ /* always use nonblocking I/O */
14189
+ net_set_nonblock(fd, TRUE);
14191
+ client = i_new(struct managesieve_client, 1);
14192
+ client->created = ioloop_time;
14193
+ client->refcount = 1;
14194
+ client->common.tls = ssl;
14195
+ client->common.secured = ssl || net_ip_compare(ip, local_ip);
14197
+ client->common.local_ip = *local_ip;
14198
+ client->common.ip = *ip;
14199
+ client->common.fd = fd;
14201
+ client_open_streams(client, fd);
14202
+ client->io = io_add(fd, IO_READ, client_input, client);
14204
+ client->last_input = ioloop_time;
14205
+ hash_insert(clients, client, client);
14209
+ if (!greeting_capability || auth_client_is_connected(auth_client))
14210
+ client_send_greeting(client);
14211
+ client_set_title(client);
14213
+ return &client->common;
14216
+void client_destroy(struct managesieve_client *client, const char *reason)
14218
+ if (client->destroyed)
14220
+ client->destroyed = TRUE;
14222
+ if (reason != NULL)
14223
+ client_syslog(&client->common, reason);
14225
+ hash_remove(clients, client);
14227
+ if (client->input != NULL)
14228
+ i_stream_close(client->input);
14229
+ if (client->output != NULL)
14230
+ o_stream_close(client->output);
14232
+ if (client->common.master_tag != 0)
14233
+ master_request_abort(&client->common);
14235
+ if (client->common.auth_request != NULL) {
14236
+ i_assert(client->common.authenticating);
14237
+ sasl_server_auth_client_error(&client->common, NULL);
14239
+ i_assert(!client->common.authenticating);
14242
+ if (client->io != NULL)
14243
+ io_remove(&client->io);
14245
+ if (client->common.fd != -1) {
14246
+ net_disconnect(client->common.fd);
14247
+ client->common.fd = -1;
14250
+ if (client->proxy_password != NULL) {
14251
+ safe_memset(client->proxy_password, 0,
14252
+ strlen(client->proxy_password));
14253
+ i_free(client->proxy_password);
14254
+ client->proxy_password = NULL;
14257
+ i_free(client->proxy_user);
14258
+ client->proxy_user = NULL;
14260
+ if (client->proxy != NULL) {
14261
+ login_proxy_free(client->proxy);
14262
+ client->proxy = NULL;
14265
+ if (client->common.proxy != NULL) {
14266
+ ssl_proxy_free(client->common.proxy);
14267
+ client->common.proxy = NULL;
14270
+ client_unref(client);
14272
+ main_listen_start();
14276
+void client_destroy_internal_failure(struct managesieve_client *client)
14278
+ client_send_byeresp(client, "TRYLATER", "Internal login failure. "
14279
+ "Refer to server log for more information.");
14280
+ client_destroy(client, "Internal login failure");
14283
+void client_ref(struct managesieve_client *client)
14285
+ client->refcount++;
14288
+bool client_unref(struct managesieve_client *client)
14290
+ i_assert(client->refcount > 0);
14291
+ if (--client->refcount > 0)
14294
+ i_assert(client->destroyed);
14296
+ managesieve_parser_destroy(&client->parser);
14298
+ if (client->input != NULL)
14299
+ i_stream_unref(&client->input);
14300
+ if (client->output != NULL)
14301
+ o_stream_unref(&client->output);
14303
+ i_free(client->common.virtual_user);
14304
+ i_free(client->common.auth_mech_name);
14310
+void client_send_line(struct managesieve_client *client, const char *line)
14312
+ struct const_iovec iov[2];
14315
+ iov[0].iov_base = line;
14316
+ iov[0].iov_len = strlen(line);
14317
+ iov[1].iov_base = "\r\n";
14318
+ iov[1].iov_len = 2;
14320
+ ret = o_stream_sendv(client->output, iov, 2);
14321
+ if (ret < 0 || (size_t)ret != iov[0].iov_len + iov[1].iov_len) {
14322
+ /* either disconnection or buffer full. in either case we
14323
+ want this connection destroyed. however destroying it here
14324
+ might break things if client is still tried to be accessed
14325
+ without being referenced.. */
14326
+ i_stream_close(client->input);
14330
+void _client_send_response(struct managesieve_client *client,
14331
+ const char *oknobye, const char *resp_code, const char *msg)
14335
+ str = t_str_new(128);
14336
+ str_append(str, oknobye);
14338
+ if ( resp_code != NULL )
14340
+ str_append(str, " (");
14341
+ str_append(str, resp_code);
14342
+ str_append_c(str, ')');
14345
+ if ( msg != NULL )
14347
+ str_append_c(str, ' ');
14348
+ managesieve_quote_append_string(str, msg, TRUE);
14351
+ client_send_line(client, str_c(str));
14354
+static void client_check_idle(struct managesieve_client *client)
14356
+ if (ioloop_time - client->last_input >= CLIENT_LOGIN_IDLE_TIMEOUT) {
14357
+ client_send_bye(client, "Disconnected for inactivity.");
14358
+ client_destroy(client, "Disconnected: Inactivity");
14362
+static void idle_timeout(void *context __attr_unused__)
14364
+ struct hash_iterate_context *iter;
14365
+ void *key, *value;
14367
+ iter = hash_iterate_init(clients);
14368
+ while (hash_iterate(iter, &key, &value)) {
14369
+ struct managesieve_client *client = key;
14371
+ client_check_idle(client);
14373
+ hash_iterate_deinit(iter);
14376
+unsigned int clients_get_count(void)
14378
+ return hash_size(clients);
14381
+void clients_notify_auth_connected(void)
14383
+ struct hash_iterate_context *iter;
14384
+ void *key, *value;
14386
+ iter = hash_iterate_init(clients);
14387
+ while (hash_iterate(iter, &key, &value)) {
14388
+ struct managesieve_client *client = key;
14390
+ if (!client->greeting_sent)
14391
+ client_send_greeting(client);
14392
+ if (client->input_blocked) {
14393
+ client->input_blocked = FALSE;
14394
+ client_input(client);
14397
+ hash_iterate_deinit(iter);
14400
+void clients_destroy_all(void)
14402
+ struct hash_iterate_context *iter;
14403
+ void *key, *value;
14405
+ iter = hash_iterate_init(clients);
14406
+ while (hash_iterate(iter, &key, &value)) {
14407
+ struct managesieve_client *client = key;
14409
+ client_destroy(client, "Disconnected: Shutting down");
14411
+ hash_iterate_deinit(iter);
14414
+void clients_init(void)
14418
+ clients = hash_create(default_pool, default_pool, 128, NULL, NULL);
14419
+ to_idle = timeout_add(1000, idle_timeout, NULL);
14421
+ /* Specific MANAGESIEVE settings */
14422
+ str = getenv("MANAGESIEVE_IMPLEMENTATION_STRING");
14423
+ managesieve_implementation_string = str != NULL ?
14424
+ str : DEFAULT_MANAGESIEVE_IMPLEMENTATION_STRING;
14427
+ sieve_set_implementation("cmu");
14430
+void clients_deinit(void)
14432
+ clients_destroy_all();
14433
+ hash_destroy(clients);
14435
+ timeout_remove(&to_idle);
14438
diff -r 894f003d9f5f src/managesieve-login/client.h
14439
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
14440
+++ b/src/managesieve-login/client.h Sun May 04 16:00:59 2008 +0200
14442
+#ifndef __CLIENT_H
14443
+#define __CLIENT_H
14445
+#include "network.h"
14446
+#include "master.h"
14447
+#include "client-common.h"
14449
+/* FIXME: Duplicate, also defined in src/managesieve */
14450
+#define DEFAULT_MANAGESIEVE_IMPLEMENTATION_STRING PACKAGE
14452
+/* maximum length for MANAGESIEVE command line. */
14453
+#define MAX_MANAGESIEVE_LINE 8192
14455
+struct managesieve_client {
14456
+ struct client common;
14462
+ struct istream *input;
14463
+ struct ostream *output;
14464
+ struct managesieve_parser *parser;
14466
+ struct login_proxy *proxy;
14467
+ char *proxy_user, *proxy_password;
14469
+ time_t last_input;
14470
+ unsigned int bad_counter;
14472
+ const char *cmd_name;
14474
+ unsigned int cmd_finished:1;
14475
+ unsigned int skip_line:1;
14476
+ unsigned int input_blocked:1;
14477
+ unsigned int destroyed:1;
14478
+ unsigned int greeting_sent:1;
14480
+ /* Maybe these should be combined in some enum state variable */
14481
+ unsigned int proxy_greeting_recvd:1;
14482
+ unsigned int proxy_login_sent:1;
14485
+void client_destroy(struct managesieve_client *client, const char *reason);
14486
+void client_destroy_internal_failure(struct managesieve_client *client);
14488
+void client_send_line(struct managesieve_client *client, const char *line);
14490
+bool client_read(struct managesieve_client *client);
14491
+bool client_skip_line(struct managesieve_client *client);
14492
+void client_input(void *context);
14494
+void client_ref(struct managesieve_client *client);
14495
+bool client_unref(struct managesieve_client *client);
14497
+void _client_send_response(struct managesieve_client *client,
14498
+ const char *oknobye, const char *resp_code, const char *msg);
14500
+#define client_send_ok(client, msg) \
14501
+ _client_send_response(client, "OK", NULL, msg)
14502
+#define client_send_no(client, msg) \
14503
+ _client_send_response(client, "NO", NULL, msg)
14504
+#define client_send_bye(client, msg) \
14505
+ _client_send_response(client, "BYE", NULL, msg)
14507
+#define client_send_okresp(client, resp_code, msg) \
14508
+ _client_send_response(client, "OK", resp_code, msg)
14509
+#define client_send_noresp(client, resp_code, msg) \
14510
+ _client_send_response(client, "NO", resp_code, msg)
14511
+#define client_send_byeresp(client, resp_code, msg) \
14512
+ _client_send_response(client, "BYE", resp_code, msg)
14514
diff -r 894f003d9f5f src/managesieve-login/managesieve-proxy.c
14515
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
14516
+++ b/src/managesieve-login/managesieve-proxy.c Sun May 04 16:00:59 2008 +0200
14518
+/* Copyright (C) 2004 Timo Sirainen */
14520
+#include <string.h>
14521
+#include "common.h"
14522
+#include "ioloop.h"
14523
+#include "istream.h"
14524
+#include "ostream.h"
14526
+#include "safe-memset.h"
14527
+#include "buffer.h"
14528
+#include "base64.h"
14529
+#include "client.h"
14530
+#include "managesieve-quote.h"
14531
+#include "managesieve-proxy.h"
14532
+#include "managesieve-parser.h"
14534
+static int proxy_input_line(struct managesieve_client *client,
14535
+ struct ostream *output, const char *line)
14540
+ i_assert(!client->destroyed);
14542
+ if (!client->proxy_login_sent) {
14543
+ string_t *plain_login, *base64;
14544
+ struct istream *input;
14545
+ struct managesieve_parser *parser;
14546
+ struct managesieve_arg *args;
14548
+ bool fatal = FALSE, greeting_recvd = FALSE;
14550
+ /* Server will send greeting which is actually a capability
14551
+ * response. Output from a faulty server should not be accepted,
14552
+ * so the response is parsed and verified.
14555
+ /* Build an input stream for the managesieve parser
14556
+ * FIXME: It would be nice if the line-wise parsing could be
14557
+ * substituded by something similar to the command line interpreter.
14558
+ * However, the current login_proxy structure does not make streams
14559
+ * known until inside proxi_input handler.
14561
+ line = t_strconcat(line, "\r\n", NULL);
14562
+ input = i_stream_create_from_data(pool_datastack_create(), line,
14564
+ parser = managesieve_parser_create(input, NULL, MAX_MANAGESIEVE_LINE);
14565
+ managesieve_parser_reset(parser);
14568
+ * FIXME: Theoretically the OK response could include a
14569
+ * response code which could be rejected by the parser.
14571
+ (void)i_stream_read(input);
14572
+ ret = managesieve_parser_read_args(parser, 2, 0, &args);
14574
+ if ( ret >= 1 ) {
14575
+ if ( args[0].type == MANAGESIEVE_ARG_ATOM &&
14576
+ strncasecmp(MANAGESIEVE_ARG_STR(&(args[0])), "OK", 2) == 0 ) {
14578
+ /* Received OK response; greeting is finished */
14579
+ greeting_recvd = TRUE;
14581
+ } else if ( args[0].type == MANAGESIEVE_ARG_STRING ) {
14582
+ if ( strncasecmp(MANAGESIEVE_ARG_STR(&(args[0])), "SASL", 4) == 0 ) {
14583
+ /* Check whether the server supports the SASL mechanism
14584
+ * we are going to use (currently only PLAIN supported).
14586
+ if ( ret == 2 && args[1].type == MANAGESIEVE_ARG_STRING ) {
14587
+ char *p = MANAGESIEVE_ARG_STR(&(args[1]));
14588
+ int mech_found = FALSE;
14590
+ while ( p != NULL ) {
14591
+ if ( strncasecmp(p, "PLAIN", 5) == 0 ) {
14592
+ mech_found = TRUE;
14596
+ p = strchr(p, ' ');
14597
+ if ( p != NULL ) p++;
14600
+ if ( !mech_found ) {
14601
+ i_error("managesieve-proxy(%s): "
14602
+ "Server does not support required PLAIN SASL mechanism.",
14603
+ client->common.virtual_user);
14610
+ /* Do not accept faulty server */
14611
+ i_error("managesieve-proxy(%s): "
14612
+ "Remote returned with invalid capability/greeting line: %s",
14613
+ client->common.virtual_user, line);
14618
+ } else if ( ret == -2 ) {
14619
+ /* Parser needs more data (not possible on mem stream) */
14622
+ } else if ( ret < 0 ) {
14623
+ const char *error_str = managesieve_parser_get_error(parser, &fatal);
14624
+ error_str = (error_str != NULL ? error_str : "unknown (bug)" );
14626
+ /* Do not accept faulty server */
14627
+ i_error("managesieve-proxy(%s): "
14628
+ "Protocol parse error(%d) in capability/greeting line: %s (line='%s')",
14629
+ client->common.virtual_user, ret, error_str, line);
14634
+ /* Cleanup parser */
14635
+ managesieve_parser_destroy(&parser);
14636
+ i_stream_destroy(&input);
14638
+ /* Time to exit if greeting was not accepted */
14640
+ client_destroy_internal_failure(client);
14645
+ /* Wait until greeting is received completely */
14646
+ if ( !greeting_recvd ) return 0;
14648
+ /* Send AUTHENTICATE "PLAIN" command
14649
+ * FIXME: Currently there seems to be no SASL client implementation,
14650
+ * so only implement the trivial PLAIN method
14655
+ /* Base64-encode the credentials
14656
+ * [authorization ID \0 authentication ID \0 pass]
14658
+ plain_login = buffer_create_dynamic(pool_datastack_create(), 64);
14659
+ buffer_append_c(plain_login, '\0');
14660
+ buffer_append(plain_login, client->proxy_user, strlen(client->proxy_user));
14661
+ buffer_append_c(plain_login, '\0');
14662
+ buffer_append(plain_login, client->proxy_password, strlen(client->proxy_password));
14664
+ base64 = buffer_create_dynamic(pool_datastack_create(),
14665
+ MAX_BASE64_ENCODED_SIZE(plain_login->used));
14666
+ base64_encode(plain_login->data, plain_login->used, base64);
14668
+ /* Send command */
14669
+ str = t_str_new(128);
14670
+ str_append(str, "AUTHENTICATE \"PLAIN\" ");
14671
+ managesieve_quote_append_string(str, str_c(base64), FALSE);
14672
+ str_append(str, "\r\n");
14673
+ (void)o_stream_send(output, str_data(str), str_len(str));
14678
+ /* Cleanup sensitive data */
14679
+ safe_memset(client->proxy_password, 0,
14680
+ strlen(client->proxy_password));
14681
+ i_free(client->proxy_password);
14682
+ client->proxy_password = NULL;
14683
+ client->proxy_login_sent = TRUE;
14688
+ if (strncasecmp(line, "OK ", 3) == 0) {
14689
+ /* Login successful. Send this line to client. */
14690
+ o_stream_cork(client->output);
14691
+ (void)o_stream_send_str(client->output, line);
14692
+ (void)o_stream_send(client->output, "\r\n", 2);
14693
+ o_stream_uncork(client->output);
14695
+ msg = t_strdup_printf("managesieve-proxy(%s): started proxying to %s:%u",
14696
+ client->common.virtual_user,
14697
+ login_proxy_get_host(client->proxy),
14698
+ login_proxy_get_port(client->proxy));
14700
+ (void)client_skip_line(client);
14701
+ login_proxy_detach(client->proxy, client->input,
14704
+ client->proxy = NULL;
14705
+ client->input = NULL;
14706
+ client->output = NULL;
14707
+ client->common.fd = -1;
14708
+ client_destroy(client, msg);
14711
+ /* Login failed. Send our own failure reply so client can't
14712
+ * figure out if user exists or not just by looking at the
14715
+ client_send_no(client, AUTH_FAILED_MSG);
14717
+ /* allow client input again */
14718
+ i_assert(client->io == NULL);
14719
+ client->io = io_add(client->common.fd, IO_READ,
14720
+ client_input, client);
14722
+ login_proxy_free(client->proxy);
14723
+ client->proxy = NULL;
14725
+ i_free(client->proxy_user);
14726
+ client->proxy_user = NULL;
14736
+static void proxy_input(struct istream *input, struct ostream *output,
14739
+ struct managesieve_client *client = context;
14740
+ const char *line;
14742
+ if (input == NULL) {
14743
+ if (client->io != NULL) {
14744
+ /* remote authentication failed, we're just
14745
+ freeing the proxy */
14749
+ if (client->destroyed) {
14750
+ /* we came here from client_destroy() */
14754
+ /* failed for some reason, probably server disconnected */
14755
+ client_send_byeresp(client, "TRYLATER", "Temporary login failure.");
14756
+ client_destroy(client, NULL);
14760
+ i_assert(!client->destroyed);
14762
+ switch (i_stream_read(input)) {
14764
+ /* buffer full */
14765
+ i_error("managesieve-proxy(%s): Remote input buffer full",
14766
+ client->common.virtual_user);
14767
+ client_destroy_internal_failure(client);
14770
+ /* disconnected */
14771
+ client_destroy(client, "Proxy: Remote disconnected");
14775
+ while ((line = i_stream_next_line(input)) != NULL) {
14776
+ if (proxy_input_line(client, output, line) < 0)
14781
+int managesieve_proxy_new(struct managesieve_client *client, const char *host,
14782
+ unsigned int port, const char *user, const char *password)
14784
+ i_assert(user != NULL);
14785
+ i_assert(!client->destroyed);
14787
+ if (password == NULL) {
14788
+ i_error("proxy(%s): password not given",
14789
+ client->common.virtual_user);
14793
+ i_assert(client->refcount > 1);
14794
+ connection_queue_add(1);
14796
+ if (client->destroyed) {
14797
+ /* connection_queue_add() decided that we were the oldest
14798
+ connection and killed us. */
14802
+ client->proxy = login_proxy_new(&client->common, host, port,
14803
+ proxy_input, client);
14804
+ if (client->proxy == NULL)
14807
+ client->proxy_login_sent = FALSE;
14808
+ client->proxy_user = i_strdup(user);
14809
+ client->proxy_password = i_strdup(password);
14811
+ /* disable input until authentication is finished */
14812
+ if (client->io != NULL)
14813
+ io_remove(&client->io);
14817
diff -r 894f003d9f5f src/managesieve-login/managesieve-proxy.h
14818
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
14819
+++ b/src/managesieve-login/managesieve-proxy.h Sun May 04 16:00:59 2008 +0200
14821
+#ifndef __MANAGESIEVE_PROXY_H
14822
+#define __MANAGESIEVE_PROXY_H
14824
+#include "login-proxy.h"
14826
+int managesieve_proxy_new(struct managesieve_client *client, const char *host,
14827
+ unsigned int port, const char *user, const char *password);
14830
diff -r 894f003d9f5f src/managesieve/Makefile.am
14831
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
14832
+++ b/src/managesieve/Makefile.am Sun May 04 16:00:59 2008 +0200
14834
+pkglibexecdir = $(libexecdir)/dovecot
14836
+pkglibexec_PROGRAMS = managesieve
14839
+ -I$(top_srcdir)/src/lib \
14840
+ -I$(top_srcdir)/src/lib-dict \
14841
+ -I$(top_srcdir)/src/lib-managesieve \
14842
+ -DMODULEDIR=\""$(moduledir)"\" \
14843
+ -I$(top_srcdir)/src/lib-sievestorage \
14844
+ -I$(top_srcdir)/src/lib-sieve
14846
+managesieve_LDFLAGS = -export-dynamic
14848
+# get some functions included which only plugins use. liblib should probably
14849
+# be a shared library so this wouldn't be needed..
14850
+unused_objects = \
14851
+ ../lib/mountpoint.o
14854
+ ../lib-managesieve/libmanagesieve.a \
14855
+ ../lib-sievestorage/libsievestorage.a \
14856
+ ../lib-sieve/libsieve.la \
14857
+ ../lib-dict/libdict.a \
14858
+ ../lib-charset/libcharset.a \
14859
+ ../lib/liblib.a \
14860
+ $(unused_objects)
14862
+managesieve_LDADD = \
14868
+managesieve_DEPENDENCIES = $(libs)
14871
+ cmd-capability.c \
14873
+ cmd-putscript.c \
14874
+ cmd-getscript.c \
14875
+ cmd-setactive.c \
14876
+ cmd-deletescript.c \
14877
+ cmd-listscripts.c \
14880
+managesieve_SOURCES = \
14886
+noinst_HEADERS = \
14890
diff -r 894f003d9f5f src/managesieve/client.c
14891
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
14892
+++ b/src/managesieve/client.c Sun May 04 16:00:59 2008 +0200
14894
+#include "common.h"
14896
+#include "ioloop.h"
14897
+#include "network.h"
14898
+#include "istream.h"
14899
+#include "ostream.h"
14900
+#include "commands.h"
14902
+#include "managesieve-quote.h"
14903
+#include "sieve-storage.h"
14904
+#include "sieve-implementation.h"
14906
+#include "client.h"
14908
+#include <stdlib.h>
14909
+#include <unistd.h>
14911
+static struct client *my_client; /* we don't need more than one currently */
14912
+static struct timeout *to_idle;
14914
+struct client *client_create(int fd_in, int fd_out, struct sieve_storage *storage)
14916
+ struct client *client;
14918
+ /* always use nonblocking I/O */
14919
+ net_set_nonblock(fd_in, TRUE);
14920
+ net_set_nonblock(fd_out, TRUE);
14922
+ client = i_new(struct client, 1);
14923
+ client->fd_in = fd_in;
14924
+ client->fd_out = fd_out;
14925
+ client->input = i_stream_create_file(fd_in, default_pool,
14926
+ managesieve_max_line_length, FALSE);
14927
+ client->output = o_stream_create_file(fd_out, default_pool,
14928
+ (size_t)-1, FALSE);
14930
+ o_stream_set_flush_callback(client->output, _client_output, client);
14932
+ client->io = io_add(fd_in, IO_READ, _client_input, client);
14933
+ client->parser = managesieve_parser_create(client->input, client->output,
14934
+ managesieve_max_line_length);
14935
+ client->last_input = ioloop_time;
14937
+ client->cmd.pool = pool_alloconly_create("command pool", 8192);
14938
+ client->cmd.client = client;
14940
+ client->storage = storage;
14942
+ i_assert(my_client == NULL);
14943
+ my_client = client;
14945
+ if (hook_client_created != NULL)
14946
+ hook_client_created(&client);
14950
+static const char *client_get_disconnect_reason(struct client *client)
14952
+ errno = client->input->stream_errno != 0 ?
14953
+ client->input->stream_errno :
14954
+ client->output->stream_errno;
14955
+ return errno == 0 || errno == EPIPE ? "Connection closed" :
14956
+ t_strdup_printf("Connection closed: %m");
14959
+void client_destroy(struct client *client, const char *reason)
14963
+ i_assert(!client->handling_input);
14964
+ i_assert(!client->destroyed);
14965
+ client->destroyed = TRUE;
14967
+ if (!client->disconnected) {
14968
+ client->disconnected = TRUE;
14969
+ if (reason == NULL)
14970
+ reason = client_get_disconnect_reason(client);
14971
+ i_info("%s", reason);
14974
+ if (client->command_pending) {
14975
+ /* try to deinitialize the command */
14976
+ i_assert(client->cmd.func != NULL);
14977
+ i_stream_close(client->input);
14978
+ o_stream_close(client->output);
14979
+ client->input_pending = FALSE;
14981
+ ret = client->cmd.func(&client->cmd);
14985
+ managesieve_parser_destroy(&client->parser);
14986
+ if (client->io != NULL)
14987
+ io_remove(&client->io);
14989
+ i_stream_destroy(&client->input);
14990
+ o_stream_destroy(&client->output);
14992
+ if (close(client->fd_in) < 0)
14993
+ i_error("close(client in) failed: %m");
14994
+ if (client->fd_in != client->fd_out) {
14995
+ if (close(client->fd_out) < 0)
14996
+ i_error("close(client out) failed: %m");
14999
+ pool_unref(client->cmd.pool);
15002
+ /* quit the program */
15003
+ my_client = NULL;
15004
+ io_loop_stop(ioloop);
15007
+void client_disconnect(struct client *client, const char *reason)
15009
+ i_assert(reason != NULL);
15011
+ if (client->disconnected)
15014
+ i_info("Disconnected: %s", reason);
15015
+ client->disconnected = TRUE;
15016
+ (void)o_stream_flush(client->output);
15018
+ i_stream_close(client->input);
15019
+ o_stream_close(client->output);
15022
+void client_disconnect_with_error(struct client *client, const char *msg)
15024
+ client_send_bye(client, msg);
15025
+ client_disconnect(client, msg);
15028
+int client_send_line(struct client *client, const char *data)
15030
+ struct const_iovec iov[2];
15032
+ if (client->output->closed)
15035
+ iov[0].iov_base = data;
15036
+ iov[0].iov_len = strlen(data);
15037
+ iov[1].iov_base = "\r\n";
15038
+ iov[1].iov_len = 2;
15040
+ if (o_stream_sendv(client->output, iov, 2) < 0)
15042
+ client->last_output = ioloop_time;
15044
+ if (o_stream_get_buffer_used_size(client->output) >=
15045
+ CLIENT_OUTPUT_OPTIMAL_SIZE) {
15046
+ /* buffer full, try flushing */
15047
+ return o_stream_flush(client->output);
15052
+void client_send_response(struct client *client,
15053
+ const char *oknobye, const char *resp_code, const char *msg)
15057
+ str = t_str_new(128);
15058
+ str_append(str, oknobye);
15060
+ if ( resp_code != NULL ) {
15061
+ str_append(str, " (");
15062
+ str_append(str, resp_code);
15063
+ str_append_c(str, ')');
15066
+ if ( msg != NULL ) {
15067
+ str_append_c(str, ' ');
15068
+ managesieve_quote_append_string(str, msg, TRUE);
15071
+ client_send_line(client, str_c(str));
15074
+void client_send_command_error(struct client_command_context *cmd,
15077
+ struct client *client = cmd->client;
15078
+ const char *error, *cmd_name;
15081
+ if (msg == NULL) {
15082
+ msg = managesieve_parser_get_error(client->parser, &fatal);
15084
+ client_disconnect_with_error(client, msg);
15089
+ if (cmd->name == NULL)
15090
+ error = t_strconcat
15091
+ ("Error in MANAGESIEVE command: ", msg, NULL);
15093
+ cmd_name = t_str_ucase(cmd->name);
15094
+ error = t_strconcat
15095
+ ("Error in MANAGESIEVE command ", cmd_name, ": ", msg, NULL);
15098
+ client_send_no(client, error);
15100
+ if (++client->bad_counter >= CLIENT_MAX_BAD_COMMANDS) {
15101
+ client_disconnect_with_error(client,
15102
+ "Too many invalid MANAGESIEVE commands.");
15105
+ /* client_read_args() failures rely on this being set, so that the
15106
+ command processing is stopped even while command function returns
15108
+ cmd->param_error = TRUE;
15111
+void client_send_storage_error(struct client *client,
15112
+ struct sieve_storage *storage)
15114
+ const char *error;
15116
+ error = sieve_storage_get_last_error(storage);
15118
+ client_send_no(client, error);
15121
+void client_send_sieve_error(struct client *client)
15123
+ const char *error;
15125
+ error = sieve_get_last_error();
15127
+ client_send_no(client, error);
15130
+bool client_read_args(struct client_command_context *cmd, unsigned int count,
15131
+ unsigned int flags, struct managesieve_arg **args)
15135
+ i_assert(count <= INT_MAX);
15137
+ ret = managesieve_parser_read_args(cmd->client->parser, count, flags, args);
15138
+ if (ret >= (int)count) {
15139
+ /* all parameters read successfully */
15141
+ } else if (ret == -2) {
15142
+ /* need more data */
15145
+ /* error, or missing arguments */
15146
+ client_send_command_error(cmd, ret < 0 ? NULL :
15147
+ "Missing arguments");
15152
+bool client_read_string_args(struct client_command_context *cmd,
15153
+ unsigned int count, ...)
15155
+ struct managesieve_arg *managesieve_args;
15160
+ if (!client_read_args(cmd, count, 0, &managesieve_args))
15163
+ va_start(va, count);
15164
+ for (i = 0; i < count; i++) {
15165
+ const char **ret = va_arg(va, const char **);
15167
+ if (managesieve_args[i].type == MANAGESIEVE_ARG_EOL) {
15168
+ client_send_command_error(cmd, "Missing arguments.");
15172
+ str = managesieve_arg_string(&managesieve_args[i]);
15173
+ if (str == NULL) {
15174
+ client_send_command_error(cmd, "Invalid arguments.");
15183
+ return i == count;
15186
+void _client_reset_command(struct client *client)
15191
+ /* reset input idle time because command output might have taken a
15192
+ long time and we don't want to disconnect client immediately then */
15193
+ client->last_input = ioloop_time;
15195
+ client->command_pending = FALSE;
15196
+ if (client->io == NULL && !client->disconnected) {
15197
+ i_assert(i_stream_get_fd(client->input) >= 0);
15198
+ client->io = io_add(i_stream_get_fd(client->input),
15199
+ IO_READ, _client_input, client);
15201
+ o_stream_set_flush_callback(client->output, _client_output, client);
15203
+ pool = client->cmd.pool;
15204
+ memset(&client->cmd, 0, sizeof(client->cmd));
15207
+ client->cmd.pool = pool;
15208
+ client->cmd.client = client;
15210
+ managesieve_parser_reset(client->parser);
15212
+ /* if there's unread data in buffer, remember that there's input
15213
+ pending and we should get around to calling client_input() soon.
15214
+ This is mostly for APPEND/IDLE. */
15215
+ (void)i_stream_get_data(client->input, &size);
15216
+ if (size > 0 && !client->destroyed)
15217
+ client->input_pending = TRUE;
15220
+/* Skip incoming data until newline is found,
15221
+ returns TRUE if newline was found. */
15222
+static bool client_skip_line(struct client *client)
15224
+ const unsigned char *data;
15225
+ size_t i, data_size;
15227
+ data = i_stream_get_data(client->input, &data_size);
15229
+ for (i = 0; i < data_size; i++) {
15230
+ if (data[i] == '\n') {
15231
+ client->input_skip_line = FALSE;
15237
+ i_stream_skip(client->input, i);
15238
+ return !client->input_skip_line;
15241
+static bool client_handle_input(struct client_command_context *cmd)
15243
+ struct client *client = cmd->client;
15245
+ if (cmd->func != NULL) {
15246
+ /* command is being executed - continue it */
15247
+ if (cmd->func(cmd) || cmd->param_error) {
15248
+ /* command execution was finished */
15249
+ if (!cmd->param_error)
15250
+ client->bad_counter = 0;
15251
+ _client_reset_command(client);
15256
+ if (client->command_pending)
15257
+ o_stream_set_flush_pending(client->output, TRUE);
15261
+ if (client->input_skip_line) {
15262
+ /* we're just waiting for new line.. */
15263
+ if (!client_skip_line(client))
15266
+ /* got the newline */
15267
+ _client_reset_command(client);
15269
+ /* pass through to parse next command */
15272
+ if (cmd->name == NULL) {
15273
+ cmd->name = managesieve_parser_read_word(client->parser);
15274
+ if (cmd->name == NULL)
15275
+ return FALSE; /* need more data */
15276
+ cmd->name = p_strdup(cmd->pool, cmd->name);
15279
+ if (cmd->name == '\0') {
15280
+ /* command not given - cmd_func is already NULL. */
15282
+ /* find the command function */
15283
+ cmd->func = command_find(cmd->name);
15286
+ client->input_skip_line = TRUE;
15287
+ if (cmd->func == NULL) {
15288
+ /* unknown command */
15289
+ client_send_command_error(cmd, "Unknown command.");
15290
+ _client_reset_command(client);
15292
+ i_assert(!client->disconnected);
15294
+ client_handle_input(cmd);
15300
+void _client_input(void *context)
15302
+ struct client *client = context;
15303
+ struct client_command_context *cmd = &client->cmd;
15306
+ if (client->command_pending) {
15307
+ /* already processing one command. wait. */
15308
+ io_remove(&client->io);
15312
+ client->input_pending = FALSE;
15313
+ client->last_input = ioloop_time;
15315
+ switch (i_stream_read(client->input)) {
15317
+ /* disconnected */
15318
+ client_destroy(client, NULL);
15321
+ /* parameter word is longer than max. input buffer size.
15322
+ this is most likely an error, so skip the new data
15323
+ until newline is found. */
15324
+ client->input_skip_line = TRUE;
15326
+ client_send_command_error(cmd, "Too long argument.");
15327
+ _client_reset_command(client);
15331
+ client->handling_input = TRUE;
15332
+ o_stream_cork(client->output);
15335
+ ret = client_handle_input(cmd);
15337
+ } while (ret && !client->disconnected);
15338
+ o_stream_uncork(client->output);
15339
+ client->handling_input = FALSE;
15341
+ if (client->command_pending)
15342
+ client->input_pending = TRUE;
15344
+ if (client->output->closed)
15345
+ client_destroy(client, NULL);
15348
+int _client_output(void *context)
15350
+ struct client *client = context;
15351
+ struct client_command_context *cmd = &client->cmd;
15355
+ client->last_output = ioloop_time;
15357
+ if ((ret = o_stream_flush(client->output)) < 0) {
15358
+ client_destroy(client, NULL);
15362
+ if (!client->command_pending)
15365
+ /* continue processing command */
15366
+ o_stream_cork(client->output);
15367
+ client->output_pending = TRUE;
15368
+ finished = cmd->func(cmd) || cmd->param_error;
15370
+ /* a bit kludgy check. normally we would want to get back to this
15371
+ output handler, but IDLE is a special case which has command
15372
+ pending but without necessarily anything to write. */
15373
+ if (!finished && client->output_pending)
15374
+ o_stream_set_flush_pending(client->output, TRUE);
15376
+ o_stream_uncork(client->output);
15379
+ /* command execution was finished */
15380
+ client->bad_counter = 0;
15381
+ _client_reset_command(client);
15383
+ if (client->input_pending)
15384
+ _client_input(client);
15389
+static void idle_timeout(void *context __attr_unused__)
15391
+ time_t idle_time, last_change;
15393
+ if (my_client == NULL)
15396
+ /* We mostly want to check last_input here, but if there is a very long
15397
+ running command (like copying thousands of messages), we don't want
15398
+ to disconnect the client just after the command was finished.
15399
+ But any output that IDLE has sent should be ignored. */
15400
+ last_change = I_MAX(my_client->last_input, my_client->last_output);
15401
+ idle_time = ioloop_time - last_change;
15403
+ if (my_client->command_pending &&
15404
+ o_stream_get_buffer_used_size(my_client->output) > 0 &&
15405
+ idle_time >= CLIENT_OUTPUT_TIMEOUT) {
15406
+ /* client isn't reading our output */
15407
+ client_destroy(my_client, "Disconnected for inactivity "
15408
+ "in reading our output");
15409
+ } else if (idle_time >= CLIENT_IDLE_TIMEOUT) {
15410
+ /* client isn't sending us anything */
15411
+ if (!my_client->command_pending) {
15412
+ client_send_bye(my_client,
15413
+ "Disconnected for inactivity.");
15415
+ client_destroy(my_client, "Disconnected for inactivity");
15419
+void clients_init(void)
15421
+ my_client = NULL;
15422
+ to_idle = timeout_add(10000, idle_timeout, NULL);
15425
+void clients_deinit(void)
15427
+ if (my_client != NULL) {
15428
+ client_send_bye(my_client, "Server shutting down.");
15429
+ client_destroy(my_client, "Server shutting down");
15432
+ timeout_remove(&to_idle);
15435
diff -r 894f003d9f5f src/managesieve/client.h
15436
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
15437
+++ b/src/managesieve/client.h Sun May 04 16:00:59 2008 +0200
15439
+#ifndef __CLIENT_H
15440
+#define __CLIENT_H
15442
+#include "commands.h"
15445
+struct sieve_storage;
15446
+struct managesieve_parser;
15447
+struct managesieve_arg;
15449
+struct client_command_context {
15450
+ struct client *client;
15453
+ const char *name;
15455
+ command_func_t *func;
15458
+ unsigned int param_error:1;
15462
+ int fd_in, fd_out;
15463
+ struct sieve_storage *storage;
15466
+ struct istream *input;
15467
+ struct ostream *output;
15469
+ time_t last_input, last_output;
15470
+ unsigned int bad_counter;
15472
+ struct managesieve_parser *parser;
15473
+ struct client_command_context cmd;
15475
+ unsigned int disconnected:1;
15476
+ unsigned int destroyed:1;
15477
+ unsigned int command_pending:1;
15478
+ unsigned int input_pending:1;
15479
+ unsigned int output_pending:1;
15480
+ unsigned int handling_input:1;
15481
+ unsigned int rawlog:1;
15482
+ unsigned int input_skip_line:1; /* skip all the data until we've
15483
+ found a new line */
15486
+/* Create new client with specified input/output handles. socket specifies
15487
+ if the handle is a socket. */
15488
+struct client *client_create(int fd_in, int fd_out, struct sieve_storage *storage);
15489
+void client_destroy(struct client *client, const char *reason);
15491
+/* Disconnect client connection */
15492
+void client_disconnect(struct client *client, const char *reason);
15493
+void client_disconnect_with_error(struct client *client, const char *msg);
15495
+/* Send a line of data to client. Returns 1 if ok, 0 if buffer is getting full,
15497
+int client_send_line(struct client *client, const char *data);
15499
+void client_send_response(struct client *client,
15500
+ const char *oknobye, const char *resp_code, const char *msg);
15502
+#define client_send_ok(client, msg) \
15503
+ client_send_response(client, "OK", NULL, msg)
15504
+#define client_send_no(client, msg) \
15505
+ client_send_response(client, "NO", NULL, msg)
15506
+#define client_send_bye(client, msg) \
15507
+ client_send_response(client, "BYE", NULL, msg)
15509
+#define client_send_okresp(client, resp_code, msg) \
15510
+ client_send_response(client, "OK", resp_code, msg)
15511
+#define client_send_noresp(client, resp_code, msg) \
15512
+ client_send_response(client, "NO", resp_code, msg)
15513
+#define client_send_byeresp(cmd, resp_code, msg) \
15514
+ client_send_response(client, "BYE", resp_code, msg)
15516
+/* Send BAD command error to client. msg can be NULL. */
15517
+void client_send_command_error(struct client_command_context *cmd,
15518
+ const char *msg);
15520
+/* Send storage or sieve related errors to the client */
15521
+void client_send_storage_error(struct client *client,
15522
+ struct sieve_storage *storage);
15523
+void client_send_sieve_error(struct client *client);
15525
+/* Read a number of arguments. Returns TRUE if everything was read or
15526
+ FALSE if either needs more data or error occurred. */
15527
+bool client_read_args(struct client_command_context *cmd, unsigned int count,
15528
+ unsigned int flags, struct managesieve_arg **args);
15529
+/* Reads a number of string arguments. ... is a list of pointers where to
15530
+ store the arguments. */
15531
+bool client_read_string_args(struct client_command_context *cmd,
15532
+ unsigned int count, ...);
15534
+void clients_init(void);
15535
+void clients_deinit(void);
15537
+void _client_reset_command(struct client *client);
15538
+void _client_input(void *context);
15539
+int _client_output(void *context);
15542
diff -r 894f003d9f5f src/managesieve/cmd-capability.c
15543
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
15544
+++ b/src/managesieve/cmd-capability.c Sun May 04 16:00:59 2008 +0200
15546
+#include "common.h"
15547
+#include "commands.h"
15549
+#include "strfuncs.h"
15550
+#include "ostream.h"
15551
+#include "sieve-implementation.h"
15553
+bool cmd_capability(struct client_command_context *cmd)
15555
+ struct client *client = cmd->client;
15556
+ const char *sievecap, *sieveimpl;
15558
+ sievecap = sieve_get_capabilities();
15559
+ if (sievecap == NULL)
15563
+ sievecap = t_strconcat("\"SIEVE\" \"", sievecap, "\"", NULL);
15564
+ sieveimpl = t_strconcat("\"IMPLEMENTATION\" \"",
15565
+ managesieve_implementation_string, "\"", NULL);
15567
+ client_send_line(client, sieveimpl);
15568
+ client_send_line(client, sievecap);
15569
+ client_send_line(client, "OK \"Capability completed.\"");
15576
diff -r 894f003d9f5f src/managesieve/cmd-deletescript.c
15577
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
15578
+++ b/src/managesieve/cmd-deletescript.c Sun May 04 16:00:59 2008 +0200
15580
+#include "common.h"
15581
+#include "commands.h"
15583
+#include "sieve-script.h"
15585
+bool cmd_deletescript(struct client_command_context *cmd)
15587
+ struct client *client = cmd->client;
15588
+ struct sieve_storage *storage = client->storage;
15589
+ const char *scriptname;
15590
+ struct sieve_script *script;
15593
+ /* <scrip name>*/
15594
+ if (!client_read_string_args(cmd, 1, &scriptname))
15598
+ script = sieve_script_init(storage, scriptname, &exists);
15600
+ if (script == NULL) {
15602
+ client_send_no(client, "Script does not exist.");
15604
+ client_send_storage_error(client, storage);
15609
+ if (sieve_script_delete(&script) < 0)
15610
+ client_send_storage_error(client, storage);
15612
+ client_send_ok(client, "Deletescript completed.");
15614
+ /* Script object is deleted no matter what in
15615
+ * sieve_script_delete()
15620
diff -r 894f003d9f5f src/managesieve/cmd-getscript.c
15621
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
15622
+++ b/src/managesieve/cmd-getscript.c Sun May 04 16:00:59 2008 +0200
15624
+#include "common.h"
15625
+#include "ostream.h"
15626
+#include "commands.h"
15627
+#include "istream.h"
15628
+#include "sieve-script.h"
15630
+struct cmd_getscript_context {
15631
+ struct client *client;
15632
+ struct client_command_context *cmd;
15633
+ struct sieve_storage *storage;
15634
+ uoff_t scriptsize;
15636
+ struct sieve_script *script;
15637
+ struct istream *scriptstream;
15642
+static bool cmd_getscript_finish(struct cmd_getscript_context *ctx)
15644
+ struct client *client = ctx->client;
15646
+ if (ctx->script != NULL)
15647
+ sieve_script_unref(&ctx->script);
15649
+ if (ctx->failed) {
15650
+ if (client->output->closed) {
15651
+ client_disconnect(client, "Disconnected");
15655
+ if (!ctx->exists) {
15656
+ client_send_no(client, "Script does not exist.");
15660
+ client_send_storage_error(client, client->storage);
15664
+ client_send_line(client, "");
15665
+ client_send_ok(client, "Getscript completed.");
15669
+static bool cmd_getscript_continue(struct client_command_context *cmd)
15671
+ struct client *client = cmd->client;
15672
+ struct cmd_getscript_context *ctx = cmd->context;
15674
+ if (o_stream_send_istream(client->output, ctx->scriptstream) < 0) {
15675
+ sieve_storage_set_critical(ctx->storage,
15676
+ "o_stream_send_istream(%s) failed: %m", sieve_script_name(ctx->script));
15677
+ ctx->failed = TRUE;
15680
+ /* FIXME: Check whether there is a bug in the io_stream_sendfile function
15681
+ * as the eof indicator of the input stream is never set. The stream_sendfile
15682
+ * function does not use read functions of the inputstream and therefore
15683
+ * the eof indicator will not be updated. Workaround: check v_offset == size
15686
+ if (ctx->scriptstream->eof || ctx->scriptstream->closed ||
15687
+ ctx->scriptstream->v_offset == ctx->scriptsize ) {
15688
+ if (client->output->closed || ctx->scriptstream->v_offset < ctx->scriptsize)
15689
+ ctx->failed = TRUE;
15690
+ } else if (!ctx->failed)
15694
+ return cmd_getscript_finish(ctx);
15697
+bool cmd_getscript(struct client_command_context *cmd)
15699
+ struct client *client = cmd->client;
15700
+ struct cmd_getscript_context *ctx;
15701
+ const char *scriptname;
15705
+ /* <scriptname> */
15706
+ if (!client_read_string_args(cmd, 1, &scriptname))
15709
+ ctx = p_new(cmd->pool, struct cmd_getscript_context, 1);
15711
+ ctx->client = client;
15712
+ ctx->storage = client->storage;
15713
+ ctx->failed = FALSE;
15715
+ ctx->exists = TRUE;
15716
+ ctx->script = sieve_script_init(client->storage, scriptname, &ctx->exists);
15718
+ if (ctx->script == NULL) {
15719
+ ctx->failed = TRUE;
15720
+ return cmd_getscript_finish(ctx);
15723
+ ctx->scriptstream = sieve_script_open(ctx->script, &deleted_r);
15725
+ if ( ctx->scriptstream == NULL ) {
15726
+ ctx->failed = TRUE;
15727
+ ctx->exists = !deleted_r;
15728
+ return cmd_getscript_finish(ctx);
15731
+ ctx->scriptsize = 0;
15732
+ if ( (ret=sieve_script_get_size(ctx->script, &ctx->scriptsize)) <= 0 ) {
15733
+ ctx->failed = TRUE;
15734
+ ctx->exists = (ret < 0);
15735
+ return cmd_getscript_finish(ctx);
15738
+ client_send_line(client, t_strdup_printf("{%"PRIuUOFF_T"}", ctx->scriptsize));
15740
+ client->command_pending = TRUE;
15741
+ cmd->func = cmd_getscript_continue;
15742
+ cmd->context = ctx;
15744
+ return cmd_getscript_continue(cmd);
15746
diff -r 894f003d9f5f src/managesieve/cmd-havespace.c
15747
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
15748
+++ b/src/managesieve/cmd-havespace.c Sun May 04 16:00:59 2008 +0200
15750
+#include "common.h"
15751
+#include "commands.h"
15753
+bool cmd_havespace(struct client_command_context *cmd)
15755
+ struct client *client = cmd->client;
15756
+ struct managesieve_arg *args;
15757
+ const char *scriptname;
15761
+ /* <scriptname> <size> */
15762
+ if (!(ret=client_read_args(cmd, 2, 0, &args)))
15766
+ client_send_no(client, "Too many arguments");
15770
+ if ( (scriptname = managesieve_arg_string(&args[0])) == NULL ) {
15771
+ client_send_no(client, "Invalid string for scriptname.");
15775
+ if ( managesieve_arg_number(&args[1], &size) < 0 ) {
15776
+ client_send_no(client, "Invalid scriptsize argument.");
15780
+ if ( size == 0 ) {
15781
+ client_send_no(client, "Cannot upload empty script.");
15785
+ client_send_ok(client, "Putscript would succeed.");
15788
diff -r 894f003d9f5f src/managesieve/cmd-listscripts.c
15789
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
15790
+++ b/src/managesieve/cmd-listscripts.c Sun May 04 16:00:59 2008 +0200
15792
+#include "common.h"
15793
+#include "commands.h"
15795
+#include "sieve-storage.h"
15796
+#include "sieve-list.h"
15797
+#include "managesieve-quote.h"
15799
+bool cmd_listscripts(struct client_command_context *cmd)
15801
+ struct client *client = cmd->client;
15802
+ struct sieve_list_context *ctx;
15803
+ const char *scriptname;
15807
+ if ( (ctx = sieve_storage_list_init(client->storage))
15809
+ client_send_storage_error(client, client->storage);
15813
+ /* FIXME: This will be quite slow for large script lists. Implement
15814
+ * some buffering to fix this. Wont truely be an issue with managesieve
15817
+ while ((scriptname = sieve_storage_list_next(ctx, &active)) != NULL) {
15819
+ str = t_str_new(128);
15821
+ managesieve_quote_append_string(str, scriptname, FALSE);
15824
+ str_append(str, " ACTIVE");
15826
+ client_send_line(client, str_c(str));
15830
+ if ( sieve_storage_list_deinit(&ctx) < 0 ) {
15831
+ client_send_storage_error(client, client->storage);
15835
+ client_send_ok(client, "Listscripts completed.");
15838
diff -r 894f003d9f5f src/managesieve/cmd-logout.c
15839
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
15840
+++ b/src/managesieve/cmd-logout.c Sun May 04 16:00:59 2008 +0200
15842
+#include "common.h"
15843
+#include "ostream.h"
15844
+#include "commands.h"
15846
+bool cmd_logout(struct client_command_context *cmd)
15848
+ struct client *client = cmd->client;
15850
+ client_send_line(client, "OK \"Logout completed.\"");
15851
+ client_disconnect(client, "Logged out");
15854
diff -r 894f003d9f5f src/managesieve/cmd-putscript.c
15855
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
15856
+++ b/src/managesieve/cmd-putscript.c Sun May 04 16:00:59 2008 +0200
15858
+#include "common.h"
15859
+#include "ioloop.h"
15860
+#include "istream.h"
15861
+#include "ostream.h"
15863
+#include "commands.h"
15864
+#include "managesieve-parser.h"
15865
+#include "sieve-storage.h"
15866
+#include "sieve-save.h"
15867
+#include "sieve-implementation.h"
15869
+#include <sys/time.h>
15871
+struct cmd_putscript_context {
15872
+ struct client *client;
15873
+ struct client_command_context *cmd;
15874
+ struct sieve_storage *storage;
15876
+ struct istream *input;
15878
+ const char *scriptname;
15879
+ uoff_t script_size;
15881
+ struct managesieve_parser *save_parser;
15882
+ struct sieve_save_context *save_ctx;
15885
+static void cmd_putscript_finish(struct cmd_putscript_context *ctx);
15886
+static bool cmd_putscript_continue_script(struct client_command_context *cmd);
15888
+static void client_input(void *context)
15890
+ struct client *client = context;
15891
+ struct client_command_context *cmd = &client->cmd;
15893
+ client->last_input = ioloop_time;
15895
+ switch (i_stream_read(client->input)) {
15897
+ /* disconnected */
15898
+ cmd_putscript_finish(cmd->context);
15899
+ /* Reset command so that client_destroy() doesn't try to call
15900
+ cmd_putscript_continue_script() anymore. */
15901
+ _client_reset_command(client);
15902
+ client_destroy(client, "Disconnected in putscript");
15905
+ cmd_putscript_finish(cmd->context);
15906
+ if (client->command_pending) {
15907
+ /* message data, this is handled internally by
15908
+ mailbox_save_continue() */
15912
+ /* parameter word is longer than max. input buffer size.
15913
+ this is most likely an error, so skip the new data
15914
+ until newline is found. */
15915
+ client->input_skip_line = TRUE;
15917
+ client_send_command_error(cmd, "Too long argument.");
15918
+ _client_reset_command(client);
15922
+ if (cmd->func(cmd)) {
15923
+ /* command execution was finished. Note that if cmd_sync()
15924
+ didn't finish, we didn't get here but the input handler
15925
+ has already been moved. So don't do anything important
15928
+ reset command once again to reset cmd_sync()'s changes. */
15929
+ _client_reset_command(client);
15931
+ if (client->input_pending)
15932
+ _client_input(client);
15936
+static void cmd_putscript_finish(struct cmd_putscript_context *ctx)
15938
+ io_remove(&ctx->client->io);
15940
+ managesieve_parser_destroy(&ctx->save_parser);
15942
+ if (ctx->input != NULL)
15943
+ i_stream_unref(&ctx->input);
15945
+ if (ctx->save_ctx != NULL)
15947
+ ctx->client->input_skip_line = TRUE;
15948
+ sieve_storage_save_abort(ctx->save_ctx);
15951
+ ctx->client->bad_counter = 0;
15952
+ o_stream_set_flush_callback(ctx->client->output,
15953
+ _client_output, ctx->client);
15956
+static bool cmd_putscript_continue_cancel(struct client_command_context *cmd)
15958
+ struct cmd_putscript_context *ctx = cmd->context;
15961
+ (void)i_stream_read(ctx->input);
15962
+ (void)i_stream_get_data(ctx->input, &size);
15963
+ i_stream_skip(ctx->input, size);
15965
+ if (ctx->input->v_offset == ctx->script_size ||
15966
+ cmd->client->input->closed) {
15967
+ cmd_putscript_finish(ctx);
15973
+static bool cmd_putscript_cancel(struct cmd_putscript_context *ctx, bool nonsync)
15975
+ ctx->client->input_skip_line = TRUE;
15978
+ cmd_putscript_finish(ctx);
15982
+ /* we have to read the nonsynced literal so we don't treat the message
15983
+ data as commands. */
15984
+ ctx->input = i_stream_create_limit(default_pool, ctx->client->input,
15985
+ ctx->client->input->v_offset,
15986
+ ctx->script_size);
15988
+ ctx->client->command_pending = TRUE;
15989
+ ctx->cmd->func = cmd_putscript_continue_cancel;
15990
+ ctx->cmd->context = ctx;
15991
+ return cmd_putscript_continue_cancel(ctx->cmd);
15994
+static bool cmd_putscript_finish_parsing(struct client_command_context *cmd)
15996
+ struct client *client = cmd->client;
15997
+ struct cmd_putscript_context *ctx = cmd->context;
15998
+ struct managesieve_arg *args;
15999
+ struct sieve_script *script;
16002
+ /* if error occurs, the CRLF is already read. */
16003
+ client->input_skip_line = FALSE;
16005
+ /* <script literal> */
16006
+ ret = managesieve_parser_read_args(ctx->save_parser, 0,
16007
+ MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE, &args);
16010
+ if (ctx->storage != NULL)
16011
+ client_send_command_error(cmd, NULL);
16012
+ cmd_putscript_finish(ctx);
16016
+ /* need more data */
16020
+ if (args[0].type == MANAGESIEVE_ARG_EOL) {
16021
+ /* last message */
16022
+ /* eat away the trailing CRLF */
16023
+ client->input_skip_line = TRUE;
16025
+ script = sieve_storage_save_get_tempscript
16028
+ if ( script == NULL || (sieve_compile(script, TRUE) < 0)) {
16029
+ client_send_sieve_error(client);
16030
+ cmd_putscript_finish(ctx);
16033
+ ret = sieve_storage_save_commit(ctx->save_ctx);
16035
+ client_send_storage_error(client, ctx->storage);
16036
+ cmd_putscript_finish(ctx);
16039
+ ctx->save_ctx = NULL;
16042
+ cmd_putscript_finish(ctx);
16043
+ client_send_ok(client, "Putscript completed.");
16048
+ client_send_command_error(cmd, "Too many command arguments.");
16049
+ cmd_putscript_finish(ctx);
16053
+static bool cmd_putscript_continue_parsing(struct client_command_context *cmd)
16055
+ struct client *client = cmd->client;
16056
+ struct cmd_putscript_context *ctx = cmd->context;
16057
+ struct managesieve_arg *args;
16058
+ bool nonsync = FALSE;
16061
+ /* if error occurs, the CRLF is already read. */
16062
+ client->input_skip_line = FALSE;
16064
+ /* <script literal> */
16065
+ ret = managesieve_parser_read_args(ctx->save_parser, 0,
16066
+ MANAGESIEVE_PARSE_FLAG_LITERAL_SIZE, &args);
16068
+ cmd_putscript_finish(ctx);
16069
+ client_send_command_error(cmd, "Invalid arguments.");
16070
+ client->input_skip_line = TRUE;
16074
+ /* need more data */
16078
+ if (args->type != MANAGESIEVE_ARG_STRING) {
16079
+ /* Validate the script argument */
16080
+ if (args->type != MANAGESIEVE_ARG_LITERAL_SIZE ) {
16081
+ client_send_command_error(cmd, "Invalid arguments.");
16082
+ return cmd_putscript_cancel(ctx, FALSE);
16085
+ ctx->script_size = MANAGESIEVE_ARG_LITERAL_SIZE(args);
16089
+ client_send_no(client, "This MANAGESIEVE implementation currently does not allow "
16090
+ "quoted strings to be used for script contents.");
16091
+ return cmd_putscript_cancel(ctx, FALSE);
16094
+ if (ctx->script_size == 0) {
16095
+ /* no message data, abort */
16096
+ client_send_no(client, "PUTSCRIPT aborted (no message data).");
16097
+ cmd_putscript_finish(ctx);
16101
+ /* save the script */
16102
+ ctx->input = i_stream_create_limit(default_pool, client->input,
16103
+ client->input->v_offset,
16104
+ ctx->script_size);
16105
+ ctx->save_ctx = sieve_storage_save_init(ctx->storage, ctx->scriptname, ctx->input);
16107
+ if ( ctx->save_ctx == NULL ) {
16108
+ /* save initialization failed */
16109
+ client_send_storage_error(client, ctx->storage);
16110
+ return cmd_putscript_cancel(ctx, nonsync);
16113
+ /* after literal comes CRLF, if we fail make sure we eat it away */
16114
+ client->input_skip_line = TRUE;
16116
+ client->command_pending = TRUE;
16117
+ cmd->func = cmd_putscript_continue_script;
16118
+ return cmd_putscript_continue_script(cmd);
16121
+static bool cmd_putscript_continue_script(struct client_command_context *cmd)
16123
+ struct client *client = cmd->client;
16124
+ struct cmd_putscript_context *ctx = cmd->context;
16128
+ if (ctx->save_ctx != NULL) {
16129
+ if (sieve_storage_save_continue(ctx->save_ctx) < 0) {
16130
+ /* we still have to finish reading the script
16132
+ sieve_storage_save_abort(ctx->save_ctx);
16133
+ ctx->save_ctx = NULL;
16137
+ if (ctx->save_ctx == NULL) {
16138
+ (void)i_stream_read(ctx->input);
16139
+ (void)i_stream_get_data(ctx->input, &size);
16140
+ i_stream_skip(ctx->input, size);
16143
+ if (ctx->input->eof || client->input->closed) {
16145
+ bool all_written = ctx->input->v_offset == ctx->script_size;
16147
+ i_stream_unref(&ctx->input);
16148
+ ctx->input = NULL;
16150
+ if (ctx->save_ctx == NULL) {
16151
+ /* failed above */
16152
+ client_send_storage_error(client, ctx->storage);
16154
+ } else if (!all_written) {
16155
+ /* client disconnected before it finished sending the
16156
+ whole message. */
16158
+ sieve_storage_save_abort(ctx->save_ctx);
16159
+ } else if (sieve_storage_save_finish(ctx->save_ctx) < 0) {
16161
+ client_send_storage_error(client, ctx->storage);
16163
+ failed = client->input->closed;
16167
+ cmd_putscript_finish(ctx);
16171
+ /* prepare for next message */
16172
+ client->command_pending = FALSE;
16173
+ managesieve_parser_reset(ctx->save_parser);
16174
+ cmd->func = cmd_putscript_finish_parsing;
16175
+ return cmd_putscript_finish_parsing(cmd);
16181
+bool cmd_putscript(struct client_command_context *cmd)
16183
+ struct client *client = cmd->client;
16184
+ struct cmd_putscript_context *ctx;
16185
+ const char *scriptname;
16187
+ /* <scriptname> */
16188
+ if (!client_read_string_args(cmd, 1, &scriptname) ||
16189
+ *scriptname == '\0')
16192
+ ctx = p_new(cmd->pool, struct cmd_putscript_context, 1);
16194
+ ctx->client = client;
16195
+ ctx->storage = client->storage;
16196
+ ctx->scriptname = scriptname;
16198
+ io_remove(&client->io);
16199
+ client->io = io_add(i_stream_get_fd(client->input), IO_READ,
16200
+ client_input, client);
16201
+ /* putscript is special because we're only waiting on client input, not
16202
+ client output, so disable the standard output handler until we're
16204
+ o_stream_set_flush_callback(client->output, NULL, NULL);
16206
+ ctx->save_parser = managesieve_parser_create(client->input, client->output,
16207
+ managesieve_max_line_length);
16209
+ cmd->func = cmd_putscript_continue_parsing;
16210
+ cmd->context = ctx;
16211
+ return cmd_putscript_continue_parsing(cmd);
16213
diff -r 894f003d9f5f src/managesieve/cmd-setactive.c
16214
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
16215
+++ b/src/managesieve/cmd-setactive.c Sun May 04 16:00:59 2008 +0200
16217
+#include "common.h"
16218
+#include "commands.h"
16220
+#include "sieve-script.h"
16222
+bool cmd_setactive(struct client_command_context *cmd)
16224
+ struct client *client = cmd->client;
16225
+ struct sieve_storage *storage = client->storage;
16226
+ const char *scriptname;
16227
+ struct sieve_script *script;
16231
+ /* <scriptname> */
16232
+ if (!client_read_string_args(cmd, 1, &scriptname))
16235
+ if ( *scriptname != '\0' ) {
16237
+ script = sieve_script_init(storage, scriptname, &exists);
16239
+ if (script == NULL) {
16241
+ client_send_no(client, "Script does not exist.");
16243
+ client_send_storage_error(client, storage);
16248
+ ret = sieve_script_activate(script);
16250
+ client_send_storage_error(client, storage);
16252
+ client_send_ok(client, ret ?
16253
+ "Setactive completed." :
16254
+ "Script is already active.");
16256
+ sieve_script_unref(&script);
16258
+ ret = sieve_storage_deactivate(storage);
16261
+ client_send_storage_error(client, storage);
16263
+ client_send_ok(client, ret ?
16264
+ "Active script is now deactivated." :
16265
+ "No scripts currently active.");
16270
diff -r 894f003d9f5f src/managesieve/commands.c
16271
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
16272
+++ b/src/managesieve/commands.c Sun May 04 16:00:59 2008 +0200
16274
+#include "common.h"
16275
+#include "array.h"
16276
+#include "commands.h"
16278
+#include <stdlib.h>
16280
+/* Might want to combine this somewhere in a commands-common.c
16281
+ * to avoid duplicate code
16284
+const struct command managesieve_commands[] = {
16285
+ { "CAPABILITY", cmd_capability },
16286
+ { "LOGOUT", cmd_logout },
16287
+ { "PUTSCRIPT", cmd_putscript },
16288
+ { "GETSCRIPT", cmd_getscript },
16289
+ { "SETACTIVE", cmd_setactive },
16290
+ { "DELETESCRIPT", cmd_deletescript },
16291
+ { "LISTSCRIPTS", cmd_listscripts },
16292
+ { "HAVESPACE", cmd_havespace }
16295
+#define MANAGESIEVE_COMMANDS_COUNT \
16296
+ (sizeof(managesieve_commands) / sizeof(managesieve_commands[0]))
16298
+static array_t ARRAY_DEFINE(commands, struct command);
16299
+static bool commands_unsorted;
16301
+void command_register(const char *name, command_func_t *func)
16303
+ struct command cmd;
16307
+ array_append(&commands, &cmd, 1);
16309
+ commands_unsorted = TRUE;
16312
+void command_unregister(const char *name)
16314
+ const struct command *cmd;
16315
+ unsigned int i, count;
16317
+ cmd = array_get(&commands, &count);
16318
+ for (i = 0; i < count; i++) {
16319
+ if (strcasecmp(cmd[i].name, name) == 0) {
16320
+ array_delete(&commands, i, 1);
16325
+ i_error("Trying to unregister unknown command '%s'", name);
16328
+void command_register_array(const struct command *cmdarr, unsigned int count)
16330
+ commands_unsorted = TRUE;
16331
+ array_append(&commands, cmdarr, count);
16334
+void command_unregister_array(const struct command *cmdarr, unsigned int count)
16336
+ while (count > 0) {
16337
+ command_unregister(cmdarr->name);
16338
+ count--; cmdarr++;
16342
+static int command_cmp(const void *p1, const void *p2)
16344
+ const struct command *c1 = p1, *c2 = p2;
16346
+ return strcasecmp(c1->name, c2->name);
16349
+static int command_bsearch(const void *name, const void *cmd_p)
16351
+ const struct command *cmd = cmd_p;
16353
+ return strcasecmp(name, cmd->name);
16356
+command_func_t *command_find(const char *name)
16358
+ const struct command *cmd;
16360
+ unsigned int count;
16362
+ base = array_get_modifyable(&commands, &count);
16363
+ if (commands_unsorted) {
16364
+ qsort(base, count, sizeof(struct command), command_cmp);
16365
+ commands_unsorted = FALSE;
16368
+ cmd = bsearch(name, base, count, sizeof(struct command),
16369
+ command_bsearch);
16370
+ return cmd == NULL ? NULL : cmd->func;
16373
+void commands_init(void)
16375
+ ARRAY_CREATE(&commands, system_pool, struct command, 64);
16376
+ commands_unsorted = FALSE;
16378
+ command_register_array(managesieve_commands, MANAGESIEVE_COMMANDS_COUNT);
16381
+void commands_deinit(void)
16383
+ command_unregister_array(managesieve_commands, MANAGESIEVE_COMMANDS_COUNT);
16384
+ array_free(&commands);
16386
diff -r 894f003d9f5f src/managesieve/commands.h
16387
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
16388
+++ b/src/managesieve/commands.h Sun May 04 16:00:59 2008 +0200
16390
+#ifndef __COMMANDS_H
16391
+#define __COMMANDS_H
16393
+struct client_command_context;
16395
+#include "managesieve-parser.h"
16397
+typedef bool command_func_t(struct client_command_context *cmd);
16400
+ const char *name;
16401
+ command_func_t *func;
16404
+/* Register command. Given name parameter must be permanently stored until
16405
+ command is unregistered. */
16406
+void command_register(const char *name, command_func_t *func);
16407
+void command_unregister(const char *name);
16409
+/* Register array of commands. */
16410
+void command_register_array(const struct command *cmdarr, unsigned int count);
16411
+void command_unregister_array(const struct command *cmdarr, unsigned int count);
16413
+command_func_t *command_find(const char *name);
16415
+void commands_init(void);
16416
+void commands_deinit(void);
16418
+/* MANAGESIEVE commands: */
16420
+/* Non-Authenticated State */
16421
+bool cmd_logout(struct client_command_context *cmd);
16423
+bool cmd_capability(struct client_command_context *cmd);
16425
+/* Authenticated State */
16426
+bool cmd_putscript(struct client_command_context *cmd);
16427
+bool cmd_getscript(struct client_command_context *cmd);
16428
+bool cmd_setactive(struct client_command_context *cmd);
16429
+bool cmd_deletescript(struct client_command_context *cmd);
16430
+bool cmd_listscripts(struct client_command_context *cmd);
16431
+bool cmd_havespace(struct client_command_context *cmd);
16435
diff -r 894f003d9f5f src/managesieve/common.h
16436
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
16437
+++ b/src/managesieve/common.h Sun May 04 16:00:59 2008 +0200
16439
+#ifndef __COMMON_H
16440
+#define __COMMON_H
16443
+#include "client.h"
16445
+/* Disconnect client after idling this many seconds */
16446
+#define CLIENT_IDLE_TIMEOUT (60*30)
16448
+/* If we can't send anything to client for this long, disconnect the client */
16449
+#define CLIENT_OUTPUT_TIMEOUT (5*60)
16451
+/* Stop buffering more data into output stream after this many bytes */
16452
+#define CLIENT_OUTPUT_OPTIMAL_SIZE 2048
16454
+/* Disconnect client when it sends too many bad commands in a row */
16455
+#define CLIENT_MAX_BAD_COMMANDS 20
16457
+/* RFC-2683 recommends at least 8000 bytes. Some clients however don't
16458
+ break large message sets to multiple commands, so we're pretty liberal
16460
+#define DEFAULT_MANAGESIEVE_MAX_LINE_LENGTH 65536
16462
+#define DEFAULT_MANAGESIEVE_IMPLEMENTATION_STRING PACKAGE
16464
+enum client_workarounds {
16465
+ WORKAROUND_DELAY_NONE = 0x00,
16468
+extern struct ioloop *ioloop;
16469
+extern unsigned int managesieve_max_line_length;
16470
+extern const char *managesieve_implementation_string;
16471
+extern enum client_workarounds client_workarounds;
16473
+//extern void (*hook_mail_storage_created)(struct sieve_storage *storage);
16474
+extern void (*hook_client_created)(struct client **client);
16477
diff -r 894f003d9f5f src/managesieve/main.c
16478
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
16479
+++ b/src/managesieve/main.c Sun May 04 16:00:59 2008 +0200
16481
+#include "common.h"
16482
+#include "ioloop.h"
16483
+#include "network.h"
16484
+#include "ostream.h"
16486
+#include "lib-signals.h"
16487
+#include "restrict-access.h"
16488
+#include "fd-close-on-exec.h"
16489
+#include "process-title.h"
16490
+#include "randgen.h"
16491
+#include "module-dir.h"
16492
+#include "dict-client.h"
16493
+#include "commands.h"
16494
+#include "sieve-storage.h"
16495
+#include "sieve-implementation.h"
16497
+#include <stdio.h>
16498
+#include <stdlib.h>
16499
+#include <unistd.h>
16500
+#include <syslog.h>
16502
+#define IS_STANDALONE() \
16503
+ (getenv("LOGGED_IN") == NULL)
16505
+#define CRITICAL_MSG \
16506
+ "Internal error occured. Refer to server log for more information."
16507
+#define CRITICAL_MSG_STAMP CRITICAL_MSG " [%Y-%m-%d %H:%M:%S]"
16509
+struct client_workaround_list {
16510
+ const char *name;
16511
+ enum client_workarounds num;
16514
+struct client_workaround_list client_workaround_list[] = {
16518
+struct ioloop *ioloop;
16519
+unsigned int managesieve_max_line_length;
16520
+const char *managesieve_implementation_string;
16521
+enum client_workarounds client_workarounds = 0;
16522
+static struct io *log_io = NULL;
16524
+static char log_prefix[128]; /* syslog() needs this to be permanent */
16526
+//void (*hook_mail_storage_created)(struct mail_storage *storage) = NULL;
16527
+void (*hook_client_created)(struct client **client) = NULL;
16529
+static void sig_die(int signo, void *context __attr_unused__)
16531
+ /* warn about being killed because of some signal, except SIGINT (^C)
16532
+ which is too common at least while testing :) */
16533
+ if (signo != SIGINT)
16534
+ i_warning("Killed with signal %d", signo);
16535
+ io_loop_stop(ioloop);
16538
+static void log_error_callback(void *context __attr_unused__)
16540
+ io_loop_stop(ioloop);
16543
+static void parse_workarounds(void)
16545
+ struct client_workaround_list *list;
16546
+ const char *env, *const *str;
16548
+ env = getenv("MANAGESIEVE_CLIENT_WORKAROUNDS");
16552
+ for (str = t_strsplit_spaces(env, " ,"); *str != NULL; str++) {
16553
+ list = client_workaround_list;
16554
+ for (; list->name != NULL; list++) {
16555
+ if (strcasecmp(*str, list->name) == 0) {
16556
+ client_workarounds |= list->num;
16560
+ if (list->name == NULL)
16561
+ i_fatal("Unknown client workaround: %s", *str);
16565
+static void open_logfile(void)
16567
+ const char *user;
16569
+ if (getenv("LOG_TO_MASTER") != NULL) {
16570
+ i_set_failure_internal();
16574
+ if (getenv("LOG_PREFIX") != NULL)
16575
+ strocpy(log_prefix, getenv("LOG_PREFIX"), sizeof(log_prefix));
16577
+ user = getenv("USER");
16578
+ if (user == NULL) {
16579
+ if (IS_STANDALONE())
16580
+ user = getlogin();
16581
+ if (user == NULL)
16584
+ if (strlen(user) >= sizeof(log_prefix)-6) {
16585
+ /* quite a long user name, cut it */
16586
+ user = t_strndup(user, sizeof(log_prefix)-6-2);
16587
+ user = t_strconcat(user, "..", NULL);
16589
+ i_snprintf(log_prefix, sizeof(log_prefix), "imap(%s): ", user);
16592
+ if (getenv("USE_SYSLOG") != NULL) {
16593
+ const char *env = getenv("SYSLOG_FACILITY");
16594
+ i_set_failure_syslog(log_prefix, LOG_NDELAY,
16595
+ env == NULL ? LOG_MAIL : atoi(env));
16597
+ /* log to file or stderr */
16598
+ i_set_failure_file(getenv("LOGFILE"), log_prefix);
16601
+ if (getenv("INFOLOGFILE") != NULL)
16602
+ i_set_info_file(getenv("INFOLOGFILE"));
16604
+ i_set_failure_timestamp_format(getenv("LOGSTAMP"));
16607
+static void drop_privileges(void)
16609
+ const char *version;
16611
+ version = getenv("DOVECOT_VERSION");
16612
+ if (version != NULL && strcmp(version, PACKAGE_VERSION) != 0) {
16613
+ i_fatal("Dovecot version mismatch: "
16614
+ "Master is v%s, managesieve is v"PACKAGE_VERSION" "
16615
+ "(if you don't care, set version_ignore=yes)", version);
16618
+ /* Log file or syslog opening probably requires roots */
16621
+ /* Most likely needed. Have to open /dev/urandom before possible
16625
+ restrict_access_by_env(!IS_STANDALONE());
16628
+static void internal_error()
16633
+ tm = localtime(&ioloop_time);
16635
+ printf("BYE \"%s\"\n",
16636
+ strftime(str, sizeof(str), CRITICAL_MSG_STAMP, tm) > 0 ?
16637
+ i_strdup(str) : i_strdup(CRITICAL_MSG));
16640
+static void main_init(void)
16642
+ struct sieve_storage *storage;
16643
+ struct client *client;
16644
+ const char *user, *str, *sieve_storage, *mail;
16646
+ lib_signals_init();
16647
+ lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
16648
+ lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
16649
+ lib_signals_ignore(SIGPIPE);
16650
+ lib_signals_set_handler(SIGALRM, FALSE, NULL, NULL);
16652
+ user = getenv("USER");
16653
+ if (user == NULL) {
16654
+ if (IS_STANDALONE())
16655
+ user = getlogin();
16656
+ if (user == NULL) {
16657
+ internal_error();
16658
+ i_fatal("USER environment missing");
16662
+ if (getenv("DEBUG") != NULL) {
16663
+ const char *home;
16665
+ home = getenv("HOME");
16666
+ i_info("Effective uid=%s, gid=%s, home=%s",
16667
+ dec2str(geteuid()), dec2str(getegid()),
16668
+ home != NULL ? home : "(none)");
16671
+ if (getenv("STDERR_CLOSE_SHUTDOWN") != NULL) {
16672
+ /* If master dies, the log fd gets closed and we'll quit */
16673
+ log_io = io_add(STDERR_FILENO, IO_ERROR,
16674
+ log_error_callback, NULL);
16678
+ sieve_set_implementation("cmu");
16680
+ dict_client_register();
16685
+ str = getenv("MANAGESIEVE_MAX_LINE_LENGTH");
16686
+ managesieve_max_line_length = str != NULL ?
16687
+ (unsigned int)strtoul(str, NULL, 10) :
16688
+ DEFAULT_MANAGESIEVE_MAX_LINE_LENGTH;
16690
+ str = getenv("MANAGESIEVE_IMPLEMENTATION_STRING");
16691
+ managesieve_implementation_string = str != NULL ?
16692
+ str : DEFAULT_MANAGESIEVE_IMPLEMENTATION_STRING;
16694
+ mail = getenv("MAIL");
16695
+ sieve_storage = getenv("SIEVE_STORAGE");
16696
+ if ( (sieve_storage == NULL || *sieve_storage == '\0') &&
16697
+ !(mail == NULL || *mail == '\0') ) {
16698
+ storage = sieve_storage_create_from_mail(mail, user);
16700
+ storage = sieve_storage_create(sieve_storage, user);
16702
+ if (storage == NULL) {
16703
+ internal_error();
16706
+ if (sieve_storage != NULL && *sieve_storage != '\0')
16707
+ i_fatal("Failed to create sieve storage with data: %s", sieve_storage);
16708
+ else if (mail != NULL && *mail != '\0')
16709
+ i_fatal("Failed to create sieve storage with mail-data: %s", mail);
16711
+ const char *home;
16713
+ home = getenv("HOME");
16714
+ if (home == NULL) home = "not set";
16716
+ i_fatal("SIEVE_STORAGE and MAIL environment missing and "
16717
+ "autodetection failed (home %s)", home);
16721
+ parse_workarounds();
16723
+ client = client_create(0, 1, storage);
16725
+ client_send_ok(client, "Logged in.");
16728
+static void main_deinit(void)
16730
+ if (log_io != NULL)
16731
+ io_remove(&log_io);
16732
+ clients_deinit();
16734
+ commands_deinit();
16735
+ dict_client_unregister();
16739
+ lib_signals_deinit();
16743
+int main(int argc __attr_unused__, char *argv[], char *envp[])
16746
+ if (getenv("LOGGED_IN") != NULL && getenv("GDB") == NULL)
16747
+ fd_debug_verify_leaks(3, 1024);
16749
+ if (IS_STANDALONE() && getuid() == 0 &&
16750
+ net_getpeername(1, NULL, NULL) == 0) {
16751
+ printf("NO \"managesieve binary must not be started from "
16752
+ "inetd, use managesieve-login instead.\"\n");
16756
+ /* NOTE: we start rooted, so keep the code minimal until
16757
+ restrict_access_by_env() is called */
16759
+ drop_privileges();
16761
+ process_title_init(argv, envp);
16762
+ ioloop = io_loop_create(system_pool);
16765
+ io_loop_run(ioloop);
16768
+ io_loop_destroy(&ioloop);
16773
diff -r 894f003d9f5f src/master/common.h
16774
--- a/src/master/common.h Sun Mar 09 12:50:11 2008 +0200
16775
+++ b/src/master/common.h Sun May 04 16:00:59 2008 +0200
16776
@@ -17,6 +17,7 @@ enum process_type {
16777
PROCESS_TYPE_SSL_PARAM,
16780
+ PROCESS_TYPE_MANAGESIEVE,
16784
diff -r 894f003d9f5f src/master/login-process.c
16785
--- a/src/master/login-process.c Sun Mar 09 12:50:11 2008 +0200
16786
+++ b/src/master/login-process.c Sun May 04 16:00:59 2008 +0200
16787
@@ -67,8 +67,20 @@ static void login_group_create(struct se
16788
group = i_new(struct login_group, 1);
16789
group->refcount = 1;
16791
- group->process_type = set->protocol == MAIL_PROTOCOL_IMAP ?
16792
- PROCESS_TYPE_IMAP : PROCESS_TYPE_POP3;
16794
+ switch ( set->protocol ) {
16795
+ case MAIL_PROTOCOL_IMAP:
16796
+ group->process_type = PROCESS_TYPE_IMAP;
16798
+ case MAIL_PROTOCOL_POP3:
16799
+ group->process_type = PROCESS_TYPE_POP3;
16801
+ case MAIL_PROTOCOL_MANAGESIEVE:
16802
+ group->process_type = PROCESS_TYPE_MANAGESIEVE;
16808
group->next = login_groups;
16809
login_groups = group;
16810
@@ -222,6 +234,8 @@ static void login_process_groups_create(
16811
login_group_create(server->imap);
16812
if (server->pop3 != NULL)
16813
login_group_create(server->pop3);
16814
+ if (server->managesieve != NULL)
16815
+ login_group_create(server->managesieve);
16819
@@ -283,6 +297,8 @@ static bool login_process_read_group(str
16820
protocol = MAIL_PROTOCOL_IMAP;
16821
else if (strcmp(proto, "pop3") == 0)
16822
protocol = MAIL_PROTOCOL_POP3;
16823
+ else if (strcmp(proto, "managesieve") == 0)
16824
+ protocol = MAIL_PROTOCOL_MANAGESIEVE;
16826
i_error("login: Unknown protocol '%s'", proto);
16828
@@ -580,6 +596,9 @@ static void login_process_init_env(struc
16829
*set->imap_capability != '\0' ?
16830
set->imap_capability :
16831
set->imap_generated_capability, NULL));
16832
+ } else if (group->process_type == PROCESS_TYPE_MANAGESIEVE) {
16833
+ env_put(t_strconcat("MANAGESIEVE_IMPLEMENTATION_STRING=",
16834
+ set->managesieve_implementation_string, NULL));
16838
diff -r 894f003d9f5f src/master/mail-process.c
16839
--- a/src/master/mail-process.c Sun Mar 09 12:50:11 2008 +0200
16840
+++ b/src/master/mail-process.c Sun May 04 16:00:59 2008 +0200
16841
@@ -231,6 +231,25 @@ mail_process_set_environment(struct sett
16842
set->pop3_client_workarounds, NULL));
16843
env_put(t_strconcat("POP3_LOGOUT_FORMAT=",
16844
set->pop3_logout_format, NULL));
16846
+ env_put(t_strdup_printf("MANAGESIEVE_MAX_LINE_LENGTH=%u",
16847
+ set->managesieve_max_line_length));
16848
+ env_put(t_strconcat("MANAGESIEVE_IMPLEMENTATION_STRING=",
16849
+ set->managesieve_implementation_string, NULL));
16851
+ /* Set sieve environment
16852
+ * FIXME: Currently just uses the expand_mail_env function to
16853
+ * substitute variables and home dir. Technically, that function
16854
+ * is not meant for the sieve implementation.
16856
+ if ( set->sieve_storage != NULL ) {
16857
+ env_put(t_strconcat("SIEVE_STORAGE=",
16858
+ expand_mail_env(set->sieve_storage, var_expand_table), NULL));
16860
+ if (set->sieve != NULL) {
16861
+ env_put(t_strconcat("SIEVE=",
16862
+ expand_mail_env(set->sieve, var_expand_table), NULL));
16865
if (set->mail_save_crlf)
16866
env_put("MAIL_SAVE_CRLF=1");
16867
@@ -348,6 +367,8 @@ void mail_process_exec(const char *proto
16868
set = server->imap;
16869
else if (strcmp(protocol, "pop3") == 0)
16870
set = server->pop3;
16871
+ else if (strcmp(protocol, "managesieve") == 0)
16872
+ set = server->managesieve;
16874
i_fatal("Unknown protocol: '%s'", protocol);
16875
executable = set->mail_executable;
16876
diff -r 894f003d9f5f src/master/main.c
16877
--- a/src/master/main.c Sun Mar 09 12:50:11 2008 +0200
16878
+++ b/src/master/main.c Sun May 04 16:00:59 2008 +0200
16879
@@ -33,7 +33,8 @@ const char *process_names[PROCESS_TYPE_M
16888
static const char *configfile = SYSCONFDIR "/" PACKAGE ".conf";
16889
@@ -241,6 +242,7 @@ static void sigchld_handler(int signo __
16891
case PROCESS_TYPE_IMAP:
16892
case PROCESS_TYPE_POP3:
16893
+ case PROCESS_TYPE_MANAGESIEVE:
16894
mail_process_destroyed(pid);
16896
case PROCESS_TYPE_SSL_PARAM:
16897
@@ -354,6 +356,10 @@ static void check_conflicts(const struct
16898
check_conflicts_set(server->pop3, ip, port,
16901
+ if (server->managesieve != NULL) {
16902
+ check_conflicts_set(server->managesieve, ip, port,
16903
+ "managesieve", proto);
16908
@@ -364,13 +370,31 @@ static void listen_protocols(struct sett
16912
- set->listen_port = set->protocol == MAIL_PROTOCOL_IMAP ? 143 : 110;
16913
+ switch (set->protocol) {
16914
+ case MAIL_PROTOCOL_IMAP:
16915
+ set->listen_port = 143;
16917
- set->ssl_listen_port = set->protocol == MAIL_PROTOCOL_IMAP ? 993 : 995;
16918
+ set->ssl_listen_port = 993;
16920
- set->ssl_listen_port = 0;
16923
+ set->ssl_listen_port = 0;
16926
+ case MAIL_PROTOCOL_POP3:
16927
+ set->listen_port = 110;
16929
+ set->ssl_listen_port = 995;
16931
+ set->ssl_listen_port = 0;
16934
+ case MAIL_PROTOCOL_MANAGESIEVE:
16935
+ set->listen_port = 2000;
16936
+ set->ssl_listen_port = 0;
16943
resolve_ip("listen", set->listen, &set->listen_ip, &set->listen_port);
16944
if (!set->ssl_disable) {
16945
@@ -413,6 +437,12 @@ static void listen_protocols(struct sett
16946
port = set->ssl_listen_port;
16947
ip = &set->ssl_listen_ip;
16949
+ } else if (strcasecmp(*proto, "managesieve") == 0) {
16950
+ if (set->protocol == MAIL_PROTOCOL_MANAGESIEVE) {
16951
+ fd = &set->listen_fd;
16952
+ port = set->listen_port;
16953
+ ip = &set->listen_ip;
16956
i_fatal("Unknown protocol %s", *proto);
16958
@@ -466,12 +496,14 @@ static void listen_fds_open(bool retry)
16959
static void listen_fds_open(bool retry)
16961
struct server_settings *server;
16964
for (server = settings_root; server != NULL; server = server->next) {
16965
if (server->imap != NULL)
16966
listen_protocols(server->imap, retry);
16967
if (server->pop3 != NULL)
16968
listen_protocols(server->pop3, retry);
16969
+ if (server->managesieve != NULL)
16970
+ listen_protocols(server->managesieve, retry);
16974
@@ -494,6 +526,11 @@ static void listen_fds_close(struct serv
16975
close(server->pop3->ssl_listen_fd) < 0)
16976
i_error("close(pop3.ssl_listen_fd) failed: %m");
16978
+ if (server->managesieve != NULL) {
16979
+ if (server->managesieve->listen_fd != null_fd &&
16980
+ close(server->managesieve->listen_fd) < 0)
16981
+ i_error("close(managesieve.listen_fd) failed: %m");
16986
@@ -516,6 +553,8 @@ static bool have_stderr(struct server_se
16987
if (server->imap != NULL && have_stderr_set(server->imap))
16989
if (server->pop3 != NULL && have_stderr_set(server->pop3))
16991
+ if (server->managesieve != NULL && have_stderr_set(server->managesieve))
16994
server = server->next;
16995
diff -r 894f003d9f5f src/master/master-settings-defs.c
16996
--- a/src/master/master-settings-defs.c Sun Mar 09 12:50:11 2008 +0200
16997
+++ b/src/master/master-settings-defs.c Sun May 04 16:00:59 2008 +0200
16998
@@ -115,5 +115,13 @@ static struct setting_def setting_defs[]
16999
DEF(SET_STR, pop3_client_workarounds),
17000
DEF(SET_STR, pop3_logout_format),
17002
+ /* managesieve */
17003
+ DEF(SET_INT, managesieve_max_line_length),
17004
+ DEF(SET_STR, managesieve_implementation_string),
17007
+ DEF(SET_STR, sieve_storage),
17008
+ DEF(SET_STR, sieve),
17012
diff -r 894f003d9f5f src/master/master-settings.c
17013
--- a/src/master/master-settings.c Sun Mar 09 12:50:11 2008 +0200
17014
+++ b/src/master/master-settings.c Sun May 04 16:00:59 2008 +0200
17015
@@ -269,6 +269,14 @@ struct settings default_settings = {
17016
MEMBER(pop3_client_workarounds) "",
17017
MEMBER(pop3_logout_format) "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s",
17019
+ /* managesieve */
17020
+ MEMBER(managesieve_max_line_length) 65536,
17021
+ MEMBER(managesieve_implementation_string) PACKAGE,
17024
+ MEMBER(sieve_storage) "",
17025
+ MEMBER(sieve) NULL,
17028
MEMBER(listen_fd) -1,
17029
MEMBER(ssl_listen_fd) -1
17030
@@ -425,6 +433,8 @@ static bool auth_settings_verify(struct
17031
auth->parent->pop3->ssl_verify_client_cert = TRUE;
17032
if (auth->parent->imap != NULL)
17033
auth->parent->imap->ssl_verify_client_cert = TRUE;
17034
+ if (auth->parent->managesieve != NULL)
17035
+ auth->parent->managesieve->ssl_verify_client_cert = TRUE;
17038
for (s = auth->sockets; s != NULL; s = s->next) {
17039
@@ -475,8 +485,11 @@ static bool settings_is_active(struct se
17040
if (set->protocol == MAIL_PROTOCOL_IMAP) {
17041
if (strstr(set->protocols, "imap") == NULL)
17043
+ } else if (set->protocol == MAIL_PROTOCOL_POP3) {
17044
+ if (strstr(set->protocols, "pop3") == NULL)
17047
- if (strstr(set->protocols, "pop3") == NULL)
17048
+ if (strstr(set->protocols, "managesieve") == NULL)
17052
@@ -1081,7 +1094,7 @@ static const char *parse_setting(const c
17054
if (strcmp(key, "login") == 0) {
17055
i_warning("Ignoring deprecated 'login' section handling. "
17056
- "Use protocol imap/pop3 { .. } instead. "
17057
+ "Use protocol imap/pop3/managesieve { .. } instead. "
17058
"Some settings may have been read incorrectly.");
17061
@@ -1104,6 +1117,15 @@ static const char *parse_setting(const c
17062
error = parse_setting_from_defs(settings_pool,
17068
+ if (error == NULL &&
17069
+ (ctx->protocol == MAIL_PROTOCOL_ANY ||
17070
+ ctx->protocol == MAIL_PROTOCOL_MANAGESIEVE)) {
17071
+ error = parse_setting_from_defs(settings_pool,
17073
+ ctx->server->managesieve,
17077
@@ -1165,6 +1187,13 @@ static const char *parse_setting(const c
17078
array_append(&ctx->server->pop3->plugin_envs,
17081
+ if (ctx->protocol == MAIL_PROTOCOL_ANY ||
17082
+ ctx->protocol == MAIL_PROTOCOL_MANAGESIEVE) {
17083
+ array_append(&ctx->server->managesieve->plugin_envs, &key, 1);
17084
+ array_append(&ctx->server->managesieve->plugin_envs,
17091
@@ -1174,7 +1203,8 @@ static struct server_settings *
17092
static struct server_settings *
17093
create_new_server(const char *name,
17094
struct settings *imap_defaults,
17095
- struct settings *pop3_defaults)
17096
+ struct settings *pop3_defaults,
17097
+ struct settings *managesieve_defaults)
17099
struct server_settings *server;
17101
@@ -1182,16 +1212,20 @@ create_new_server(const char *name,
17102
server->name = p_strdup(settings_pool, name);
17103
server->imap = p_new(settings_pool, struct settings, 1);
17104
server->pop3 = p_new(settings_pool, struct settings, 1);
17105
+ server->managesieve = p_new(settings_pool, struct settings, 1);
17106
server->auth_defaults = default_auth_settings;
17108
*server->imap = *imap_defaults;
17109
*server->pop3 = *pop3_defaults;
17110
+ *server->managesieve = *managesieve_defaults;
17112
ARRAY_CREATE(&server->dicts, settings_pool, const char *, 4);
17113
ARRAY_CREATE(&server->imap->plugin_envs, settings_pool,
17115
ARRAY_CREATE(&server->pop3->plugin_envs, settings_pool,
17117
+ ARRAY_CREATE(&server->managesieve->plugin_envs, settings_pool,
17118
+ const char *, 8);
17120
server->imap->server = server;
17121
server->imap->protocol = MAIL_PROTOCOL_IMAP;
17122
@@ -1205,6 +1239,12 @@ create_new_server(const char *name,
17123
server->pop3->mail_executable = PKG_LIBEXECDIR"/pop3";
17124
server->pop3->mail_plugin_dir = MODULEDIR"/pop3";
17126
+ server->managesieve->server = server;
17127
+ server->managesieve->protocol = MAIL_PROTOCOL_MANAGESIEVE;
17128
+ server->managesieve->login_executable = PKG_LIBEXECDIR"/managesieve-login";
17129
+ server->managesieve->mail_executable = PKG_LIBEXECDIR"/managesieve";
17130
+ server->managesieve->mail_plugin_dir = MODULEDIR"/managesieve";
17135
@@ -1248,8 +1288,8 @@ static bool parse_section(const char *ty
17137
ctx->type = SETTINGS_TYPE_SERVER;
17138
ctx->server = create_new_server(name, ctx->server->imap,
17139
- ctx->server->pop3);
17140
- server = ctx->root;
17141
+ ctx->server->pop3, ctx->server->managesieve);
17142
+ server = ctx->root;
17143
while (server->next != NULL)
17144
server = server->next;
17145
server->next = ctx->server;
17146
@@ -1270,6 +1310,8 @@ static bool parse_section(const char *ty
17147
ctx->protocol = MAIL_PROTOCOL_POP3;
17148
else if (strcmp(name, "lda") == 0)
17149
ctx->protocol = MAIL_PROTOCOL_LDA;
17150
+ else if (strcmp(name, "managesieve") == 0)
17151
+ ctx->protocol = MAIL_PROTOCOL_MANAGESIEVE;
17153
*errormsg = "Unknown protocol name";
17155
@@ -1380,7 +1422,7 @@ bool master_settings_read(const char *pa
17156
ctx.protocol = MAIL_PROTOCOL_ANY;
17157
ctx.server = ctx.root =
17158
create_new_server("default",
17159
- &default_settings, &default_settings);
17160
+ &default_settings, &default_settings, &default_settings);
17161
ctx.auth = &ctx.server->auth_defaults;
17163
if (!settings_read(path, NULL, parse_setting, parse_section, &ctx))
17164
@@ -1397,7 +1439,9 @@ bool master_settings_read(const char *pa
17166
if (!nochecks && !nofixes) {
17167
ctx.root->defaults = settings_is_active(ctx.root->imap) ?
17168
- ctx.root->imap : ctx.root->pop3;
17170
+ (settings_is_active(ctx.root->pop3) ?
17171
+ ctx.root->pop3 : ctx.root->managesieve);
17173
path = t_strconcat(ctx.root->defaults->base_dir,
17174
"/master.pid", NULL);
17175
@@ -1407,7 +1451,8 @@ bool master_settings_read(const char *pa
17177
for (server = ctx.root; server != NULL; server = server->next) {
17178
if ((*server->imap->protocols == '\0' ||
17179
- *server->pop3->protocols == '\0') && !nochecks) {
17180
+ *server->pop3->protocols == '\0' ||
17181
+ *server->managesieve->protocols == '\0') && !nochecks) {
17182
i_error("No protocols given in configuration file");
17185
@@ -1437,6 +1482,15 @@ bool master_settings_read(const char *pa
17186
server->defaults = server->pop3;
17189
+ if (!settings_is_active(server->managesieve) && !nochecks)
17190
+ server->managesieve = NULL;
17192
+ if (!settings_fix(server->managesieve, nochecks, nofixes))
17194
+ if (server->defaults == NULL)
17195
+ server->defaults = server->managesieve;
17198
if (server->defaults == NULL) {
17200
ctx.root = server->next;
17201
@@ -1653,8 +1707,8 @@ static void dict_settings_dump(const str
17203
void master_settings_dump(struct server_settings *set, bool nondefaults)
17205
- const void *sets[4];
17206
- const char *set_names[4];
17207
+ const void *sets[5];
17208
+ const char *set_names[5];
17209
unsigned int count;
17211
sets[0] = &default_settings;
17212
@@ -1673,6 +1727,11 @@ void master_settings_dump(struct server_
17213
sets[count] = set->pop3;
17214
set_names[count] = "pop3";
17217
+ if (set->managesieve != NULL) {
17218
+ sets[count] = set->managesieve;
17219
+ set_names[count] = "managesieve";
17222
settings_dump(setting_defs, sets, set_names, count, nondefaults, 0);
17223
namespace_settings_dump(set->namespaces, nondefaults);
17224
diff -r 894f003d9f5f src/master/master-settings.h
17225
--- a/src/master/master-settings.h Sun Mar 09 12:50:11 2008 +0200
17226
+++ b/src/master/master-settings.h Sun May 04 16:00:59 2008 +0200
17228
#include "network.h"
17230
enum mail_protocol {
17231
- MAIL_PROTOCOL_ANY,
17232
- MAIL_PROTOCOL_IMAP,
17233
+ MAIL_PROTOCOL_ANY,
17234
+ MAIL_PROTOCOL_IMAP,
17235
MAIL_PROTOCOL_POP3,
17236
- MAIL_PROTOCOL_LDA
17237
+ MAIL_PROTOCOL_LDA,
17238
+ MAIL_PROTOCOL_MANAGESIEVE
17242
@@ -123,6 +124,14 @@ struct settings {
17243
const char *pop3_client_workarounds;
17244
const char *pop3_logout_format;
17246
+ /* managesieve */
17247
+ unsigned int managesieve_max_line_length;
17248
+ const char *managesieve_implementation_string;
17251
+ const char *sieve_storage;
17252
+ const char *sieve;
17255
int listen_fd, ssl_listen_fd;
17257
@@ -231,9 +240,10 @@ struct server_settings {
17258
struct settings *defaults;
17259
struct settings *imap;
17260
struct settings *pop3;
17261
+ struct settings *managesieve;
17262
struct auth_settings *auths;
17263
struct auth_settings auth_defaults;
17264
- struct namespace_settings *namespaces;
17265
+ struct namespace_settings *namespaces;
17267
array_t ARRAY_DEFINE(dicts, const char *);