Merge lp:~james-page/ubuntu/oneiric/dovecot-metadata-plugin/new-upstream-release into lp:ubuntu/oneiric/dovecot-metadata-plugin

Proposed by James Page
Status: Merged
Merge reported by: Luke Yelavich
Merged at revision: not available
Proposed branch: lp:~james-page/ubuntu/oneiric/dovecot-metadata-plugin/new-upstream-release
Merge into: lp:ubuntu/oneiric/dovecot-metadata-plugin
Diff against target: 4296 lines (+3039/-687)
43 files modified
.hg_archival.txt (+2/-3)
.hgignore (+11/-2)
.hgsigs (+1/-0)
.hgtags (+1/-0)
AUTHORS (+1/-0)
INSTALL (+189/-60)
Makefile.am (+2/-0)
NEWS (+9/-2)
TODO (+1/-0)
autogen.sh (+5/-10)
configure.ac (+20/-0)
configure.in (+0/-56)
debian/changelog (+11/-0)
debian/control (+3/-3)
debian/copyright (+25/-46)
debian/rules (+4/-0)
src/Makefile.am (+40/-16)
src/dict-ext.c (+139/-0)
src/dict-ext.h (+58/-0)
src/imap-annotatemore-plugin.c (+475/-120)
src/imap-annotatemore-plugin.h (+0/-7)
src/imap-arg-ext.c (+83/-0)
src/imap-arg-ext.h (+32/-0)
src/imap-metadata-plugin.c (+699/-0)
src/mailbox-ext.c (+30/-0)
src/mailbox-ext.h (+30/-0)
src/metadata-backend.c (+296/-0)
src/metadata-backend.h (+49/-0)
src/metadata-entry-private.h (+32/-0)
src/metadata-entry.c (+173/-0)
src/metadata-entry.h (+83/-0)
src/metadata-global.h (+52/-0)
src/metadata-mail-storage-module.c (+92/-0)
src/metadata-mail-storage-module.h (+28/-0)
src/metadata-mail-user-module-private.h (+40/-0)
src/metadata-mail-user-module.c (+56/-0)
src/metadata-mail-user-module.h (+28/-0)
src/metadata-plugin.c (+34/-345)
src/metadata-plugin.h (+0/-17)
src/metadata-settings.c (+85/-0)
src/metadata-settings.h (+40/-0)
src/str-ext.c (+42/-0)
src/str-ext.h (+38/-0)
To merge this branch: bzr merge lp:~james-page/ubuntu/oneiric/dovecot-metadata-plugin/new-upstream-release
Reviewer Review Type Date Requested Status
Dave Walker (community) Approve
Matthias Klose Approve
Ubuntu branches Pending
Review via email: mp+77597@code.launchpad.net

Description of the change

New upstream release - existing version is not compatible with v2 of dovecot.
Had to switch upstream source to dovecot rather than kolab as development appears to be active there.
General tidy and update of copyright at the same time

To post a comment you must log in.
Revision history for this message
Matthias Klose (doko) wrote :

this looks ok. the alternative is either to remove the package or upgrade to the new version.

review: Approve
Revision history for this message
Dave Walker (davewalker) wrote :

I think it's probably better to have something that should hopefully work, rather than something we know doesn't work and FTBFS.

I'd rather we have this new upstream snapshot rather than dropping from the archive.

Thanks.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.hg_archival.txt'
2--- .hg_archival.txt 2010-08-11 17:11:01 +0000
3+++ .hg_archival.txt 2011-09-29 19:49:50 +0000
4@@ -1,5 +1,4 @@
5 repo: ae7e8348c8755ea0c8209c5b5efcc81228530b5e
6-node: 4372f4ab8b0ee66b8c8e88e3661a67d441537b2d
7+node: bdf2445e101fefc55ffa7bb2c4c5807af0799bd2
8 branch: default
9-latesttag: works-with-dovecot-1.1
10-latesttagdistance: 10
11+tag: v8
12
13=== modified file '.hgignore'
14--- .hgignore 2010-08-11 17:11:01 +0000
15+++ .hgignore 2011-09-29 19:49:50 +0000
16@@ -1,17 +1,26 @@
17 Makefile$
18 Makefile\.in$
19+^stamp-h1$
20+^metadata-config\.h$
21+^metadata-config\.h\.in$
22+^ChangeLog$
23+^\.cscope
24 ^aclocal\.m4$
25 ^autom4te\.cache
26 ^configure$
27+^configure.lineno$
28 ^config\.
29 ^depcomp$
30 ^libtool$
31 ^install-sh
32 ^ltmain\.sh$
33+^m4
34 ^missing$
35-^src/.deps
36-^src/.libs
37+^patches
38+^src/\.deps
39+^src/\.libs
40 ~$
41+\.tar\.gz$
42 \.pyc$
43 \.lo$
44 \.la$
45
46=== added file '.hgsigs'
47--- .hgsigs 1970-01-01 00:00:00 +0000
48+++ .hgsigs 2011-09-29 19:49:50 +0000
49@@ -0,0 +1,1 @@
50+fc360cf4ef81fe0b54ea56074d6b6ee9dcb1c3d5 0 iEYEABECAAYFAk30p6cACgkQjqfyF1DtJW689QCfU6UysrMwwqCycZ9EFbyN9Iof5D0AoL/np56Phl9xCZN9hzeaq0qIcszU
51
52=== modified file '.hgtags'
53--- .hgtags 2010-08-11 17:11:01 +0000
54+++ .hgtags 2011-09-29 19:49:50 +0000
55@@ -1,1 +1,2 @@
56 604f64c0f4852e3c0b5fd20b46cb7372791ec91b works-with-dovecot-1.1
57+fc360cf4ef81fe0b54ea56074d6b6ee9dcb1c3d5 v7
58
59=== modified file 'AUTHORS'
60--- AUTHORS 2010-08-11 17:11:01 +0000
61+++ AUTHORS 2011-09-29 19:49:50 +0000
62@@ -1,1 +1,2 @@
63 Bernhard Herzog <bh@intevation.de>
64+Dennis Schridde <devurandom@gna.org>
65
66=== modified file 'INSTALL'
67--- INSTALL 2010-08-11 17:11:01 +0000
68+++ INSTALL 2011-09-29 19:49:50 +0000
69@@ -1,16 +1,25 @@
70 Installation Instructions
71 *************************
72
73-Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free
74-Software Foundation, Inc.
75+Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
76+2006, 2007, 2008, 2009 Free Software Foundation, Inc.
77
78-This file is free documentation; the Free Software Foundation gives
79-unlimited permission to copy, distribute and modify it.
80+ Copying and distribution of this file, with or without modification,
81+are permitted in any medium without royalty provided the copyright
82+notice and this notice are preserved. This file is offered as-is,
83+without warranty of any kind.
84
85 Basic Installation
86 ==================
87
88-These are generic installation instructions.
89+ Briefly, the shell commands `./configure; make; make install' should
90+configure, build, and install this package. The following
91+more-detailed instructions are generic; see the `README' file for
92+instructions specific to this package. Some packages provide this
93+`INSTALL' file but do not implement all of the features documented
94+below. The lack of an optional feature in a given package is not
95+necessarily a bug. More recommendations for GNU packages can be found
96+in *note Makefile Conventions: (standards)Makefile Conventions.
97
98 The `configure' shell script attempts to guess correct values for
99 various system-dependent variables used during compilation. It uses
100@@ -23,9 +32,9 @@
101
102 It can also use an optional file (typically called `config.cache'
103 and enabled with `--cache-file=config.cache' or simply `-C') that saves
104-the results of its tests to speed up reconfiguring. (Caching is
105+the results of its tests to speed up reconfiguring. Caching is
106 disabled by default to prevent problems with accidental use of stale
107-cache files.)
108+cache files.
109
110 If you need to do unusual things to compile the package, please try
111 to figure out how `configure' could check whether to do them, and mail
112@@ -35,30 +44,37 @@
113 may remove or edit it.
114
115 The file `configure.ac' (or `configure.in') is used to create
116-`configure' by a program called `autoconf'. You only need
117-`configure.ac' if you want to change it or regenerate `configure' using
118-a newer version of `autoconf'.
119+`configure' by a program called `autoconf'. You need `configure.ac' if
120+you want to change it or regenerate `configure' using a newer version
121+of `autoconf'.
122
123-The simplest way to compile this package is:
124+ The simplest way to compile this package is:
125
126 1. `cd' to the directory containing the package's source code and type
127- `./configure' to configure the package for your system. If you're
128- using `csh' on an old version of System V, you might need to type
129- `sh ./configure' instead to prevent `csh' from trying to execute
130- `configure' itself.
131+ `./configure' to configure the package for your system.
132
133- Running `configure' takes awhile. While running, it prints some
134- messages telling which features it is checking for.
135+ Running `configure' might take a while. While running, it prints
136+ some messages telling which features it is checking for.
137
138 2. Type `make' to compile the package.
139
140 3. Optionally, type `make check' to run any self-tests that come with
141- the package.
142+ the package, generally using the just-built uninstalled binaries.
143
144 4. Type `make install' to install the programs and any data files and
145- documentation.
146-
147- 5. You can remove the program binaries and object files from the
148+ documentation. When installing into a prefix owned by root, it is
149+ recommended that the package be configured and built as a regular
150+ user, and only the `make install' phase executed with root
151+ privileges.
152+
153+ 5. Optionally, type `make installcheck' to repeat any self-tests, but
154+ this time using the binaries in their final installed location.
155+ This target does not install anything. Running this target as a
156+ regular user, particularly if the prior `make install' required
157+ root privileges, verifies that the installation completed
158+ correctly.
159+
160+ 6. You can remove the program binaries and object files from the
161 source code directory by typing `make clean'. To also remove the
162 files that `configure' created (so you can compile the package for
163 a different kind of computer), type `make distclean'. There is
164@@ -67,45 +83,69 @@
165 all sorts of other programs in order to regenerate files that came
166 with the distribution.
167
168+ 7. Often, you can also type `make uninstall' to remove the installed
169+ files again. In practice, not all packages have tested that
170+ uninstallation works correctly, even though it is required by the
171+ GNU Coding Standards.
172+
173+ 8. Some packages, particularly those that use Automake, provide `make
174+ distcheck', which can by used by developers to test that all other
175+ targets like `make install' and `make uninstall' work correctly.
176+ This target is generally not run by end users.
177+
178 Compilers and Options
179 =====================
180
181-Some systems require unusual options for compilation or linking that the
182-`configure' script does not know about. Run `./configure --help' for
183-details on some of the pertinent environment variables.
184+ Some systems require unusual options for compilation or linking that
185+the `configure' script does not know about. Run `./configure --help'
186+for details on some of the pertinent environment variables.
187
188 You can give `configure' initial values for configuration parameters
189 by setting variables in the command line or in the environment. Here
190 is an example:
191
192- ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix
193+ ./configure CC=c99 CFLAGS=-g LIBS=-lposix
194
195 *Note Defining Variables::, for more details.
196
197 Compiling For Multiple Architectures
198 ====================================
199
200-You can compile the package for more than one kind of computer at the
201+ You can compile the package for more than one kind of computer at the
202 same time, by placing the object files for each architecture in their
203-own directory. To do this, you must use a version of `make' that
204-supports the `VPATH' variable, such as GNU `make'. `cd' to the
205+own directory. To do this, you can use GNU `make'. `cd' to the
206 directory where you want the object files and executables to go and run
207 the `configure' script. `configure' automatically checks for the
208-source code in the directory that `configure' is in and in `..'.
209-
210- If you have to use a `make' that does not support the `VPATH'
211-variable, you have to compile the package for one architecture at a
212-time in the source code directory. After you have installed the
213-package for one architecture, use `make distclean' before reconfiguring
214-for another architecture.
215+source code in the directory that `configure' is in and in `..'. This
216+is known as a "VPATH" build.
217+
218+ With a non-GNU `make', it is safer to compile the package for one
219+architecture at a time in the source code directory. After you have
220+installed the package for one architecture, use `make distclean' before
221+reconfiguring for another architecture.
222+
223+ On MacOS X 10.5 and later systems, you can create libraries and
224+executables that work on multiple system types--known as "fat" or
225+"universal" binaries--by specifying multiple `-arch' options to the
226+compiler but only a single `-arch' option to the preprocessor. Like
227+this:
228+
229+ ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
230+ CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
231+ CPP="gcc -E" CXXCPP="g++ -E"
232+
233+ This is not guaranteed to produce working output in all cases, you
234+may have to build one architecture at a time and combine the results
235+using the `lipo' tool if you have problems.
236
237 Installation Names
238 ==================
239
240-By default, `make install' installs the package's commands under
241+ By default, `make install' installs the package's commands under
242 `/usr/local/bin', include files under `/usr/local/include', etc. You
243 can specify an installation prefix other than `/usr/local' by giving
244-`configure' the option `--prefix=PREFIX'.
245+`configure' the option `--prefix=PREFIX', where PREFIX must be an
246+absolute file name.
247
248 You can specify separate installation prefixes for
249 architecture-specific files and architecture-independent files. If you
250@@ -116,16 +156,47 @@
251 In addition, if you use an unusual directory layout you can give
252 options like `--bindir=DIR' to specify different values for particular
253 kinds of files. Run `configure --help' for a list of the directories
254-you can set and what kinds of files go in them.
255+you can set and what kinds of files go in them. In general, the
256+default for these options is expressed in terms of `${prefix}', so that
257+specifying just `--prefix' will affect all of the other directory
258+specifications that were not explicitly provided.
259+
260+ The most portable way to affect installation locations is to pass the
261+correct locations to `configure'; however, many packages provide one or
262+both of the following shortcuts of passing variable assignments to the
263+`make install' command line to change installation locations without
264+having to reconfigure or recompile.
265+
266+ The first method involves providing an override variable for each
267+affected directory. For example, `make install
268+prefix=/alternate/directory' will choose an alternate location for all
269+directory configuration variables that were expressed in terms of
270+`${prefix}'. Any directories that were specified during `configure',
271+but not in terms of `${prefix}', must each be overridden at install
272+time for the entire installation to be relocated. The approach of
273+makefile variable overrides for each directory variable is required by
274+the GNU Coding Standards, and ideally causes no recompilation.
275+However, some platforms have known limitations with the semantics of
276+shared libraries that end up requiring recompilation when using this
277+method, particularly noticeable in packages that use GNU Libtool.
278+
279+ The second method involves providing the `DESTDIR' variable. For
280+example, `make install DESTDIR=/alternate/directory' will prepend
281+`/alternate/directory' before all installation names. The approach of
282+`DESTDIR' overrides is not required by the GNU Coding Standards, and
283+does not work on platforms that have drive letters. On the other hand,
284+it does better at avoiding recompilation issues, and works well even
285+when some directory options were not specified in terms of `${prefix}'
286+at `configure' time.
287+
288+Optional Features
289+=================
290
291 If the package supports it, you can cause programs to be installed
292 with an extra prefix or suffix on their names by giving `configure' the
293 option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
294
295-Optional Features
296-=================
297-
298-Some packages pay attention to `--enable-FEATURE' options to
299+ Some packages pay attention to `--enable-FEATURE' options to
300 `configure', where FEATURE indicates an optional part of the package.
301 They may also pay attention to `--with-PACKAGE' options, where PACKAGE
302 is something like `gnu-as' or `x' (for the X Window System). The
303@@ -137,14 +208,53 @@
304 you can use the `configure' options `--x-includes=DIR' and
305 `--x-libraries=DIR' to specify their locations.
306
307+ Some packages offer the ability to configure how verbose the
308+execution of `make' will be. For these packages, running `./configure
309+--enable-silent-rules' sets the default to minimal output, which can be
310+overridden with `make V=1'; while running `./configure
311+--disable-silent-rules' sets the default to verbose, which can be
312+overridden with `make V=0'.
313+
314+Particular systems
315+==================
316+
317+ On HP-UX, the default C compiler is not ANSI C compatible. If GNU
318+CC is not installed, it is recommended to use the following options in
319+order to use an ANSI C compiler:
320+
321+ ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
322+
323+and if that doesn't work, install pre-built binaries of GCC for HP-UX.
324+
325+ On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
326+parse its `<wchar.h>' header file. The option `-nodtk' can be used as
327+a workaround. If GNU CC is not installed, it is therefore recommended
328+to try
329+
330+ ./configure CC="cc"
331+
332+and if that doesn't work, try
333+
334+ ./configure CC="cc -nodtk"
335+
336+ On Solaris, don't put `/usr/ucb' early in your `PATH'. This
337+directory contains several dysfunctional programs; working variants of
338+these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
339+in your `PATH', put it _after_ `/usr/bin'.
340+
341+ On Haiku, software installed for all users goes in `/boot/common',
342+not `/usr/local'. It is recommended to use the following options:
343+
344+ ./configure --prefix=/boot/common
345+
346 Specifying the System Type
347 ==========================
348
349-There may be some features `configure' cannot figure out automatically,
350-but needs to determine by the type of machine the package will run on.
351-Usually, assuming the package is built to be run on the _same_
352-architectures, `configure' can figure that out, but if it prints a
353-message saying it cannot guess the machine type, give it the
354+ There may be some features `configure' cannot figure out
355+automatically, but needs to determine by the type of machine the package
356+will run on. Usually, assuming the package is built to be run on the
357+_same_ architectures, `configure' can figure that out, but if it prints
358+a message saying it cannot guess the machine type, give it the
359 `--build=TYPE' option. TYPE can either be a short name for the system
360 type, such as `sun4', or a canonical name which has the form:
361
362@@ -152,7 +262,8 @@
363
364 where SYSTEM can have one of these forms:
365
366- OS KERNEL-OS
367+ OS
368+ KERNEL-OS
369
370 See the file `config.sub' for the possible values of each field. If
371 `config.sub' isn't included in this package, then this package doesn't
372@@ -170,9 +281,9 @@
373 Sharing Defaults
374 ================
375
376-If you want to set default values for `configure' scripts to share, you
377-can create a site shell script called `config.site' that gives default
378-values for variables like `CC', `cache_file', and `prefix'.
379+ If you want to set default values for `configure' scripts to share,
380+you can create a site shell script called `config.site' that gives
381+default values for variables like `CC', `cache_file', and `prefix'.
382 `configure' looks for `PREFIX/share/config.site' if it exists, then
383 `PREFIX/etc/config.site' if it exists. Or, you can set the
384 `CONFIG_SITE' environment variable to the location of the site script.
385@@ -181,7 +292,7 @@
386 Defining Variables
387 ==================
388
389-Variables not defined in a site shell script can be set in the
390+ Variables not defined in a site shell script can be set in the
391 environment passed to `configure'. However, some packages may run
392 configure again during the build, and the customized values of these
393 variables may be lost. In order to avoid this problem, you should set
394@@ -190,21 +301,29 @@
395 ./configure CC=/usr/local2/bin/gcc
396
397 causes the specified `gcc' to be used as the C compiler (unless it is
398-overridden in the site shell script). Here is a another example:
399-
400- /bin/bash ./configure CONFIG_SHELL=/bin/bash
401-
402-Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent
403-configuration-related scripts to be executed by `/bin/bash'.
404+overridden in the site shell script).
405+
406+Unfortunately, this technique does not work for `CONFIG_SHELL' due to
407+an Autoconf bug. Until the bug is fixed you can use this workaround:
408+
409+ CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
410
411 `configure' Invocation
412 ======================
413
414-`configure' recognizes the following options to control how it operates.
415+ `configure' recognizes the following options to control how it
416+operates.
417
418 `--help'
419 `-h'
420- Print a summary of the options to `configure', and exit.
421+ Print a summary of all of the options to `configure', and exit.
422+
423+`--help=short'
424+`--help=recursive'
425+ Print a summary of the options unique to this package's
426+ `configure', and exit. The `short' variant lists options used
427+ only in the top level, while the `recursive' variant lists options
428+ also present in any nested packages.
429
430 `--version'
431 `-V'
432@@ -231,6 +350,16 @@
433 Look for the package's source code in directory DIR. Usually
434 `configure' can determine that directory automatically.
435
436+`--prefix=DIR'
437+ Use DIR as the installation prefix. *note Installation Names::
438+ for more details, including other options available for fine-tuning
439+ the installation locations.
440+
441+`--no-create'
442+`-n'
443+ Run the configure checks, but stop before creating any output
444+ files.
445+
446 `configure' also accepts some other, not widely useful, options. Run
447 `configure --help' for more details.
448
449
450=== modified file 'Makefile.am'
451--- Makefile.am 2010-08-11 17:11:01 +0000
452+++ Makefile.am 2011-09-29 19:49:50 +0000
453@@ -1,3 +1,5 @@
454+ACLOCAL_AMFLAGS = -I m4
455+
456 SUBDIRS = src
457
458 EXTRA_DIST = \
459
460=== modified file 'NEWS'
461--- NEWS 2010-08-11 17:11:01 +0000
462+++ NEWS 2011-09-29 19:49:50 +0000
463@@ -1,2 +1,9 @@
464-There hasn't been a release yet. See the mercurial logs details on the
465-changes that happened so far.
466+2011-06-12: Version 8
467+ * Fix compliance with RFC 5464 Section 4.2.2
468+ * Minor cleanups
469+
470+2011-06-10: Version 7 - "Works with Akonadi"
471+ * RFC 5464 compatible
472+ * imap-annotatemore is a partial implementation of draft-daboo-imap-annotatemore-08
473+ Based on work by Bernhard Herzog (Intevation GmbH)
474+ * imap-metadata is a complete implementation of RFC 5464
475
476=== added file 'TODO'
477--- TODO 1970-01-01 00:00:00 +0000
478+++ TODO 2011-09-29 19:49:50 +0000
479@@ -0,0 +1,1 @@
480+* Handle mailbox moves/renames
481
482=== modified file 'autogen.sh'
483--- autogen.sh 2010-08-11 17:11:01 +0000
484+++ autogen.sh 2011-09-29 19:49:50 +0000
485@@ -1,12 +1,7 @@
486 #!/bin/sh
487
488-# If you've non-standard directories, set these
489-#ACLOCAL_DIR=
490-#GETTEXT_DIR=
491-
492-if test "$ACLOCAL_DIR" != ""; then
493- ACLOCAL="aclocal -I $ACLOCAL_DIR"
494- export ACLOCAL
495-fi
496-
497-autoreconf -i
498+echo '+ creating m4/ ...'
499+test -d m4 || mkdir m4
500+
501+echo '+ running autoreconf ...'
502+autoreconf --force --install
503
504=== added file 'configure.ac'
505--- configure.ac 1970-01-01 00:00:00 +0000
506+++ configure.ac 2011-09-29 19:49:50 +0000
507@@ -0,0 +1,20 @@
508+AC_PREREQ(2.65)
509+AC_INIT([dovecot-metadata],[8],[devurandom@gmx.net])
510+
511+AM_INIT_AUTOMAKE([1.10 silent-rules])
512+AM_MAINTAINER_MODE
513+
514+AC_CONFIG_MACRO_DIR([m4])
515+AC_CONFIG_SRCDIR([src])
516+
517+AC_PROG_CC_STDC
518+LT_INIT
519+
520+DC_DOVECOT([2.0])
521+
522+AC_CONFIG_HEADERS([metadata-config.h])
523+AC_CONFIG_FILES([
524+ Makefile
525+ src/Makefile
526+])
527+AC_OUTPUT
528
529=== removed file 'configure.in'
530--- configure.in 2010-08-11 17:11:01 +0000
531+++ configure.in 1970-01-01 00:00:00 +0000
532@@ -1,56 +0,0 @@
533-AC_INIT(dovecot-metadata, 0.0.1, [bh@intevation.de])
534-AC_CONFIG_SRCDIR([src])
535-
536-AC_CONFIG_HEADERS([metadata-config.h])
537-AM_INIT_AUTOMAKE
538-
539-AM_MAINTAINER_MODE
540-
541-AC_PROG_CC
542-AM_PROG_LIBTOOL
543-
544-AC_ARG_WITH(dovecot,
545-[ --with-dovecot[=DIR] Dovecot base directory (../dovecot)],
546- dovecotdir="$withval",
547- dovecotdir=../dovecot
548-)
549-old=`pwd`
550-cd $dovecotdir
551-dovecotdir=`pwd`
552-cd $old
553-AC_SUBST(dovecotdir)
554-
555-if ! test -f "$dovecotdir/dovecot-config"; then
556- echo
557- echo "dovecot-config not found from $dovecotdir, use --with-dovecot=PATH"
558- echo "to give path to compiled Dovecot sources or to a directory with the"
559- echo "installed dovecot-config file."
560- AC_MSG_ERROR([dovecot-config not found])
561-fi
562-
563-if test -d "$dovecotdir/src"; then
564- # compiling against sources
565- have_dovecot_libs=yes
566-else
567- # compiling against installed headers
568- have_dovecot_libs=no
569-fi
570-AM_CONDITIONAL(HAVE_DOVECOT_LIBS, test "$have_dovecot_libs" = "yes")
571-
572-dnl replace relative ../ paths in the file with full paths
573-eval `cat $dovecotdir/dovecot-config|sed 's,\$(top_builddir)/,$dovecotdir/,g'`
574-
575-if test $have_dovecot_libs = yes; then
576- dovecot_incdir="$dovecotdir"
577-fi
578-
579-
580-AC_SUBST(dovecot_incdir)
581-AC_SUBST(moduledir)
582-
583-AC_CONFIG_FILES([
584-Makefile
585-src/Makefile
586-])
587-
588-AC_OUTPUT
589
590=== modified file 'debian/changelog'
591--- debian/changelog 2010-08-11 17:11:01 +0000
592+++ debian/changelog 2011-09-29 19:49:50 +0000
593@@ -1,3 +1,14 @@
594+dovecot-metadata-plugin (8-0ubuntu1) oneiric; urgency=low
595+
596+ * New upstream release, fixes FTBFS with dovecot >= 2.0.0 (LP: #831179).
597+ - d/control: Switched upstream source to http://hg.dovecot.org as original
598+ source appears to be inactive.
599+ - d/control: Bumped Standards-Version, no changes.
600+ - d/copyright: Updated and converted to DEP-5.
601+ - d/rules: Cleared dependency_libs in .la files.
602+
603+ -- James Page <james.page@ubuntu.com> Thu, 29 Sep 2011 20:35:51 +0100
604+
605 dovecot-metadata-plugin (0.0.1~hg144-0ubuntu1) maverick; urgency=low
606
607 * Initial release to support Kolab server
608
609=== modified file 'debian/control'
610--- debian/control 2010-08-11 17:11:01 +0000
611+++ debian/control 2011-09-29 19:49:50 +0000
612@@ -4,14 +4,14 @@
613 Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
614 XSBC-Original-Maintainer: Scott Kitterman <scott@kitterman.com>
615 Build-Depends: debhelper (>= 7.0.50), automake, libtool, dovecot-dev, libldap2-dev
616-Standards-Version: 3.9.1
617-Homepage: http://hg.intevation.org/kolab/dovecot-metadata-plugin
618+Standards-Version: 3.9.2
619+Homepage: http://hg.dovecot.org/dovecot-metadata-plugin
620
621 Package: dovecot-metadata-plugin
622 Architecture: any
623 Depends: ${shlibs:Depends}, ${misc:Depends}
624 Description: Experimental IMAP METADATA Extension for Dovecot
625- This is a set of plugins for the Dovecot IMAP server version 1.2 that
626+ This is a set of plugins for the Dovecot IMAP server version 2.0 that
627 implement the IMAP METADATA Extension. The goal of the development is
628 to extend dovecot so that it can be used as the IMAP component instead
629 of Cyrus IMAPd in the Kolab server.
630
631=== modified file 'debian/copyright'
632--- debian/copyright 2010-08-11 17:11:01 +0000
633+++ debian/copyright 2011-09-29 19:49:50 +0000
634@@ -1,46 +1,25 @@
635-This work was packaged for Ubuntu by:
636-
637- Scott Kitterman <scott@kitterman.com> on Wed, 11 Aug 2010 17:11:01 -0400
638-
639-It was downloaded from:
640-
641-http://hg.intevation.org/kolab/dovecot-metadata-plugin/archive/tip.tar.gz
642-
643-Upstream Author:
644-
645- Bernhard Herzog <bh@intevation.de>
646-
647-Copyright:
648-
649- src/imap-annotatemore-plugin.c:/* Copyright (C) 2008 by Intevation GmbH
650- src/metadata-plugin.c:/* Copyright (C) 2008, 2009 by Intevation GmbH
651- test/imapsupport.py:# Copyright (C) 2008 by Intevation GmbH
652- test/test_annotatemore.py:# Copyright (C) 2008 by Intevation GmbH
653- test/runtests.py:# Copyright (C) 2004, 2005, 2006, 2007, 2008 by Intevation GmbH
654- test/test_annotatemore_private.py:# Copyright (C) 2008 by Intevation GmbH
655-
656-License:
657-
658- This library is free software; you can redistribute it and/or
659- modify it under the terms of the GNU Lesser General Public
660- License as published by the Free Software Foundation; either
661- version 2.1 of the License, or (at your option) any later version.
662-
663- This library is distributed in the hope that it will be useful,
664- but WITHOUT ANY WARRANTY; without even the implied warranty of
665- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
666- Lesser General Public License for more details.
667-
668- You should have received a copy of the GNU Lesser General Public
669- License along with this library; if not, write to the Free Software
670- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
671-
672-On Debian systems, the complete text of the GNU Lesser General Public License
673-2.1 can be found in '/usr/share/common-licenses/GPL/LGPL-2.1
674-
675-The Debian packaging is:
676-
677- Copyright (C) 2010 Scott Kitterman <scott@kitterman.com>
678-
679-and is licensed under the smae terms as the upstream source. See above.
680-
681+Format: http://dep.debian.net/deps/dep5/
682+Upstream-Name: Dovecot Metadata Plugin
683+Source: http://hg.dovecot.org/dovecot-metadata-plugin
684+
685+Files: *
686+Copyright: 2008, by Intevation GmbH
687+ 2010, Dennis Schridde
688+ 2008, Bernhard Herzog <bh@intevation.de>
689+License: LGPL-3
690+
691+Files: test/*
692+Copyright: 2004-2008, by Intevation GmbH
693+License: LGPL-2.1
694+
695+Files: debian/*
696+Copyright: 2010 Scott Kitterman <scott@kitterman.com>
697+License: LGPL-2.1
698+
699+License: LGPL-3
700+ On Debian systems, the complete text of the GNU Lesser General Public License
701+ 3 can be found in '/usr/share/common-licenses/GPL/LGPL-3
702+
703+License: LGPL-2.1
704+ On Debian systems, the complete text of the GNU Lesser General Public License
705+ 2.1 can be found in '/usr/share/common-licenses/GPL/LGPL-2.1
706
707=== modified file 'debian/rules'
708--- debian/rules 2010-08-11 17:11:01 +0000
709+++ debian/rules 2011-09-29 19:49:50 +0000
710@@ -13,6 +13,10 @@
711 dh $@
712
713 override_dh_auto_configure:
714+ test -d m4 || mkdir m4
715 autoreconf -f -i -Wall,no-obsolete
716 dh_auto_configure -- --with-dovecot=/usr/lib/dovecot/
717
718+override_dh_install:
719+ sed -i "/dependency_libs/ s/'.*'/''/" `find . -name '*.la'`
720+ dh_install
721
722=== modified file 'src/Makefile.am'
723--- src/Makefile.am 2010-08-11 17:11:01 +0000
724+++ src/Makefile.am 2011-09-29 19:49:50 +0000
725@@ -1,26 +1,50 @@
726-AM_CPPFLAGS = \
727- -I$(dovecot_incdir) \
728- -I$(dovecot_incdir)/src/lib \
729- -I$(dovecot_incdir)/src/lib-mail \
730- -I$(dovecot_incdir)/src/lib-imap \
731- -I$(dovecot_incdir)/src/lib-storage \
732- -I$(dovecot_incdir)/src/lib-dict \
733- -I$(dovecot_incdir)/src/imap \
734- -DPKG_RUNDIR=\""$(rundir)"\"
735-
736-
737-imap_moduledir = $(moduledir)/imap
738-
739+AM_CPPFLAGS = $(LIBDOVECOT_INCLUDE) $(LIBDOVECOT_STORAGE_INCLUDE) $(LIBDOVECOT_IMAP_INCLUDE)
740+
741+imap_moduledir = $(dovecot_moduledir)
742 imap_module_LTLIBRARIES = \
743 lib80_metadata_plugin.la \
744+ lib90_imap_metadata_plugin.la \
745 lib90_imap_annotatemore_plugin.la
746
747 lib80_metadata_plugin_la_LDFLAGS = -module -avoid-version
748-
749 lib80_metadata_plugin_la_SOURCES = \
750- metadata-plugin.c
751+ metadata-plugin.c \
752+ metadata-backend.c \
753+ metadata-entry.c \
754+ metadata-mail-user-module.c \
755+ metadata-mail-storage-module.c \
756+ metadata-settings.c \
757+ dict-ext.c \
758+ imap-arg-ext.c \
759+ mailbox-ext.c \
760+ str-ext.c
761+
762+lib90_imap_metadata_plugin_la_LDFLAGS = -module -avoid-version
763+lib90_imap_metadata_plugin_la_SOURCES = \
764+ imap-metadata-plugin.c
765+if DOVECOT_PLUGIN_DEPS
766+lib90_imap_metadata_plugin_la_LIBADD = \
767+ lib80_metadata_plugin.la
768+endif
769
770 lib90_imap_annotatemore_plugin_la_LDFLAGS = -module -avoid-version
771-
772 lib90_imap_annotatemore_plugin_la_SOURCES = \
773 imap-annotatemore-plugin.c
774+if DOVECOT_PLUGIN_DEPS
775+lib90_imap_annotatemore_plugin_la_LIBADD = \
776+ lib80_metadata_plugin.la
777+endif
778+
779+noinst_HEADERS = \
780+ metadata-backend.h \
781+ metadata-entry.h \
782+ metadata-entry-private.h \
783+ metadata-global.h \
784+ metadata-mail-user-module.h \
785+ metadata-mail-user-module-private.h \
786+ metadata-mail-storage-module.h \
787+ metadata-settings.h \
788+ dict-ext.h \
789+ imap-arg-ext.h \
790+ mailbox-ext.h \
791+ str-ext.h
792
793=== added file 'src/dict-ext.c'
794--- src/dict-ext.c 1970-01-01 00:00:00 +0000
795+++ src/dict-ext.c 2011-09-29 19:49:50 +0000
796@@ -0,0 +1,139 @@
797+/*
798+ Copyright (c) 2010 by Dennis Schridde
799+
800+ This file is part of dovecot-metadata.
801+
802+ dovecot-metadata is free software: you can redistribute it and/or modify
803+ it under the terms of the GNU Lesser General Public License as published by
804+ the Free Software Foundation, either version 3 of the License, or
805+ (at your option) any later version.
806+
807+ dovecot-metadata is distributed in the hope that it will be useful,
808+ but WITHOUT ANY WARRANTY; without even the implied warranty of
809+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
810+ GNU Lesser General Public License for more details.
811+
812+ You should have received a copy of the GNU Lesser General Public License
813+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
814+*/
815+#include "dict-ext.h"
816+
817+#include "str.h"
818+#include "dict.h"
819+
820+enum dict_scope
821+dict_get_scope(const char *path) {
822+ if (strncasecmp(DICT_PATH_SHARED, path, sizeof(DICT_PATH_SHARED)) == 0)
823+ return DICT_SCOPE_SHARED;
824+ else if (strncasecmp(DICT_PATH_PRIVATE, path, sizeof(DICT_PATH_PRIVATE)) == 0)
825+ return DICT_SCOPE_PRIVATE;
826+
827+ return DICT_SCOPE_INVALID;
828+}
829+
830+const char *
831+dict_path_from_scope(enum dict_scope scope) {
832+ switch (scope) {
833+ case DICT_SCOPE_SHARED:
834+ return DICT_PATH_SHARED;
835+ case DICT_SCOPE_PRIVATE:
836+ return DICT_PATH_PRIVATE;
837+ case DICT_SCOPE_INVALID:
838+ return "";
839+ }
840+
841+ return "";
842+}
843+
844+struct dict_iterate_multiscope_context {
845+ struct dict_iterate_context *dict_ctx;
846+ struct dict *dict;
847+ enum dict_iterate_flags flags;
848+ string_t *path;
849+ bool do_shared;
850+ bool failed;
851+};
852+
853+struct dict_iterate_multiscope_context *
854+dict_iterate_multiscope_init(struct dict *dict, const char *path, enum dict_iterate_multiscope_flags flags) {
855+ struct dict_iterate_multiscope_context *ctx = i_new(struct dict_iterate_multiscope_context, 1);
856+ memset(ctx, 0, sizeof(*ctx));
857+
858+ ctx->dict = dict;
859+ ctx->flags = flags & ~DICT_ITERATE_MULTISCOPE_FLAG_MULTISCOPE;
860+
861+ ctx->path = str_new(default_pool, 128);
862+ if (flags & DICT_ITERATE_MULTISCOPE_FLAG_MULTISCOPE)
863+ str_append(ctx->path, DICT_PATH_PRIVATE);
864+ str_append(ctx->path, path);
865+
866+ ctx->do_shared = (flags & DICT_ITERATE_MULTISCOPE_FLAG_MULTISCOPE);
867+ ctx->failed = false;
868+
869+ ctx->dict_ctx = dict_iterate_init(ctx->dict, str_c(ctx->path), ctx->flags);
870+
871+ return ctx;
872+}
873+
874+bool
875+dict_iterate_multiscope(struct dict_iterate_multiscope_context *ctx, const char **name, const char **value) {
876+ i_assert(ctx != NULL);
877+ if (ctx == NULL)
878+ return false;
879+
880+ if (ctx->failed)
881+ return false;
882+
883+ *name = NULL;
884+ while (*name == NULL) {
885+ bool ret = dict_iterate(ctx->dict_ctx, name, value);
886+
887+ /* we have no more shared/ keys and priv/ was also already iterated over */
888+ if (!ret && !ctx->do_shared) {
889+ return false;
890+ }
891+ /* no more priv/ keys, continue with shared/ */
892+ else if (!ret && ctx->do_shared) {
893+ if (dict_iterate_deinit(&ctx->dict_ctx) < 0) {
894+ ctx->failed = true;
895+ return false;
896+ }
897+
898+ /* replace priv/ with shared/ */
899+ str_delete(ctx->path, 0, sizeof(DICT_PATH_SHARED));
900+ str_insert(ctx->path, 0, DICT_PATH_PRIVATE);
901+
902+ ctx->do_shared = false;
903+
904+ ctx->dict_ctx = dict_iterate_init(ctx->dict, str_c(ctx->path), ctx->flags);
905+ if (ctx->dict_ctx == NULL) {
906+ ctx->failed = true;
907+ }
908+
909+ continue;
910+ }
911+ }
912+
913+ return true;
914+}
915+
916+int
917+dict_iterate_multiscope_deinit(struct dict_iterate_multiscope_context **ctx) {
918+ i_assert(ctx != NULL);
919+ if (ctx == NULL)
920+ return -1;
921+
922+ i_assert(*ctx != NULL);
923+ if (*ctx == NULL)
924+ return -1;
925+
926+ int ret = (*ctx)->failed ? -1 : 0;
927+
928+ if (dict_iterate_deinit(&(*ctx)->dict_ctx) < 0)
929+ ret = -1;
930+
931+ str_free(&(*ctx)->path);
932+ i_free(*ctx);
933+
934+ return ret;
935+}
936
937=== added file 'src/dict-ext.h'
938--- src/dict-ext.h 1970-01-01 00:00:00 +0000
939+++ src/dict-ext.h 2011-09-29 19:49:50 +0000
940@@ -0,0 +1,58 @@
941+/*
942+ Copyright (c) 2010 by Dennis Schridde
943+
944+ This file is part of dovecot-metadata.
945+
946+ dovecot-metadata is free software: you can redistribute it and/or modify
947+ it under the terms of the GNU Lesser General Public License as published by
948+ the Free Software Foundation, either version 3 of the License, or
949+ (at your option) any later version.
950+
951+ dovecot-metadata is distributed in the hope that it will be useful,
952+ but WITHOUT ANY WARRANTY; without even the implied warranty of
953+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
954+ GNU Lesser General Public License for more details.
955+
956+ You should have received a copy of the GNU Lesser General Public License
957+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
958+*/
959+#ifndef DOVECOT_DICT_EXT_H
960+#define DOVECOT_DICT_EXT_H
961+
962+#include "metadata-global.h"
963+
964+#include <stdbool.h>
965+
966+#include "dict.h"
967+
968+enum dict_scope {
969+ DICT_SCOPE_SHARED,
970+ DICT_SCOPE_PRIVATE,
971+ DICT_SCOPE_INVALID
972+};
973+
974+enum dict_scope
975+dict_get_scope(const char *key)
976+ ATTR_NONNULL(1);
977+
978+const char *
979+dict_path_from_scope(enum dict_scope scope);
980+
981+struct dict_iterate_multiscope_context;
982+
983+enum dict_iterate_multiscope_flags {
984+ DICT_ITERATE_MULTISCOPE_FLAG_MULTISCOPE = 0x1000
985+};
986+
987+struct dict_iterate_multiscope_context *
988+dict_iterate_multiscope_init(struct dict *dict, const char *path, enum dict_iterate_multiscope_flags flags)
989+ ATTR_NONNULL(1,2);
990+bool
991+dict_iterate_multiscope(struct dict_iterate_multiscope_context *ctx, const char **name, const char **value)
992+ ATTR_NONNULL(1,2,3);
993+int
994+dict_iterate_multiscope_deinit(struct dict_iterate_multiscope_context **ctx)
995+ ATTR_NONNULL(1);
996+
997+
998+#endif
999
1000=== modified file 'src/imap-annotatemore-plugin.c'
1001--- src/imap-annotatemore-plugin.c 2010-08-11 17:11:01 +0000
1002+++ src/imap-annotatemore-plugin.c 2011-09-29 19:49:50 +0000
1003@@ -1,20 +1,43 @@
1004-/* Copyright (C) 2008 by Intevation GmbH
1005- * Authors:
1006- * Bernhard Herzog <bh@intevation.de>
1007- *
1008- * This program is free software under the LGPL (>=v2.1)
1009- * Read the file COPYING coming with the software for details.
1010- */
1011-
1012-#include "lib.h"
1013+/*
1014+ Copyright (c) 2008 by Intevation GmbH / Bernhard Herzog <bh@intevation.de>
1015+ Copyright (c) 2010 by Dennis Schridde
1016+
1017+ This file is part of dovecot-metadata.
1018+
1019+ dovecot-metadata is free software: you can redistribute it and/or modify
1020+ it under the terms of the GNU Lesser General Public License as published by
1021+ the Free Software Foundation, either version 3 of the License, or
1022+ (at your option) any later version.
1023+
1024+ dovecot-metadata is distributed in the hope that it will be useful,
1025+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1026+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1027+ GNU Lesser General Public License for more details.
1028+
1029+ You should have received a copy of the GNU Lesser General Public License
1030+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
1031+*/
1032+#include "metadata-global.h"
1033+
1034 #include "str.h"
1035-#include "common.h"
1036+#include "imap-common.h"
1037+#include "imap-client.h"
1038 #include "imap-quote.h"
1039+#include "mailbox-list.h"
1040
1041 #include <string.h>
1042
1043-#include "metadata-plugin.h"
1044-#include "imap-annotatemore-plugin.h"
1045+#include "str-ext.h"
1046+#include "metadata-entry-private.h"
1047+#include "metadata-backend.h"
1048+
1049+/* The IMAP Annotatemore plugin is a partial implementation of draft-daboo-imap-annotatemore-08 */
1050+
1051+const char *imap_annotatemore_plugin_version = DOVECOT_VERSION;
1052+const char *imap_annotatemore_plugin_dependencies[] = { "metadata", NULL };
1053+
1054+static struct module *imap_annotatemore_module;
1055+static void (*next_hook_client_created)(struct client **client);
1056
1057 enum attribute_properties {
1058 ATTR_INVALID = 0x0001,
1059@@ -23,30 +46,32 @@
1060 ATTR_BOTH = ATTR_PUBLIC | ATTR_PRIVATE,
1061 };
1062
1063+
1064 static bool validate_entry_name(struct client_command_context *cmd,
1065 const char *entry)
1066 {
1067 if (entry == NULL) {
1068- client_send_tagline(cmd, "BAD Missing entry name.");
1069+ client_send_command_error(cmd, "Missing entry name.");
1070 return FALSE;
1071 }
1072
1073- if (entry[0] != '/') {
1074- client_send_tagline(cmd,
1075- "BAD entry name must start with slash.");
1076+ if (entry[0] != '/' && entry[0] != '*' && entry[0] != '%') {
1077+ client_send_command_error(cmd,
1078+ "Entry name must start with slash or be a glob.");
1079 return FALSE;
1080 }
1081
1082 return TRUE;
1083 }
1084
1085+
1086 static enum attribute_properties
1087 validate_attribute_name(struct client_command_context *cmd,
1088 const char *attribute)
1089 {
1090 if (attribute == NULL) {
1091- client_send_tagline(cmd,
1092- "BAD Missing or NIL attribute name.");
1093+ client_send_command_error(cmd,
1094+ "Missing or NIL attribute name.");
1095 return ATTR_INVALID;
1096 }
1097
1098@@ -56,64 +81,273 @@
1099 return ATTR_PRIVATE;
1100 } else if (strcmp(attribute, "value") == 0) {
1101 return ATTR_BOTH;
1102+ } else if (strchr(attribute, '*')) {
1103+ return ATTR_BOTH;
1104+ } else if (strchr(attribute, '%')) {
1105+ client_send_command_error(cmd, "'%' globs not supported.");
1106+ return ATTR_INVALID;
1107 } else {
1108- client_send_tagline(cmd,
1109- "BAD only 'value.shared' and"
1110- " 'value.priv' attributes are"
1111- " supported '.'");
1112+ client_send_command_error(cmd,
1113+ "Only 'value.shared' and 'value.priv' attributes"
1114+ " are supported '.'");
1115 return ATTR_INVALID;
1116 }
1117 }
1118
1119
1120+static const char *
1121+entry_scopes[ENTRY_SCOPE_MAX] = {
1122+ "private/", /* ENTRY_SCOPE_PRIVATE */
1123+ "shared/" /* ENTRY_SCOPE_SHARED */
1124+};
1125+
1126+static const char *
1127+entry_types[ENTRY_TYPE_MAX] = {
1128+ "vendor/", /* ENTRY_TYPE_VENDOR */
1129+ "", /* ENTRY_TYPE_RFC */
1130+};
1131+
1132+static const char **
1133+entry_subtypes_rfc[ENTRY_SUBJECT_MAX] = {
1134+ (const char*[]){ // server
1135+ "comment",
1136+ "admin",
1137+ NULL
1138+ },
1139+ (const char*[]){ // mailbox
1140+ "comment",
1141+ NULL
1142+ }
1143+};
1144+
1145+
1146+/* validates that the part after /vendor conforms to the RFC */
1147+static ATTR_NONNULL(1)
1148+bool is_valid_annotatemore_vendor_name(const char *name) {
1149+ const char *lastslash = NULL, *lastcr = NULL;
1150+ int num_components = 2;
1151+
1152+ for (const char *c = name; *c != '\0'; c++) {
1153+ switch (*c) {
1154+ case '/':
1155+ // Two consecutive slashes, or a slash at the end are an error
1156+ if (lastslash == c-1 || *(c+1) == '\0') {
1157+ return false;
1158+ }
1159+ lastslash = c;
1160+ num_components++;
1161+ break;
1162+ case '*':
1163+ case '%':
1164+ return false;
1165+ case '\r':
1166+ lastcr = c;
1167+ break;
1168+ case '\n':
1169+ // line ending has to be CRLF
1170+ if (lastcr != c-1) {
1171+ return false;
1172+ }
1173+ break;
1174+ default:
1175+ break;
1176+ }
1177+ }
1178+
1179+ return num_components >= 4;
1180+}
1181+
1182+
1183+static ATTR_NONNULL(1)
1184+bool is_valid_annotatemore_subtype_name(const char *name, enum metadata_entry_subject subject) {
1185+ bool found_subtype = false;
1186+
1187+ i_assert(subject > 0 && subject < ENTRY_SUBJECT_MAX);
1188+
1189+ for (const char **subtype = entry_subtypes_rfc[subject]; *subtype != NULL; subtype++) {
1190+ size_t subtype_len = strlen(*subtype);
1191+
1192+ if (strncasecmp(name, *subtype, subtype_len) == 0) {
1193+ found_subtype = true;
1194+ }
1195+ }
1196+
1197+ return found_subtype;
1198+}
1199+
1200+
1201+/* sets entry->type and returns remaining string */
1202+static ATTR_NONNULL(1)
1203+enum metadata_entry_type
1204+parse_entry_type(const char **name, enum metadata_entry_subject subject) {
1205+ if (**name == '\0')
1206+ return ENTRY_TYPE_NONE;
1207+
1208+ for (int type = 0; type < ENTRY_TYPE_MAX; type++) {
1209+ size_t type_len = strlen(entry_types[type]);
1210+
1211+ if (strncasecmp(*name, entry_types[type], type_len) == 0) {
1212+ *name += type_len;
1213+
1214+ switch (type) {
1215+ case ENTRY_TYPE_RFC:
1216+ if (!is_valid_annotatemore_subtype_name(*name, subject))
1217+ return ENTRY_TYPE_MAX;
1218+ break;
1219+ case ENTRY_TYPE_VENDOR:
1220+ if (!is_valid_annotatemore_vendor_name(*name))
1221+ return ENTRY_TYPE_MAX;
1222+ break;
1223+ }
1224+
1225+ return type;
1226+ }
1227+ }
1228+
1229+ return ENTRY_TYPE_MAX;
1230+}
1231+
1232+
1233+static ATTR_NONNULL(2)
1234+const char *
1235+backend_name(enum metadata_entry_scope scope, const char *name) {
1236+ if (name == NULL)
1237+ return NULL;
1238+
1239+ string_t *backend_name = t_str_new(128);
1240+ str_append(backend_name, "/");
1241+ str_append(backend_name, entry_scopes[scope]);
1242+ str_append(backend_name, name);
1243+
1244+ return str_c(backend_name);
1245+}
1246+
1247+
1248+/* fill entry with data parsed from entry->full_name */
1249+static ATTR_NONNULL(3)
1250+struct metadata_entry *
1251+parse_entry(struct mailbox *box, enum metadata_entry_scope scope, const char *name, const char *value) {
1252+ if (name == NULL || *name == '\0' || *name++ != '/')
1253+ return NULL;
1254+
1255+ string_t *backend_name = t_str_new(128);
1256+ str_append(backend_name, "/");
1257+ str_append(backend_name, entry_scopes[scope]);
1258+ str_append(backend_name, name);
1259+
1260+ enum metadata_entry_type type = parse_entry_type(&name, box ? ENTRY_SUBJECT_MAILBOX : ENTRY_SUBJECT_SERVER);
1261+ if (type >= ENTRY_TYPE_MAX)
1262+ return NULL;
1263+
1264+ return metadata_entry_alloc(box, str_c(backend_name), value);
1265+}
1266+
1267+
1268 static void send_annotation_line(struct client_command_context *cmd,
1269- const char *mailbox,
1270- const char *entry,
1271+ struct mailbox *box,
1272+ const char *entry_name,
1273 const char *value,
1274 bool private)
1275 {
1276 if (value != NULL) {
1277- string_t *str = t_str_new(128);
1278- str_append(str, "* ANNOTATION ");
1279- imap_quote_append_string(str, mailbox, FALSE);
1280- str_append(str, " ");
1281- imap_quote_append_string(str, entry, FALSE);
1282- str_printfa(str, " (\"value.%s\" ",
1283- private ? "priv" : "shared");
1284- imap_quote_append_string(str, value, FALSE);
1285- str_append(str, ")");
1286- client_send_line(cmd->client, str_c(str));
1287+ const char *mailbox_name = mailbox_get_vname(box);
1288+ const char *str = t_strdup_printf(
1289+ "* ANNOTATION %s %s (value.%s %s)",
1290+ mailbox_name, entry_name, private ? "priv" : "shared", value
1291+ );
1292+
1293+ client_send_line(cmd->client, str);
1294 }
1295 }
1296
1297
1298-static bool get_and_send_annotation(struct client_command_context *cmd,
1299- const char *mailbox,
1300- const char *entry,
1301+static void get_and_send_annotation(struct client_command_context *cmd,
1302+ struct mailbox *box,
1303+ const char *entry_name,
1304 enum attribute_properties scope)
1305 {
1306- const char *value;
1307- bool success = TRUE;
1308-
1309- if ((scope & ATTR_PUBLIC) != 0) {
1310- value = NULL;
1311- success = metadata_get_metadata_entry(cmd, mailbox, entry,
1312- &value, FALSE);
1313- send_annotation_line(cmd, mailbox, entry, value, FALSE);
1314- }
1315-
1316- if (!success) {
1317- return FALSE;
1318- }
1319-
1320- if ((scope & ATTR_PRIVATE) != 0) {
1321- value = NULL;
1322- success = metadata_get_metadata_entry(cmd, mailbox, entry,
1323- &value, TRUE);
1324- send_annotation_line(cmd, mailbox, entry, value, TRUE);
1325- }
1326-
1327- return success;
1328+ if (strchr(entry_name, '*')) {
1329+ int entrylastchar = strlen(entry_name);
1330+ if (entrylastchar > 0)
1331+ entrylastchar--;
1332+
1333+ /* We do not support more than one glob, and at no other location than the end */
1334+ if (strchr_num(entry_name, '*') == 1 && entry_name[entrylastchar] == '*') {
1335+ const char *entrypattern = t_strdup_until(entry_name, &entry_name[entrylastchar]);
1336+
1337+ if ((scope & ATTR_PUBLIC) != 0) {
1338+ struct metadata_entry *entry = metadata_entry_alloc(box, backend_name(ENTRY_SCOPE_SHARED, entrypattern), NULL);
1339+
1340+ struct metadata_iterate_context *iter = metadata_iterate_init(box, entry, METADATA_ITERATE_DEPTH_INF);
1341+ while (metadata_iterate(iter, entry)) {
1342+ const char *name = metadata_entry_get_name(entry) + strlen(entry_scopes[ENTRY_SCOPE_SHARED]);
1343+ const char *value = metadata_entry_get_value(entry);
1344+
1345+ send_annotation_line(cmd, box, name, value, FALSE);
1346+ }
1347+ if (metadata_iterate_deinit(&iter) < 0) {
1348+ client_send_tagline(cmd, "NO Iterating metadata failed.");
1349+ return;
1350+ }
1351+ }
1352+
1353+ if ((scope & ATTR_PRIVATE) != 0) {
1354+ struct metadata_entry *entry = metadata_entry_alloc(box, backend_name(ENTRY_SCOPE_PRIVATE, entrypattern), NULL);
1355+
1356+ struct metadata_iterate_context *iter = metadata_iterate_init(box, entry, METADATA_ITERATE_DEPTH_INF);
1357+ while (metadata_iterate(iter, entry)) {
1358+ const char *name = metadata_entry_get_name(entry) + strlen(entry_scopes[ENTRY_SCOPE_SHARED]);
1359+ const char *value = metadata_entry_get_value(entry);
1360+
1361+ send_annotation_line(cmd, box, name, value, TRUE);
1362+ }
1363+ if (metadata_iterate_deinit(&iter) < 0) {
1364+ client_send_tagline(cmd, "NO Iterating metadata failed.");
1365+ return;
1366+ }
1367+ }
1368+ } else {
1369+ client_send_command_error(cmd, "'*' globs only supported at end of pattern.");
1370+ return;
1371+ }
1372+ } else if (strchr(entry_name, '%')) {
1373+ client_send_command_error(cmd, "'%' globs not supported.");
1374+ return;
1375+ } else {
1376+ if ((scope & ATTR_PUBLIC) != 0) {
1377+ struct metadata_entry *entry = metadata_entry_alloc(box, backend_name(ENTRY_SCOPE_SHARED, entry_name), NULL);
1378+ if (entry == NULL) {
1379+ client_send_tagline(cmd, "NO Allocating entry failed.");
1380+ return;
1381+ }
1382+
1383+ int success = metadata_get_entry(entry, cmd->client->user);
1384+ if (success < 0) {
1385+ client_send_tagline(cmd, "NO Getting entry failed.");
1386+ return;
1387+ }
1388+ else if (success > 0) {
1389+ send_annotation_line(cmd, box, entry->name, entry->value, FALSE);
1390+ }
1391+ }
1392+
1393+ if ((scope & ATTR_PRIVATE) != 0) {
1394+ struct metadata_entry *entry = metadata_entry_alloc(box, backend_name(ENTRY_SCOPE_PRIVATE, entry_name), NULL);
1395+ if (entry == NULL) {
1396+ client_send_tagline(cmd, "NO Allocating entry failed.");
1397+ return;
1398+ }
1399+
1400+ int success = metadata_get_entry(entry, cmd->client->user);
1401+ if (success < 0) {
1402+ client_send_tagline(cmd, "NO Getting entry failed.");
1403+ return;
1404+ }
1405+ else if (success > 0) {
1406+ send_annotation_line(cmd, box, entry->name, entry->value, TRUE);
1407+ }
1408+ }
1409+ }
1410 }
1411
1412
1413@@ -121,11 +355,22 @@
1414 const struct imap_arg *attribute,
1415 const char **value_r)
1416 {
1417- const struct imap_arg *attrlist;
1418-
1419- if (IMAP_ARG_LIST_COUNT(attribute) == 1) {
1420- attrlist = IMAP_ARG_LIST_ARGS(attribute);
1421- *value_r = IMAP_ARG_STR(&attrlist[0]);
1422+ const struct imap_arg *attrlist = NULL;
1423+ unsigned int attrcount = 0;
1424+
1425+ if (!imap_arg_get_list_full(attribute, &attrlist, &attrcount)) {
1426+ // Actually this should never happen, since we first test args[1].type == IMAP_ARG_LIST ! */
1427+ i_error("metadata: got attributes of non-list type after confirming they were of correct type!");
1428+ client_send_command_error(cmd, "Attributes must be of list type.");
1429+ return FALSE;
1430+ }
1431+
1432+ if (attrcount == 1) {
1433+ if (!imap_arg_get_astring(&attrlist[0], value_r)) {
1434+ client_send_command_error(cmd,
1435+ "Value must be of string type.");
1436+ return FALSE;
1437+ }
1438 return TRUE;
1439 } else {
1440 client_send_tagline(cmd,
1441@@ -138,22 +383,26 @@
1442 static bool cmd_getannotation(struct client_command_context *cmd)
1443 {
1444 const struct imap_arg *args;
1445- const char *mailbox;
1446- const char *entry;
1447+ const char *mailbox_name;
1448+ const char *entry_name;
1449 const char *attribute;
1450 enum attribute_properties attribute_properties;
1451
1452 if (!client_read_args(cmd, 3, 0, &args))
1453 return FALSE;
1454
1455- mailbox = IMAP_ARG_STR(&args[0]);
1456- if (mailbox == NULL) {
1457- client_send_tagline(cmd,
1458- "BAD Missing mailbox name.");
1459+ if (!imap_arg_get_astring(&args[0], &mailbox_name)) {
1460+ client_send_command_error(cmd,
1461+ "Mailbox name must be of string type.");
1462+ return TRUE;
1463+ }
1464+ if (mailbox_name == NULL) {
1465+ client_send_command_error(cmd,
1466+ "Missing mailbox name.");
1467 return TRUE;
1468 }
1469
1470- if (*mailbox == '\0') {
1471+ if (*mailbox_name == '\0') {
1472 client_send_tagline(cmd,
1473 "NO Server annotations not yet"
1474 " implemented.");
1475@@ -161,26 +410,77 @@
1476 }
1477
1478 if (args[1].type == IMAP_ARG_LIST) {
1479- if (!extract_single_value(cmd, &args[1], &entry))
1480- return TRUE;
1481- } else
1482- entry = IMAP_ARG_STR(&args[1]);
1483+ if (!extract_single_value(cmd, &args[1], &entry_name))
1484+ return TRUE;
1485+ } else {
1486+ if (!imap_arg_get_astring(&args[1], &entry_name)) {
1487+ client_send_command_error(cmd,
1488+ "Entry name must be of string type.");
1489+ return TRUE;
1490+ }
1491+ }
1492
1493- if (!validate_entry_name(cmd, entry))
1494+ if (!validate_entry_name(cmd, entry_name))
1495 return TRUE;
1496
1497 if (args[2].type == IMAP_ARG_LIST) {
1498 if (!extract_single_value(cmd, &args[2], &attribute))
1499 return TRUE;
1500- } else
1501- attribute = IMAP_ARG_STR(&args[2]);
1502+ } else {
1503+ if (!imap_arg_get_astring(&args[2], &attribute)) {
1504+ client_send_command_error(cmd,
1505+ "Attribute must be of string type.");
1506+ return TRUE;
1507+ }
1508+ }
1509
1510 attribute_properties = validate_attribute_name(cmd, attribute);
1511 if (attribute_properties & ATTR_INVALID)
1512 return TRUE;
1513
1514- if (get_and_send_annotation(cmd, mailbox, entry, attribute_properties))
1515- client_send_tagline(cmd, "OK Completed.");
1516+ if (str_has_wildcards(mailbox_name)) {
1517+ for (const struct mail_namespace *ns = cmd->client->user->namespaces; ns != NULL; ns = ns->next) {
1518+ const struct mailbox_info *info = NULL;
1519+
1520+ struct mailbox_list_iterate_context *ctx = mailbox_list_iter_init(ns->list, mailbox_name, 0);
1521+ while ((info = mailbox_list_iter_next(ctx)) != NULL) {
1522+ i_debug("Getting info for mailbox '%s'", info->name);
1523+
1524+ struct mailbox *box = mailbox_alloc(ns->list, info->name, MAILBOX_FLAG_READONLY);
1525+ if (box == NULL) {
1526+ client_send_tagline(cmd, "NO Allocating mailbox failed.");
1527+ return TRUE;
1528+ }
1529+
1530+ get_and_send_annotation(cmd, box, entry_name, attribute_properties);
1531+
1532+ mailbox_free(&box);
1533+ }
1534+ if (mailbox_list_iter_deinit(&ctx) < 0) {
1535+ client_send_tagline(cmd, "NO Iterating mailboxes failed.");
1536+ }
1537+ }
1538+ } else {
1539+ struct mail_namespace *ns = mail_namespace_find(cmd->client->user->namespaces, &mailbox_name);
1540+ if (ns == NULL) {
1541+ client_send_tagline(cmd,
1542+ "NO Mailbox not found.");
1543+ return TRUE;
1544+ }
1545+
1546+ struct mailbox *box = mailbox_alloc(ns->list, mailbox_name, MAILBOX_FLAG_READONLY);
1547+ if (box == NULL) {
1548+ client_send_tagline(cmd,
1549+ "NO Allocating mailbox failed.");
1550+ return TRUE;
1551+ }
1552+
1553+ get_and_send_annotation(cmd, box, entry_name, attribute_properties);
1554+
1555+ mailbox_free(&box);
1556+ }
1557+
1558+ client_send_tagline(cmd, "OK Completed.");
1559
1560 return TRUE;
1561 }
1562@@ -194,44 +494,50 @@
1563 const struct imap_arg *pairs;
1564 unsigned int count;
1565
1566- if (attributes->type != IMAP_ARG_LIST) {
1567- client_send_tagline(cmd,
1568- "BAD attributes parameter must be a list"
1569+ if (!imap_arg_get_list_full(attributes, &pairs, &count)) {
1570+ client_send_command_error(cmd,
1571+ "Attributes parameter must be a list"
1572 " of attribute value pairs.");
1573 return FALSE;
1574 }
1575
1576- count = IMAP_ARG_LIST_COUNT(attributes);
1577- pairs = IMAP_ARG_LIST_ARGS(attributes);
1578-
1579 if (count % 2 != 0) {
1580- client_send_tagline(cmd,
1581- "BAD list of attribute value pairs"
1582+ client_send_command_error(cmd,
1583+ "List of attribute value pairs"
1584 " must have an even number of elements");
1585 return FALSE;
1586 }
1587
1588 if (count == 0) {
1589- client_send_tagline(cmd,
1590- "BAD list of attribute value pairs"
1591+ client_send_command_error(cmd,
1592+ "List of attribute value pairs"
1593 " is empty");
1594 return FALSE;
1595 }
1596
1597 if (count == 2) {
1598 enum attribute_properties properties;
1599- properties = validate_attribute_name(cmd,
1600- IMAP_ARG_STR(&pairs[0]));
1601+ const char *tmp;
1602+ if (!imap_arg_get_astring(&pairs[0], &tmp)) {
1603+ client_send_command_error(cmd,
1604+ "Attribute must be of string type.");
1605+ return FALSE;
1606+ }
1607+ properties = validate_attribute_name(cmd, tmp);
1608 if ((properties & ATTR_INVALID) != 0)
1609 return FALSE;
1610 if ((properties & ATTR_BOTH) == ATTR_BOTH) {
1611- client_send_tagline(cmd,
1612- "BAD attribute must end in .priv"
1613+ client_send_command_error(cmd,
1614+ "Attribute must end in .priv"
1615 " or .shared for SETANNOTATION");
1616 return FALSE;
1617 }
1618
1619- *value_r = IMAP_ARG_STR(&pairs[1]);
1620+ if (!imap_arg_get_astring(&pairs[1], value_r)) {
1621+ client_send_command_error(cmd,
1622+ "Value must be of string type.");
1623+ return FALSE;
1624+ }
1625 *private_r = ((properties & ATTR_PRIVATE) != 0);
1626 return TRUE;
1627 }
1628@@ -244,22 +550,25 @@
1629 static bool cmd_setannotation(struct client_command_context *cmd)
1630 {
1631 const struct imap_arg *args;
1632- const char *mailbox;
1633- const char *entry;
1634+ const char *mailbox_name;
1635+ const char *entry_name;
1636 const char *value;
1637 bool private;
1638- bool success;
1639
1640 if (!client_read_args(cmd, 3, 0, &args))
1641 return FALSE;
1642
1643- mailbox = IMAP_ARG_STR(&args[0]);
1644- if (mailbox == NULL) {
1645- client_send_tagline(cmd,
1646- "BAD Missing mailbox name.");
1647- return TRUE;
1648- }
1649- if (*mailbox == '\0') {
1650+ if (!imap_arg_get_astring(&args[0], &mailbox_name)) {
1651+ client_send_command_error(cmd,
1652+ "Mailbox name must be of string type.");
1653+ return TRUE;
1654+ }
1655+ if (mailbox_name == NULL) {
1656+ client_send_command_error(cmd,
1657+ "Missing mailbox name.");
1658+ return TRUE;
1659+ }
1660+ if (*mailbox_name == '\0') {
1661 client_send_tagline(cmd,
1662 "NO Server annotations not yet"
1663 " implemented.");
1664@@ -271,38 +580,84 @@
1665 "NO Lists of entries not yet implemented.");
1666 return TRUE;
1667 }
1668- entry = IMAP_ARG_STR(&args[1]);
1669- if (!validate_entry_name(cmd, entry))
1670+ if (!imap_arg_get_astring(&args[1], &entry_name)) {
1671+ client_send_command_error(cmd,
1672+ "Entry name must be of string type.");
1673+ return TRUE;
1674+ }
1675+ if (entry_name == NULL) {
1676+ client_send_tagline(cmd, "NO Entry name is NULL.");
1677+ return true;
1678+ }
1679+
1680+ if (!validate_entry_name(cmd, entry_name))
1681 return TRUE;
1682
1683 if (!pair_extract_value(cmd, &args[2], &value, &private))
1684 return TRUE;
1685
1686- if (private && !metadata_private_allowed()) {
1687- client_send_tagline(cmd,
1688- "NO private annotations not supported.");
1689- return TRUE;
1690- }
1691-
1692- success = metadata_set_metadata_entry(cmd, mailbox, entry, value,
1693- private);
1694- if (success) {
1695- client_send_tagline(cmd, "OK Completed.");
1696- }
1697+ struct mail_namespace *ns = mail_namespace_find(cmd->client->user->namespaces, &mailbox_name);
1698+ if (ns == NULL) {
1699+ client_send_tagline(cmd,
1700+ "NO Mailbox not found.");
1701+ return TRUE;
1702+ }
1703+
1704+ struct mailbox *box = mailbox_alloc(ns->list, mailbox_name, MAILBOX_FLAG_READONLY);
1705+ if (box == NULL) {
1706+ client_send_tagline(cmd,
1707+ "NO Allocating mailbox failed.");
1708+ return TRUE;
1709+ }
1710+
1711+ struct metadata_entry *entry = parse_entry(box, private ? ENTRY_SCOPE_PRIVATE : ENTRY_SCOPE_SHARED, entry_name, value);
1712+ if (entry == NULL) {
1713+ client_send_tagline(cmd,
1714+ "NO Parsing entry failed.");
1715+ mailbox_free(&box);
1716+ return TRUE;
1717+ }
1718+
1719+ if (metadata_set_entry(entry, cmd->client->user) < 0) {
1720+ client_send_tagline(cmd,
1721+ "NO Setting entry failed.");
1722+ mailbox_free(&box);
1723+ return TRUE;
1724+ }
1725+
1726+ client_send_tagline(cmd, "OK Completed.");
1727+
1728+ mailbox_free(&box);
1729
1730 return TRUE;
1731 }
1732
1733
1734-void imap_annotatemore_plugin_init(void)
1735+static void imap_annotatemore_client_created(struct client **client)
1736+{
1737+ if (mail_user_is_plugin_loaded((*client)->user, imap_annotatemore_module))
1738+ str_append((*client)->capability_string, " ANNOTATEMORE");
1739+
1740+ if (next_hook_client_created != NULL)
1741+ next_hook_client_created(client);
1742+}
1743+
1744+
1745+void imap_annotatemore_plugin_init(struct module *module)
1746 {
1747 command_register("GETANNOTATION", cmd_getannotation, 0);
1748 command_register("SETANNOTATION", cmd_setannotation, 0);
1749- str_append(capability_string, " ANNOTATEMORE");
1750+
1751+ imap_annotatemore_module = module;
1752+ next_hook_client_created = hook_client_created;
1753+ hook_client_created = imap_annotatemore_client_created;
1754 }
1755
1756+
1757 void imap_annotatemore_plugin_deinit(void)
1758 {
1759 command_unregister("SETANNOTATION");
1760 command_unregister("GETANNOTATION");
1761+
1762+ hook_client_created = next_hook_client_created;
1763 }
1764
1765=== removed file 'src/imap-annotatemore-plugin.h'
1766--- src/imap-annotatemore-plugin.h 2010-08-11 17:11:01 +0000
1767+++ src/imap-annotatemore-plugin.h 1970-01-01 00:00:00 +0000
1768@@ -1,7 +0,0 @@
1769-#ifndef __ANNOTATEMORE_PLUGIN
1770-#define __ANNOTATEMORE_PLUGIN
1771-
1772-void imap_annotatemore_plugin_init(void);
1773-void imap_annotatemore_plugin_deinit(void);
1774-
1775-#endif
1776
1777=== added file 'src/imap-arg-ext.c'
1778--- src/imap-arg-ext.c 1970-01-01 00:00:00 +0000
1779+++ src/imap-arg-ext.c 2011-09-29 19:49:50 +0000
1780@@ -0,0 +1,83 @@
1781+/*
1782+ Copyright (c) 2010 by Dennis Schridde
1783+
1784+ This file is part of dovecot-metadata.
1785+
1786+ dovecot-metadata is free software: you can redistribute it and/or modify
1787+ it under the terms of the GNU Lesser General Public License as published by
1788+ the Free Software Foundation, either version 3 of the License, or
1789+ (at your option) any later version.
1790+
1791+ dovecot-metadata is distributed in the hope that it will be useful,
1792+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1793+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1794+ GNU Lesser General Public License for more details.
1795+
1796+ You should have received a copy of the GNU Lesser General Public License
1797+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
1798+*/
1799+#include "imap-arg-ext.h"
1800+
1801+#include <stdlib.h>
1802+
1803+bool
1804+imap_arg_get_astringlist(const struct imap_arg *arg, const char ***list_r) {
1805+ i_assert(list_r != NULL);
1806+ if (list_r == NULL) {
1807+ return false;
1808+ }
1809+
1810+ if (*list_r != NULL) {
1811+ free(*list_r);
1812+ *list_r = NULL;
1813+ }
1814+
1815+ size_t list_size = 0;
1816+
1817+ if (arg->type == IMAP_ARG_LIST) { // entries
1818+ const struct imap_arg *arglist = NULL;
1819+ if (!imap_arg_get_list(arg, &arglist)) {
1820+ return false;
1821+ }
1822+
1823+ while (arglist[list_size].type != IMAP_ARG_EOL) {
1824+ const char *astring = NULL;
1825+ if (!imap_arg_get_astring(&arglist[list_size], &astring)) {
1826+ free(*list_r);
1827+ *list_r = NULL;
1828+ return false;
1829+ }
1830+
1831+ *list_r = realloc(*list_r, (list_size+2)*sizeof(*list_r));
1832+ if (*list_r == NULL) {
1833+ return false;
1834+ }
1835+
1836+ (*list_r)[list_size] = astring;
1837+
1838+ list_size++;
1839+ }
1840+ }
1841+ else if (IMAP_ARG_TYPE_IS_ASTRING(arg->type)) {
1842+ const char *astring = NULL;
1843+ if (!imap_arg_get_astring(arg, &astring)) {
1844+ return false;
1845+ }
1846+
1847+ *list_r = realloc(*list_r, (list_size+2)*sizeof(*list_r));
1848+ if (*list_r == NULL) {
1849+ return false;
1850+ }
1851+
1852+ (*list_r)[list_size] = astring;
1853+
1854+ list_size++;
1855+ }
1856+ else {
1857+ return false;
1858+ }
1859+
1860+ (*list_r)[list_size] = NULL;
1861+
1862+ return true;
1863+}
1864
1865=== added file 'src/imap-arg-ext.h'
1866--- src/imap-arg-ext.h 1970-01-01 00:00:00 +0000
1867+++ src/imap-arg-ext.h 2011-09-29 19:49:50 +0000
1868@@ -0,0 +1,32 @@
1869+/*
1870+ Copyright (c) 2010 by Dennis Schridde
1871+
1872+ This file is part of dovecot-metadata.
1873+
1874+ dovecot-metadata is free software: you can redistribute it and/or modify
1875+ it under the terms of the GNU Lesser General Public License as published by
1876+ the Free Software Foundation, either version 3 of the License, or
1877+ (at your option) any later version.
1878+
1879+ dovecot-metadata is distributed in the hope that it will be useful,
1880+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1881+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1882+ GNU Lesser General Public License for more details.
1883+
1884+ You should have received a copy of the GNU Lesser General Public License
1885+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
1886+*/
1887+#ifndef DOVECOT_IMAP_ARG_EXT_H
1888+#define DOVECOT_IMAP_ARG_EXT_H
1889+
1890+#include "metadata-global.h"
1891+
1892+#include <stdbool.h>
1893+
1894+#include "imap-arg.h"
1895+
1896+bool
1897+imap_arg_get_astringlist(const struct imap_arg *arg, const char ***list_r)
1898+ ATTR_WARN_UNUSED_RESULT;
1899+
1900+#endif
1901
1902=== added file 'src/imap-metadata-plugin.c'
1903--- src/imap-metadata-plugin.c 1970-01-01 00:00:00 +0000
1904+++ src/imap-metadata-plugin.c 2011-09-29 19:49:50 +0000
1905@@ -0,0 +1,699 @@
1906+/*
1907+ Copyright (c) 2010 by Dennis Schridde
1908+
1909+ This file is part of dovecot-metadata.
1910+
1911+ dovecot-metadata is free software: you can redistribute it and/or modify
1912+ it under the terms of the GNU Lesser General Public License as published by
1913+ the Free Software Foundation, either version 3 of the License, or
1914+ (at your option) any later version.
1915+
1916+ dovecot-metadata is distributed in the hope that it will be useful,
1917+ but WITHOUT ANY WARRANTY; without even the implied warranty of
1918+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1919+ GNU Lesser General Public License for more details.
1920+
1921+ You should have received a copy of the GNU Lesser General Public License
1922+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
1923+*/
1924+#include "metadata-global.h"
1925+
1926+#include "imap-common.h"
1927+#include "imap-client.h"
1928+#include "imap-quote.h"
1929+
1930+#include <stdbool.h>
1931+#include <stdlib.h>
1932+
1933+#include "str-ext.h"
1934+#include "imap-arg-ext.h"
1935+#include "dict-ext.h"
1936+#include "metadata-entry.h"
1937+#include "metadata-backend.h"
1938+#include "metadata-mail-user-module-private.h"
1939+
1940+/* The IMAP Metadata plugin is an implementation of RFC 5464 */
1941+
1942+const char *imap_metadata_plugin_version = DOVECOT_VERSION;
1943+const char *imap_metadata_plugin_dependencies[] = { "metadata", NULL };
1944+
1945+static struct module *imap_metadata_module;
1946+static void (*next_hook_client_created)(struct client **client);
1947+
1948+
1949+static const char *
1950+entry_scopes[ENTRY_SCOPE_MAX] = {
1951+ "private/", /* ENTRY_SCOPE_PRIVATE */
1952+ "shared/" /* ENTRY_SCOPE_SHARED */
1953+};
1954+
1955+static const char *
1956+entry_types[ENTRY_TYPE_MAX] = {
1957+ "vendor/", /* ENTRY_TYPE_VENDOR */
1958+ "", /* ENTRY_TYPE_RFC */
1959+};
1960+
1961+static const char **
1962+entry_subtypes_rfc[ENTRY_SUBJECT_MAX] = {
1963+ (const char*[]){ // server
1964+ "comment",
1965+ "admin",
1966+ NULL
1967+ },
1968+ (const char*[]){ // mailbox
1969+ "comment",
1970+ NULL
1971+ }
1972+};
1973+
1974+
1975+enum getmetadata_option {
1976+ GETMETADATA_OPTION_MAXSIZE,
1977+ GETMEDADATA_OPTION_DEPTH
1978+};
1979+
1980+struct option_definition {
1981+ const char *name;
1982+ int num_values;
1983+ enum getmetadata_option option;
1984+};
1985+
1986+static
1987+struct option_definition
1988+getmetadata_options[] = {
1989+ {"maxsize", 1, GETMETADATA_OPTION_MAXSIZE},
1990+ {"depth", 1, GETMEDADATA_OPTION_DEPTH},
1991+ {NULL, 0, 0}
1992+};
1993+
1994+
1995+static
1996+struct option_definition *
1997+parse_getmetadata_option(const char *option) {
1998+ struct option_definition *optdef = getmetadata_options;
1999+ while (optdef->name != NULL) {
2000+ if (strcasecmp(optdef->name, option) == 0) {
2001+ return optdef;
2002+ }
2003+ optdef++;
2004+ }
2005+ return NULL;
2006+}
2007+
2008+
2009+static int
2010+parse_getmetadata_depth(const char *value) {
2011+ if (!str_is_numeric(value, '\0')) {
2012+ if (strcasecmp(value, "infinity"))
2013+ return METADATA_ITERATE_DEPTH_INF;
2014+
2015+ return -1;
2016+ }
2017+
2018+ char *end = NULL;
2019+ long int val = strtol(value, &end, 10);
2020+
2021+ if (end == value)
2022+ return -2;
2023+ if (val == LONG_MAX)
2024+ return -3;
2025+ if (val < 0)
2026+ return -4;
2027+
2028+ return val;
2029+}
2030+
2031+
2032+static int
2033+parse_getmetadata_maxsize(const char *value) {
2034+ if (!str_is_numeric(value, '\0'))
2035+ return -1;
2036+
2037+ char *end = NULL;
2038+ long int val = strtol(value, &end, 10);
2039+
2040+ if (end == value)
2041+ return -2;
2042+ if (val == LONG_MAX)
2043+ return -3;
2044+ if (val < 0)
2045+ return -4;
2046+
2047+ return val;
2048+}
2049+
2050+
2051+/* validates that the part after /vendor conforms to the RFC */
2052+static ATTR_NONNULL(1)
2053+bool
2054+is_valid_rfc5464_vendor_name(const char *name) {
2055+ const char *lastslash = NULL, *lastcr = NULL;
2056+ int num_components = 3; // "vendor/" already includes the slash of component No3
2057+
2058+ for (const char *c = name; *c != '\0'; c++) {
2059+ switch (*c) {
2060+ case '/':
2061+ // Two consecutive slashes, or a slash at the end are an error
2062+ if (lastslash == c-1 || *(c+1) == '\0') {
2063+ return false;
2064+ }
2065+ lastslash = c;
2066+ num_components++;
2067+ break;
2068+ case '*':
2069+ case '%':
2070+ return false;
2071+ case '\r':
2072+ lastcr = c;
2073+ break;
2074+ case '\n':
2075+ // line ending has to be CRLF
2076+ if (lastcr != c-1) {
2077+ return false;
2078+ }
2079+ break;
2080+ default:
2081+ break;
2082+ }
2083+ }
2084+
2085+ return num_components >= 4;
2086+}
2087+
2088+
2089+static ATTR_NONNULL(1)
2090+bool
2091+is_valid_rfc5464_subtype_name(const char *name, enum metadata_entry_subject subject) {
2092+ bool found_subtype = false;
2093+
2094+ i_assert(subject > 0 && subject < ENTRY_SUBJECT_MAX);
2095+
2096+ for (const char **subtype = entry_subtypes_rfc[subject]; *subtype != NULL; subtype++) {
2097+ size_t subtype_len = strlen(*subtype);
2098+
2099+ if (strncasecmp(name, *subtype, subtype_len) == 0
2100+ && name[subtype_len] == '\0') {
2101+ found_subtype = true;
2102+ }
2103+ }
2104+
2105+ return found_subtype;
2106+}
2107+
2108+
2109+/* sets entry->scope and returns remaining string */
2110+static ATTR_NONNULL(1)
2111+enum metadata_entry_scope
2112+parse_entry_scope(const char **name) {
2113+ for (int scope = 0; scope < ENTRY_SCOPE_MAX; scope++) {
2114+ size_t scope_len = strlen(entry_scopes[scope]);
2115+
2116+ if (strncasecmp(*name, entry_scopes[scope], scope_len) == 0) {
2117+ *name += scope_len;
2118+ return scope;
2119+ }
2120+ }
2121+
2122+ return ENTRY_SCOPE_MAX;
2123+}
2124+
2125+
2126+/* sets entry->type and returns remaining string */
2127+static ATTR_NONNULL(1)
2128+enum metadata_entry_type
2129+parse_entry_type(const char **name, enum metadata_entry_subject subject) {
2130+ for (int type = 0; type < ENTRY_TYPE_MAX; type++) {
2131+ size_t type_len = strlen(entry_types[type]);
2132+
2133+ if (strncasecmp(*name, entry_types[type], type_len) == 0) {
2134+ *name += type_len;
2135+
2136+ switch (type) {
2137+ case ENTRY_TYPE_RFC:
2138+ if (!is_valid_rfc5464_subtype_name(*name, subject))
2139+ return ENTRY_TYPE_MAX;
2140+ break;
2141+ case ENTRY_TYPE_VENDOR:
2142+ if (!is_valid_rfc5464_vendor_name(*name))
2143+ return ENTRY_TYPE_MAX;
2144+ break;
2145+ }
2146+
2147+ return type;
2148+ }
2149+ }
2150+
2151+ return ENTRY_TYPE_MAX;
2152+}
2153+
2154+
2155+/* fill entry with data parsed from entry->full_name */
2156+static ATTR_NONNULL(2)
2157+struct metadata_entry *
2158+parse_entry(struct mailbox *box, const char *name, const char *value) {
2159+ const char *name_tmp = name;
2160+ if (name_tmp == NULL || *name_tmp++ != '/')
2161+ return NULL;
2162+
2163+ enum metadata_entry_scope scope = parse_entry_scope(&name_tmp);
2164+ if (scope >= ENTRY_SCOPE_MAX)
2165+ return NULL;
2166+
2167+ enum metadata_entry_type type = parse_entry_type(&name_tmp, box ? ENTRY_SUBJECT_MAILBOX : ENTRY_SUBJECT_SERVER);
2168+ if (type >= ENTRY_TYPE_MAX)
2169+ return NULL;
2170+
2171+ return metadata_entry_alloc(box, name, value);
2172+}
2173+
2174+
2175+static int
2176+get_and_send_entry(struct client_command_context *cmd, struct mailbox *box, const char *name, int depth, int maxsize, int *longentries) {
2177+ if (str_has_wildcards(name)) {
2178+ client_send_tagline(cmd, "NO Wildcards in entry name not allowed.");
2179+ return -1;
2180+ }
2181+
2182+ if (depth == 0) {
2183+ struct metadata_entry *entry = parse_entry(box, name, NULL);
2184+ if (entry == NULL) {
2185+ client_send_tagline(cmd, "NO Parsing entry failed.");
2186+ return -1;
2187+ }
2188+
2189+ int success = metadata_get_entry(entry, cmd->client->user);
2190+ if (success < 0) {
2191+ i_assert(0);
2192+ client_send_tagline(cmd, "NO Getting entry failed.");
2193+ return -1;
2194+ }
2195+ else if (success > 0) {
2196+ const char *str = t_strdup_printf(
2197+ "* METADATA %s (%s %s)",
2198+ mailbox_get_vname(box), metadata_entry_get_name(entry), metadata_entry_get_value(entry)
2199+ );
2200+
2201+ return client_send_line(cmd->client, str);
2202+ }
2203+
2204+ return 0;
2205+ }
2206+
2207+ struct metadata_entry *entry = metadata_entry_alloc(box, name, NULL);
2208+
2209+ int num_entries = 0;
2210+
2211+ string_t *str = t_str_new(128);
2212+ str_append_printf(str, "* METADATA %s (", mailbox_get_vname(box));
2213+
2214+ struct metadata_iterate_context *iter = metadata_iterate_init(box, entry, depth);
2215+ while (metadata_iterate(iter, entry)) {
2216+ const char *name = metadata_entry_get_name(entry);
2217+ const char *value = metadata_entry_get_value(entry);
2218+
2219+ /* only respect maxsize if it is not 'undefined' */
2220+ if (maxsize > 0) {
2221+ size_t val_len = strlen(value);
2222+ if (val_len > maxsize && val_len > *longentries) {
2223+ *longentries = val_len;
2224+ continue;
2225+ }
2226+ }
2227+
2228+ str_append_printf(str, "%s %s ", name, value);
2229+
2230+ num_entries++;
2231+ }
2232+ if (metadata_iterate_deinit(&iter) < 0) {
2233+ client_send_tagline(cmd, "NO Iterating metadata failed.");
2234+ return -1;
2235+ }
2236+
2237+ str_append(str, ")");
2238+
2239+ if (num_entries > 0) {
2240+ return client_send_line(cmd->client, str_c(str));
2241+ }
2242+
2243+ return 0;
2244+}
2245+
2246+
2247+static bool
2248+cmd_getmetadata(struct client_command_context *cmd) {
2249+ const struct imap_arg *args;
2250+ int maxsize = 0, depth = 0;
2251+
2252+ if (!client_read_args(cmd, 0, 0, &args))
2253+ return false;
2254+
2255+ if (args[0].type == IMAP_ARG_LIST) { // options
2256+ const struct imap_arg *arglist = NULL;
2257+ if (!imap_arg_get_list(&args[0], &arglist)) {
2258+ client_send_command_error(cmd, "Cannot read options, list expected.");
2259+ return true;
2260+ }
2261+
2262+ while (arglist->type != IMAP_ARG_EOL) {
2263+ if (!IMAP_ARG_TYPE_IS_ASTRING(arglist->type)) {
2264+ client_send_command_error(cmd, "Option not a string.");
2265+ return true;
2266+ }
2267+
2268+ const char *option = NULL;
2269+ if (!imap_arg_get_astring(arglist, &option)){
2270+ client_send_command_error(cmd, "Cannot read option, string expected.");
2271+ return true;
2272+ }
2273+
2274+ struct option_definition *optdef = parse_getmetadata_option(option);
2275+ if (optdef == NULL) {
2276+ const char *estr = t_strdup_printf("Unknown option: %s.", option);
2277+ client_send_command_error(cmd, estr);
2278+ return true;
2279+ }
2280+
2281+ arglist++;
2282+
2283+ const char *values[optdef->num_values];
2284+ memset(values, 0, sizeof(*values) * optdef->num_values);
2285+
2286+ for (int i = 0; i < optdef->num_values; i++) {
2287+ if (!IMAP_ARG_TYPE_IS_ASTRING(arglist[i].type)) {
2288+ const char *estr = t_strdup_printf(
2289+ "Value %d/%d of %s not a string.",
2290+ i, optdef->num_values, option
2291+ );
2292+ client_send_command_error(cmd, estr);
2293+ return true;
2294+ }
2295+
2296+ if (!imap_arg_get_astring(&arglist[i], &values[i])){
2297+ const char *estr = t_strdup_printf(
2298+ "Cannot read value %d/%d of %s, string expected.",
2299+ i, optdef->num_values, option
2300+ );
2301+ client_send_command_error(cmd, estr);
2302+ return true;
2303+ }
2304+ }
2305+
2306+ switch (optdef->option) {
2307+ case GETMEDADATA_OPTION_DEPTH:
2308+ depth = parse_getmetadata_depth(values[0]);
2309+ if (depth < 0) {
2310+ client_send_command_error(cmd, "Value 1/1 of DEPTH is not numeric and positive or \"infinity\".");
2311+ return true;
2312+ }
2313+ break;
2314+ case GETMETADATA_OPTION_MAXSIZE:
2315+ maxsize = parse_getmetadata_maxsize(values[0]);
2316+ if (maxsize < 0) {
2317+ client_send_command_error(cmd, "Value 1/1 of MAXSIZE is not numeric and positive.");
2318+ return true;
2319+ }
2320+ break;
2321+ }
2322+
2323+ arglist += optdef->num_values;
2324+ }
2325+
2326+ args++;
2327+ }
2328+
2329+ if (!IMAP_ARG_TYPE_IS_ASTRING(args[0].type)) { // mailbox name
2330+ client_send_command_error(cmd, "Mailbox name not a string.");
2331+ return true;
2332+ }
2333+
2334+ const char *mailbox_name = NULL;
2335+ if (!imap_arg_get_astring(&args[0], &mailbox_name)){
2336+ client_send_command_error(cmd, "Cannot read mailbox name, string expected.");
2337+ return true;
2338+ }
2339+
2340+ if (mailbox_name == NULL) {
2341+ client_send_tagline(cmd, "NO Mailbox name is NULL.");
2342+ return true;
2343+ }
2344+
2345+ const char **entry_names = NULL;
2346+ if (!imap_arg_get_astringlist(&args[1], &entry_names)) {
2347+ client_send_command_error(cmd, "Cannot read entries, string or list of strings expected.");
2348+ return true;
2349+ }
2350+
2351+ int warn_longentries = 0;
2352+
2353+ if (str_has_wildcards(mailbox_name)) {
2354+ for (const struct mail_namespace *ns = cmd->client->user->namespaces; ns != NULL; ns = ns->next) {
2355+ const struct mailbox_info *info = NULL;
2356+
2357+ struct mailbox_list_iterate_context *ctx = mailbox_list_iter_init(ns->list, mailbox_name, 0);
2358+ while ((info = mailbox_list_iter_next(ctx)) != NULL) {
2359+ i_debug("Getting info for mailbox '%s'", info->name);
2360+
2361+ struct mailbox *box = mailbox_alloc(ns->list, info->name, MAILBOX_FLAG_READONLY);
2362+ if (box == NULL) {
2363+ client_send_tagline(cmd, "NO Allocating mailbox failed.");
2364+ return TRUE;
2365+ }
2366+
2367+ const char **entry_name = entry_names;
2368+ while (*entry_name != NULL) {
2369+ if (get_and_send_entry(cmd, box, *entry_name, depth, maxsize, &warn_longentries) < 0) {
2370+ /* get_and_send_entry outputs the response for the client, already */
2371+ mailbox_free(&box);
2372+ return true;
2373+ }
2374+
2375+ entry_name++;
2376+ }
2377+
2378+ mailbox_free(&box);
2379+ }
2380+
2381+ if (mailbox_list_iter_deinit(&ctx) < 0) {
2382+ client_send_tagline(cmd, "NO Iterating mailboxes failed.");
2383+ }
2384+ }
2385+ }
2386+ else {
2387+ struct mailbox *box = NULL;
2388+ /* empty mailbox_name -> box=NULL -> server scope */
2389+ if (*mailbox_name != '\0') {
2390+ struct mail_namespace *ns = mail_namespace_find(cmd->client->user->namespaces, &mailbox_name);
2391+ if (ns == NULL) {
2392+ client_send_tagline(cmd, "NO Mailbox not found.");
2393+ return true;
2394+ }
2395+
2396+ box = mailbox_alloc(ns->list, mailbox_name, MAILBOX_FLAG_READONLY);
2397+ if (box == NULL) {
2398+ client_send_tagline(cmd, "NO Allocating mailbox failed.");
2399+ return true;
2400+ }
2401+ }
2402+
2403+ const char **entry_name = entry_names;
2404+ while (*entry_name != NULL) {
2405+ if (get_and_send_entry(cmd, box, *entry_name, depth, maxsize, &warn_longentries) < 0) {
2406+ /* get_and_send_entry outputs the response for the client, already */
2407+ mailbox_free(&box);
2408+ return true;
2409+ }
2410+
2411+ entry_name++;
2412+ }
2413+
2414+ mailbox_free(&box);
2415+ }
2416+
2417+ free(entry_names);
2418+
2419+ const char *response;
2420+ if (warn_longentries > 0) {
2421+ response = t_strdup_printf("OK [METADATA LONGENTRIES %d] Completed.", warn_longentries);
2422+ }
2423+ else {
2424+ response = "OK Completed.";
2425+ }
2426+ client_send_tagline(cmd, response);
2427+
2428+ return true;
2429+}
2430+
2431+
2432+static bool
2433+cmd_setmetadata(struct client_command_context *cmd) {
2434+ const struct imap_arg *args;
2435+
2436+ if (!client_read_args(cmd, 0, 0, &args))
2437+ return false;
2438+
2439+ if (!IMAP_ARG_TYPE_IS_ASTRING(args[0].type)) { // mailbox name
2440+ client_send_command_error(cmd, "Mailbox name not a string.");
2441+ return true;
2442+ }
2443+
2444+ const char *mailbox_name = NULL;
2445+ if (!imap_arg_get_astring(&args[0], &mailbox_name)){
2446+ client_send_command_error(cmd, "Cannot read mailbox name, string expected.");
2447+ return true;
2448+ }
2449+
2450+ if (mailbox_name == NULL) {
2451+ client_send_tagline(cmd, "NO Mailbox name is NULL.");
2452+ return true;
2453+ }
2454+
2455+ struct mailbox *box = NULL;
2456+ /* empty name -> box=NULL -> server scope */
2457+ if (*mailbox_name != '\0') {
2458+ struct mail_namespace *ns = mail_namespace_find(cmd->client->user->namespaces, &mailbox_name);
2459+ if (ns == NULL) {
2460+ client_send_tagline(cmd, "NO Mailbox not found.");
2461+ return true;
2462+ }
2463+
2464+ box = mailbox_alloc(ns->list, mailbox_name, MAILBOX_FLAG_READONLY);
2465+ if (box == NULL) {
2466+ client_send_tagline(cmd, "NO Allocating mailbox failed.");
2467+ return true;
2468+ }
2469+ }
2470+
2471+ bool warn_maxsize = false, warn_toomany = false, warn_noprivate = false;
2472+
2473+ if (args[1].type == IMAP_ARG_LIST) { // entries
2474+ const struct imap_arg *arglist = NULL;
2475+ if (!imap_arg_get_list(&args[1], &arglist)){
2476+ client_send_command_error(cmd, "Cannot read entries, list expected.");
2477+ mailbox_free(&box);
2478+ return true;
2479+ }
2480+
2481+ while (arglist[0].type != IMAP_ARG_EOL) {
2482+ if (!IMAP_ARG_TYPE_IS_ASTRING(arglist[0].type)) {
2483+ client_send_command_error(cmd, "Entry name not a string.");
2484+ mailbox_free(&box);
2485+ return true;
2486+ }
2487+
2488+ const char *name = NULL;
2489+ if (!imap_arg_get_astring(&arglist[0], &name)){
2490+ client_send_command_error(cmd, "Cannot read entry name, string expected.");
2491+ mailbox_free(&box);
2492+ return true;
2493+ }
2494+
2495+ if (name == NULL) {
2496+ client_send_tagline(cmd, "NO Entry name is NULL.");
2497+ mailbox_free(&box);
2498+ return true;
2499+ }
2500+
2501+ const char *value;
2502+ if (arglist[1].type == IMAP_ARG_NIL) {
2503+ value = NULL;
2504+ }
2505+ else if (IMAP_ARG_TYPE_IS_ASTRING(arglist[1].type)) {
2506+ if (!imap_arg_get_astring(&arglist[1], &value)){
2507+ client_send_command_error(cmd, "Cannot read value, string expected.");
2508+ mailbox_free(&box);
2509+ return true;
2510+ }
2511+ }
2512+ else {
2513+ client_send_command_error(cmd, "Value not nil or string.");
2514+ mailbox_free(&box);
2515+ return true;
2516+ }
2517+
2518+ struct metadata_entry *entry = parse_entry(box, name, value);
2519+ if (entry == NULL) {
2520+ client_send_tagline(cmd, "NO Parsing entry failed.");
2521+ mailbox_free(&box);
2522+ return true;
2523+ }
2524+
2525+ int ret = metadata_set_entry(entry, cmd->client->user);
2526+ if (ret == -2) {
2527+ warn_maxsize = true;
2528+ }
2529+ else if (ret == -3) {
2530+ warn_toomany = true;
2531+ }
2532+ else if (ret < 0) {
2533+ client_send_tagline(cmd, "NO Setting entry failed.");
2534+ mailbox_free(&box);
2535+ return true;
2536+ }
2537+
2538+ /* skip this name/value pair */
2539+ arglist += 2;
2540+ }
2541+ }
2542+ else {
2543+ client_send_command_error(cmd, "Entries not a list.");
2544+ mailbox_free(&box);
2545+ return true;
2546+ }
2547+
2548+ const char *response;
2549+ if (warn_maxsize) {
2550+ struct metadata_mail_user *muser = METADATA_USER_CONTEXT(cmd->client->user);
2551+ if (muser == NULL) {
2552+ i_error("metadata: found NULL user, can't set metadata");
2553+ client_send_tagline(cmd, "NO Internal error.");
2554+ mailbox_free(&box);
2555+ return true;
2556+ }
2557+
2558+ response = t_strdup_printf("OK [METADATA MAXSIZE %d] Completed.", muser->set->maxsize);
2559+ }
2560+ else if (warn_toomany) {
2561+ response = t_strdup_printf("OK [METADATA TOOMANY] Completed.");
2562+ }
2563+ else if (warn_noprivate) {
2564+ response = t_strdup_printf("OK [METADATA NOPRIVATE] Completed.");
2565+ }
2566+ else {
2567+ response = "OK Completed.";
2568+ }
2569+ client_send_tagline(cmd, response);
2570+
2571+ mailbox_free(&box);
2572+
2573+ return true;
2574+}
2575+
2576+
2577+static void imap_metadata_client_created(struct client **client)
2578+{
2579+ if (mail_user_is_plugin_loaded((*client)->user, imap_metadata_module))
2580+ str_append((*client)->capability_string, " METADATA");
2581+
2582+ if (next_hook_client_created != NULL)
2583+ next_hook_client_created(client);
2584+}
2585+
2586+
2587+void imap_metadata_plugin_init(struct module *module)
2588+{
2589+ command_register("GETMETADATA", cmd_getmetadata, 0);
2590+ command_register("SETMETADATA", cmd_setmetadata, 0);
2591+
2592+ imap_metadata_module = module;
2593+ next_hook_client_created = hook_client_created;
2594+ hook_client_created = imap_metadata_client_created;
2595+}
2596+
2597+
2598+void imap_metadata_plugin_deinit(void)
2599+{
2600+ command_unregister("SETMETADATA");
2601+ command_unregister("GETMETADATA");
2602+
2603+ hook_client_created = next_hook_client_created;
2604+}
2605
2606=== added file 'src/mailbox-ext.c'
2607--- src/mailbox-ext.c 1970-01-01 00:00:00 +0000
2608+++ src/mailbox-ext.c 2011-09-29 19:49:50 +0000
2609@@ -0,0 +1,30 @@
2610+/*
2611+ Copyright (c) 2010 by Dennis Schridde
2612+
2613+ This file is part of dovecot-metadata.
2614+
2615+ dovecot-metadata is free software: you can redistribute it and/or modify
2616+ it under the terms of the GNU Lesser General Public License as published by
2617+ the Free Software Foundation, either version 3 of the License, or
2618+ (at your option) any later version.
2619+
2620+ dovecot-metadata is distributed in the hope that it will be useful,
2621+ but WITHOUT ANY WARRANTY; without even the implied warranty of
2622+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2623+ GNU Lesser General Public License for more details.
2624+
2625+ You should have received a copy of the GNU Lesser General Public License
2626+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
2627+*/
2628+#include "mailbox-ext.h"
2629+
2630+#include "hex-binary.h"
2631+
2632+const char *
2633+mailbox_get_guid_string(struct mailbox *box) {
2634+ uint8_t guid[MAIL_GUID_128_SIZE];
2635+ if (mailbox_get_guid(box, guid) < 0)
2636+ return NULL;
2637+
2638+ return binary_to_hex(guid, sizeof(guid));
2639+}
2640
2641=== added file 'src/mailbox-ext.h'
2642--- src/mailbox-ext.h 1970-01-01 00:00:00 +0000
2643+++ src/mailbox-ext.h 2011-09-29 19:49:50 +0000
2644@@ -0,0 +1,30 @@
2645+/*
2646+ Copyright (c) 2010 by Dennis Schridde
2647+
2648+ This file is part of dovecot-metadata.
2649+
2650+ dovecot-metadata is free software: you can redistribute it and/or modify
2651+ it under the terms of the GNU Lesser General Public License as published by
2652+ the Free Software Foundation, either version 3 of the License, or
2653+ (at your option) any later version.
2654+
2655+ dovecot-metadata is distributed in the hope that it will be useful,
2656+ but WITHOUT ANY WARRANTY; without even the implied warranty of
2657+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2658+ GNU Lesser General Public License for more details.
2659+
2660+ You should have received a copy of the GNU Lesser General Public License
2661+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
2662+*/
2663+#ifndef DOVECOT_MAILBOX_EXT_H
2664+#define DOVECOT_MAILBOX_EXT_H
2665+
2666+#include "metadata-global.h"
2667+
2668+#include "mail-storage.h"
2669+
2670+const char *
2671+mailbox_get_guid_string(struct mailbox *box)
2672+ ATTR_NONNULL(1);
2673+
2674+#endif
2675
2676=== added file 'src/metadata-backend.c'
2677--- src/metadata-backend.c 1970-01-01 00:00:00 +0000
2678+++ src/metadata-backend.c 2011-09-29 19:49:50 +0000
2679@@ -0,0 +1,296 @@
2680+/*
2681+ Copyright (c) 2010 by Dennis Schridde
2682+
2683+ This file is part of dovecot-metadata.
2684+
2685+ dovecot-metadata is free software: you can redistribute it and/or modify
2686+ it under the terms of the GNU Lesser General Public License as published by
2687+ the Free Software Foundation, either version 3 of the License, or
2688+ (at your option) any later version.
2689+
2690+ dovecot-metadata is distributed in the hope that it will be useful,
2691+ but WITHOUT ANY WARRANTY; without even the implied warranty of
2692+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2693+ GNU Lesser General Public License for more details.
2694+
2695+ You should have received a copy of the GNU Lesser General Public License
2696+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
2697+*/
2698+#include "metadata-backend.h"
2699+
2700+#include "dict.h"
2701+#include "mail-storage.h"
2702+
2703+#include <string.h>
2704+
2705+#include "str-ext.h"
2706+#include "dict-ext.h"
2707+#include "mailbox-ext.h"
2708+#include "metadata-entry-private.h"
2709+#include "metadata-mail-user-module-private.h"
2710+
2711+static const char *
2712+dict_subjects[ENTRY_SUBJECT_MAX+1] = {
2713+ "server/", /* ENTRY_SUBJECT_SERVER */
2714+ "mailbox/", /* ENTRY_SUBJECT_MAILBOX */
2715+ NULL
2716+};
2717+
2718+static const char*
2719+dictsubject_from_entry(struct metadata_entry *entry) {
2720+ switch (metadata_entry_get_subject(entry)) {
2721+ case ENTRY_SUBJECT_SERVER:
2722+ return dict_subjects[ENTRY_SUBJECT_SERVER];
2723+ case ENTRY_SUBJECT_MAILBOX:
2724+ return t_strconcat(dict_subjects[ENTRY_SUBJECT_MAILBOX], entry->mailbox_guid, "/", NULL);
2725+ case ENTRY_SUBJECT_MAX:
2726+ return NULL;
2727+ }
2728+
2729+ return NULL;
2730+}
2731+
2732+static const char*
2733+t_dictkey_from_entry(struct metadata_entry *entry) {
2734+ const char *subject = dictsubject_from_entry(entry);
2735+ if (subject == NULL)
2736+ return NULL;
2737+
2738+ const char *path_prefix = NULL;
2739+ switch (metadata_entry_get_scope(entry)) {
2740+ case ENTRY_SCOPE_SHARED:
2741+ path_prefix = DICT_PATH_SHARED;
2742+ break;
2743+ case ENTRY_SCOPE_PRIVATE:
2744+ path_prefix = DICT_PATH_PRIVATE;
2745+ break;
2746+ case ENTRY_SCOPE_INVALID:
2747+ case ENTRY_SCOPE_NONE:
2748+ return NULL;
2749+ }
2750+
2751+ // -> "prefix/" "subject/" "name"
2752+ return t_strconcat(path_prefix, subject, &entry->name[1], NULL);
2753+}
2754+
2755+static int
2756+count_entries(struct metadata_mail_user *muser) {
2757+ struct dict_iterate_context *iter;
2758+ const char *key;
2759+ const char *value;
2760+ int num = 0;
2761+
2762+ iter = dict_iterate_init(muser->dict, DICT_PATH_SHARED, DICT_ITERATE_FLAG_RECURSE);
2763+ while (dict_iterate(iter, &key, &value)) {
2764+ num++;
2765+ }
2766+ if (dict_iterate_deinit(&iter) < 0) {
2767+ i_error("metadata: dict iteration failed, can't count shared entries");
2768+ return -1;
2769+ }
2770+ iter = dict_iterate_init(muser->dict, DICT_PATH_PRIVATE, DICT_ITERATE_FLAG_RECURSE);
2771+ while (dict_iterate(iter, &key, &value)) {
2772+ num++;
2773+ }
2774+ if (dict_iterate_deinit(&iter) < 0) {
2775+ i_error("metadata: dict iteration failed, can't count private entries");
2776+ return -1;
2777+ }
2778+
2779+ return num;
2780+}
2781+
2782+int
2783+metadata_set_entry(struct metadata_entry *entry, struct mail_user *user) {
2784+ struct metadata_mail_user *muser = METADATA_USER_CONTEXT(user);
2785+ if (muser == NULL) {
2786+ i_error("metadata: found NULL user, can't set their metadata");
2787+ return -1;
2788+ }
2789+
2790+ if (!metadata_entry_is_valid(entry))
2791+ return -4;
2792+ if (strlen(entry->name) > muser->set->maxsize)
2793+ return -2;
2794+ if (count_entries(muser) > muser->set->maxentries)
2795+ return -3;
2796+
2797+ const char *key = t_dictkey_from_entry(entry);
2798+ if (key == NULL)
2799+ return -1;
2800+
2801+ struct dict_transaction_context *dt = dict_transaction_begin(muser->dict);
2802+
2803+ if (entry->value == NULL)
2804+ dict_unset(dt, key);
2805+ else
2806+ dict_set(dt, key, entry->value);
2807+
2808+ if (dict_transaction_commit(&dt) < 0) {
2809+ i_error("metadata: dict commit failed");
2810+ return -1;
2811+ }
2812+
2813+ return 0;
2814+}
2815+
2816+int
2817+metadata_get_entry(struct metadata_entry *entry, struct mail_user *user) {
2818+ struct metadata_mail_user *muser = METADATA_USER_CONTEXT(user);
2819+ if (muser == NULL) {
2820+ i_error("metadata: found NULL user, can't get their metadata");
2821+ return -1;
2822+ }
2823+
2824+ if (!metadata_entry_is_valid(entry))
2825+ return -4;
2826+
2827+ const char *key = t_dictkey_from_entry(entry);
2828+ if (key == NULL)
2829+ return -1;
2830+
2831+ return dict_lookup(muser->dict, user->pool, key, &entry->value);
2832+}
2833+
2834+struct metadata_iterate_context {
2835+ struct dict_iterate_multiscope_context *dict_ctx;
2836+ int depth;
2837+ bool failed;
2838+};
2839+
2840+struct metadata_iterate_context*
2841+metadata_iterate_init(struct mailbox *mailbox, struct metadata_entry *entry, int depth) {
2842+ struct metadata_iterate_context *ctx = i_new(struct metadata_iterate_context, 1);
2843+ memset(ctx, 0, sizeof(*ctx));
2844+
2845+ struct mail_storage *storage = mailbox_get_storage(mailbox);
2846+ struct mail_user *user = mail_storage_get_user(storage);
2847+ struct metadata_mail_user *muser = METADATA_USER_CONTEXT(user);
2848+ if (muser == NULL) {
2849+ i_error("metadata: found NULL user, can't iterate over their metadata");
2850+ ctx->failed = true;
2851+ return ctx;
2852+ }
2853+
2854+ i_assert(entry != NULL);
2855+ if (entry == NULL) {
2856+ ctx->failed = true;
2857+ return ctx;
2858+ }
2859+
2860+ const char *entry_name = metadata_entry_get_name(entry);
2861+ const int root_depth = strchr_num(entry_name, '/');
2862+ ctx->depth = root_depth + depth;
2863+
2864+ enum dict_iterate_multiscope_flags flags = 0;
2865+ if (depth != 0)
2866+ flags |= DICT_ITERATE_FLAG_RECURSE;
2867+
2868+ switch (metadata_entry_get_scope(entry)) {
2869+ case ENTRY_SCOPE_SHARED:
2870+ case ENTRY_SCOPE_PRIVATE:
2871+ break;
2872+ case ENTRY_SCOPE_INVALID:
2873+ ctx->failed = true;
2874+ return ctx;
2875+ case ENTRY_SCOPE_NONE:
2876+ flags |= DICT_ITERATE_MULTISCOPE_FLAG_MULTISCOPE;
2877+ break;
2878+ }
2879+
2880+ const char *key = t_dictkey_from_entry(entry);
2881+ if (key == NULL) {
2882+ ctx->failed = true;
2883+ return ctx;
2884+ }
2885+
2886+ ctx->dict_ctx = dict_iterate_multiscope_init(muser->dict, key, flags);
2887+ if (ctx->dict_ctx == NULL) {
2888+ ctx->failed = true;
2889+ return ctx;
2890+ }
2891+
2892+ return ctx;
2893+}
2894+
2895+static ATTR_NONNULL(2)
2896+const char *
2897+entry_name_from_dict_name(enum metadata_entry_subject subject, const char *dict_name) {
2898+ /* skip dict internal prefixes: priv/ or shared/ */
2899+ const char *name_after_scope = strchr(dict_name, '/');
2900+ if (name_after_scope == NULL) {
2901+ return NULL;
2902+ }
2903+
2904+ /* skip '/' */
2905+ name_after_scope++;
2906+
2907+ /* skip dict internal prefixes: server/ or mailbox/ */
2908+ const char *name_after_subject = strchr(name_after_scope, '/');
2909+ if (name_after_subject == NULL) {
2910+ return NULL;
2911+ }
2912+
2913+ /* do not skip '/', the name needs to start with a '/'! */
2914+
2915+ /* skip dict internal prefixes: <mailbox_guid>/ (for mailboxes only) */
2916+ if (subject == ENTRY_SUBJECT_MAILBOX) {
2917+ name_after_subject = strchr(name_after_subject+1, '/');
2918+ if (name_after_subject == NULL) {
2919+ return NULL;
2920+ }
2921+ }
2922+
2923+ return name_after_subject;
2924+}
2925+
2926+bool
2927+metadata_iterate(struct metadata_iterate_context *ctx, struct metadata_entry *entry) {
2928+ i_assert(ctx != NULL);
2929+ if (ctx == NULL)
2930+ return false;
2931+
2932+ if (ctx->failed)
2933+ return false;
2934+
2935+ entry->name = NULL;
2936+ while (entry->name == NULL) {
2937+ const char *dict_name = NULL, *dict_value = NULL;
2938+ if (!dict_iterate_multiscope(ctx->dict_ctx, &dict_name, &dict_value))
2939+ return false;
2940+
2941+ const char *entry_name = entry_name_from_dict_name(metadata_entry_get_subject(entry), dict_name);
2942+ if (entry_name == NULL) {
2943+ ctx->failed = true;
2944+ return false;
2945+ }
2946+
2947+ if (ctx->depth != METADATA_ITERATE_DEPTH_INF && strchr_num(entry_name, '/') > ctx->depth)
2948+ continue;
2949+
2950+ entry->name = i_strdup(entry_name);
2951+ entry->value = i_strdup(dict_value);
2952+ }
2953+
2954+ return true;
2955+}
2956+
2957+int
2958+metadata_iterate_deinit(struct metadata_iterate_context **ctx) {
2959+ i_assert(ctx != NULL);
2960+ if (ctx == NULL)
2961+ return -1;
2962+
2963+ i_assert(*ctx != NULL);
2964+ if (*ctx == NULL)
2965+ return -1;
2966+
2967+ int ret = (*ctx)->failed ? -1 : 0;
2968+
2969+ if ((*ctx)->dict_ctx != NULL && dict_iterate_multiscope_deinit(&(*ctx)->dict_ctx) < 0)
2970+ ret = -1;
2971+
2972+ i_free(*ctx);
2973+
2974+ return ret;
2975+}
2976
2977=== added file 'src/metadata-backend.h'
2978--- src/metadata-backend.h 1970-01-01 00:00:00 +0000
2979+++ src/metadata-backend.h 2011-09-29 19:49:50 +0000
2980@@ -0,0 +1,49 @@
2981+/*
2982+ Copyright (c) 2010 by Dennis Schridde
2983+
2984+ This file is part of dovecot-metadata.
2985+
2986+ dovecot-metadata is free software: you can redistribute it and/or modify
2987+ it under the terms of the GNU Lesser General Public License as published by
2988+ the Free Software Foundation, either version 3 of the License, or
2989+ (at your option) any later version.
2990+
2991+ dovecot-metadata is distributed in the hope that it will be useful,
2992+ but WITHOUT ANY WARRANTY; without even the implied warranty of
2993+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2994+ GNU Lesser General Public License for more details.
2995+
2996+ You should have received a copy of the GNU Lesser General Public License
2997+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
2998+*/
2999+#ifndef DOVECOT_METADATA_BACKEND_H
3000+#define DOVECOT_METADATA_BACKEND_H
3001+
3002+#include "metadata-global.h"
3003+
3004+#include <stdbool.h>
3005+
3006+#include "metadata-entry.h"
3007+
3008+int
3009+metadata_get_entry(struct metadata_entry *entry, struct mail_user *user)
3010+ ATTR_NONNULL(1,2);
3011+int
3012+metadata_set_entry(struct metadata_entry *entry, struct mail_user *user)
3013+ ATTR_NONNULL(1,2);
3014+
3015+const int METADATA_ITERATE_DEPTH_INF = INT_MAX;
3016+
3017+struct metadata_iterate_context;
3018+
3019+struct metadata_iterate_context *
3020+metadata_iterate_init(struct mailbox *mailbox, struct metadata_entry *entry, int depth)
3021+ ATTR_NONNULL(1,2);
3022+bool
3023+metadata_iterate(struct metadata_iterate_context *ctx, struct metadata_entry *entry)
3024+ ATTR_NONNULL(1,2);
3025+int
3026+metadata_iterate_deinit(struct metadata_iterate_context **ctx)
3027+ ATTR_NONNULL(1);
3028+
3029+#endif
3030
3031=== added file 'src/metadata-entry-private.h'
3032--- src/metadata-entry-private.h 1970-01-01 00:00:00 +0000
3033+++ src/metadata-entry-private.h 2011-09-29 19:49:50 +0000
3034@@ -0,0 +1,32 @@
3035+/*
3036+ Copyright (c) 2010 by Dennis Schridde
3037+
3038+ This file is part of dovecot-metadata.
3039+
3040+ dovecot-metadata is free software: you can redistribute it and/or modify
3041+ it under the terms of the GNU Lesser General Public License as published by
3042+ the Free Software Foundation, either version 3 of the License, or
3043+ (at your option) any later version.
3044+
3045+ dovecot-metadata is distributed in the hope that it will be useful,
3046+ but WITHOUT ANY WARRANTY; without even the implied warranty of
3047+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3048+ GNU Lesser General Public License for more details.
3049+
3050+ You should have received a copy of the GNU Lesser General Public License
3051+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
3052+*/
3053+#ifndef DOVECOT_METADATA_ENTRY_PRIVATE_H
3054+#define DOVECOT_METADATA_ENTRY_PRIVATE_H
3055+
3056+#include "metadata-entry.h"
3057+
3058+struct metadata_entry {
3059+ enum metadata_entry_scope scope;
3060+ enum metadata_entry_type type;
3061+ const char *mailbox_guid; // implicitly defines the subject!
3062+ const char *name;
3063+ const char *value;
3064+};
3065+
3066+#endif
3067
3068=== added file 'src/metadata-entry.c'
3069--- src/metadata-entry.c 1970-01-01 00:00:00 +0000
3070+++ src/metadata-entry.c 2011-09-29 19:49:50 +0000
3071@@ -0,0 +1,173 @@
3072+/*
3073+ Copyright (c) 2010 by Dennis Schridde
3074+
3075+ This file is part of dovecot-metadata.
3076+
3077+ dovecot-metadata is free software: you can redistribute it and/or modify
3078+ it under the terms of the GNU Lesser General Public License as published by
3079+ the Free Software Foundation, either version 3 of the License, or
3080+ (at your option) any later version.
3081+
3082+ dovecot-metadata is distributed in the hope that it will be useful,
3083+ but WITHOUT ANY WARRANTY; without even the implied warranty of
3084+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3085+ GNU Lesser General Public License for more details.
3086+
3087+ You should have received a copy of the GNU Lesser General Public License
3088+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
3089+*/
3090+#include "metadata-entry-private.h"
3091+
3092+#include <stdlib.h>
3093+#include <string.h>
3094+
3095+
3096+#include "mailbox-ext.h"
3097+
3098+static
3099+const char *
3100+entry_scopes[ENTRY_SCOPE_MAX+1] = {
3101+ "private/", /* ENTRY_SCOPE_PRIVATE */
3102+ "shared/", /* ENTRY_SCOPE_SHARED */
3103+ NULL
3104+};
3105+
3106+static
3107+enum metadata_entry_scope
3108+parse_scope(const char *name) {
3109+ if (name == NULL)
3110+ return ENTRY_SCOPE_INVALID;
3111+
3112+ /* scope must be empty or begin with '/' */
3113+ if (name[0] != '/') {
3114+ if (name[0] == '\0')
3115+ return ENTRY_SCOPE_NONE;
3116+
3117+ return ENTRY_SCOPE_INVALID;
3118+ }
3119+
3120+ /* skip '/' */
3121+ name++;
3122+
3123+ /* scope is the first component */
3124+ for (int i = 0; i < ENTRY_SCOPE_MAX; i++) {
3125+ if (strncasecmp(entry_scopes[i], name, strlen(entry_scopes[i])) == 0)
3126+ return i;
3127+ }
3128+
3129+ return ENTRY_SCOPE_INVALID;
3130+}
3131+
3132+static
3133+const char *
3134+entry_types[ENTRY_TYPE_MAX+1] = {
3135+ "vendor/", /* ENTRY_TYPE_VENDOR */
3136+ "", /* ENTRY_TYPE_RFC */
3137+ NULL
3138+};
3139+
3140+static
3141+enum metadata_entry_type
3142+parse_type(const char *name) {
3143+ /* lazy evaluation of scope existance */
3144+ if (name == NULL || *name++ != '/')
3145+ return ENTRY_TYPE_INVALID;
3146+
3147+ /* type is the second component */
3148+ name = strchr(name, '/');
3149+ if (name++ == NULL)
3150+ return ENTRY_TYPE_NONE;
3151+
3152+ for (int i = 0; i < ENTRY_TYPE_MAX; i++) {
3153+ if (strncasecmp(entry_types[i], name, strlen(entry_types[i])) == 0)
3154+ return i;
3155+ }
3156+
3157+ return ENTRY_TYPE_INVALID;
3158+}
3159+
3160+/* create entry on mailbox with name=value */
3161+struct metadata_entry *
3162+metadata_entry_alloc(struct mailbox *mailbox, const char *name, const char *value) {
3163+ struct metadata_entry *entry = i_new(struct metadata_entry, 1);
3164+ memset(entry, 0, sizeof(*entry));
3165+
3166+ if (mailbox != NULL) {
3167+ const char *mailbox_guid = mailbox_get_guid_string(mailbox);
3168+ if (mailbox_guid != NULL)
3169+ entry->mailbox_guid = strdup(mailbox_guid);
3170+ }
3171+
3172+ entry->scope = parse_scope(name);
3173+ entry->type = parse_type(name);
3174+ if (metadata_entry_is_valid(entry)) {
3175+ if (name != NULL)
3176+ entry->name = strdup(name);
3177+ if (value != NULL)
3178+ entry->value = strdup(value);
3179+ }
3180+
3181+ return entry;
3182+}
3183+
3184+/* free structures allocated for entry and invalidate it */
3185+void
3186+entry_free(struct metadata_entry *entry) {
3187+ free((char*)entry->value);
3188+ free((char*)entry->name);
3189+ memset(entry, 0, sizeof(*entry));
3190+}
3191+
3192+bool
3193+metadata_entry_is_valid(struct metadata_entry *entry) {
3194+ i_assert(entry != NULL);
3195+ if (entry == NULL)
3196+ return false;
3197+
3198+ return entry->scope < ENTRY_SCOPE_MAX && entry->type < ENTRY_TYPE_MAX;
3199+}
3200+
3201+const char *
3202+metadata_entry_get_name(struct metadata_entry *entry) {
3203+ i_assert(entry != NULL);
3204+ if (entry == NULL)
3205+ return NULL;
3206+
3207+ return entry->name;
3208+}
3209+
3210+const char *
3211+metadata_entry_get_value(struct metadata_entry *entry) {
3212+ i_assert(entry != NULL);
3213+ if (entry == NULL)
3214+ return NULL;
3215+
3216+ return entry->value;
3217+}
3218+
3219+enum metadata_entry_subject
3220+metadata_entry_get_subject(struct metadata_entry *entry) {
3221+ i_assert(entry != NULL);
3222+ if (entry == NULL)
3223+ return ENTRY_SUBJECT_INVALID;
3224+
3225+ return entry->mailbox_guid ? ENTRY_SUBJECT_MAILBOX : ENTRY_SUBJECT_SERVER;
3226+}
3227+
3228+enum metadata_entry_scope
3229+metadata_entry_get_scope(struct metadata_entry *entry) {
3230+ i_assert(entry != NULL);
3231+ if (entry == NULL)
3232+ return ENTRY_SCOPE_INVALID;
3233+
3234+ return entry->scope;
3235+}
3236+
3237+enum metadata_entry_type
3238+metadata_entry_get_type(struct metadata_entry *entry) {
3239+ i_assert(entry != NULL);
3240+ if (entry == NULL)
3241+ return ENTRY_TYPE_INVALID;
3242+
3243+ return entry->type;
3244+}
3245
3246=== added file 'src/metadata-entry.h'
3247--- src/metadata-entry.h 1970-01-01 00:00:00 +0000
3248+++ src/metadata-entry.h 2011-09-29 19:49:50 +0000
3249@@ -0,0 +1,83 @@
3250+/*
3251+ Copyright (c) 2010 by Dennis Schridde
3252+
3253+ This file is part of dovecot-metadata.
3254+
3255+ dovecot-metadata is free software: you can redistribute it and/or modify
3256+ it under the terms of the GNU Lesser General Public License as published by
3257+ the Free Software Foundation, either version 3 of the License, or
3258+ (at your option) any later version.
3259+
3260+ dovecot-metadata is distributed in the hope that it will be useful,
3261+ but WITHOUT ANY WARRANTY; without even the implied warranty of
3262+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3263+ GNU Lesser General Public License for more details.
3264+
3265+ You should have received a copy of the GNU Lesser General Public License
3266+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
3267+*/
3268+#ifndef DOVECOT_METADATA_ENTRY_H
3269+#define DOVECOT_METADATA_ENTRY_H
3270+
3271+#include "metadata-global.h"
3272+
3273+#include <stdbool.h>
3274+
3275+#include "mail-storage.h"
3276+
3277+enum metadata_entry_subject {
3278+ ENTRY_SUBJECT_SERVER = 0,
3279+ ENTRY_SUBJECT_MAILBOX = 1,
3280+ ENTRY_SUBJECT_MAX,
3281+ ENTRY_SUBJECT_INVALID = ENTRY_SUBJECT_MAX
3282+};
3283+
3284+enum metadata_entry_scope {
3285+ ENTRY_SCOPE_PRIVATE = 0,
3286+ ENTRY_SCOPE_SHARED = 1,
3287+ ENTRY_SCOPE_MAX,
3288+ ENTRY_SCOPE_INVALID = ENTRY_SCOPE_MAX,
3289+ ENTRY_SCOPE_NONE
3290+};
3291+
3292+enum metadata_entry_type {
3293+ ENTRY_TYPE_VENDOR = 0,
3294+ ENTRY_TYPE_RFC = 1,
3295+ ENTRY_TYPE_MAX,
3296+ ENTRY_TYPE_INVALID = ENTRY_TYPE_MAX,
3297+ ENTRY_TYPE_NONE
3298+};
3299+
3300+struct metadata_entry *
3301+metadata_entry_alloc(struct mailbox *mailbox, const char *name, const char *value)
3302+ ATTR_NONNULL(2);
3303+
3304+void
3305+metadata_entry_free(struct metadata_entry *entry)
3306+ ATTR_NONNULL(1);
3307+
3308+bool
3309+metadata_entry_is_valid(struct metadata_entry *entry)
3310+ ATTR_NONNULL(1);
3311+
3312+const char *
3313+metadata_entry_get_name(struct metadata_entry *entry)
3314+ ATTR_NONNULL(1);
3315+
3316+const char *
3317+metadata_entry_get_value(struct metadata_entry *entry)
3318+ ATTR_NONNULL(1);
3319+
3320+enum metadata_entry_subject
3321+metadata_entry_get_subject(struct metadata_entry *entry)
3322+ ATTR_NONNULL(1);
3323+
3324+enum metadata_entry_scope
3325+metadata_entry_get_scope(struct metadata_entry *entry)
3326+ ATTR_NONNULL(1);
3327+
3328+enum metadata_entry_type
3329+metadata_entry_get_type(struct metadata_entry *entry)
3330+ ATTR_NONNULL(1);
3331+
3332+#endif
3333
3334=== added file 'src/metadata-global.h'
3335--- src/metadata-global.h 1970-01-01 00:00:00 +0000
3336+++ src/metadata-global.h 2011-09-29 19:49:50 +0000
3337@@ -0,0 +1,52 @@
3338+/*
3339+ Copyright (c) 2010 by Dennis Schridde
3340+
3341+ This file is part of dovecot-metadata.
3342+
3343+ dovecot-metadata is free software: you can redistribute it and/or modify
3344+ it under the terms of the GNU Lesser General Public License as published by
3345+ the Free Software Foundation, either version 3 of the License, or
3346+ (at your option) any later version.
3347+
3348+ dovecot-metadata is distributed in the hope that it will be useful,
3349+ but WITHOUT ANY WARRANTY; without even the implied warranty of
3350+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3351+ GNU Lesser General Public License for more details.
3352+
3353+ You should have received a copy of the GNU Lesser General Public License
3354+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
3355+*/
3356+#ifndef DOVECOT_METADATA_GLOBAL_H
3357+#define DOVECOT_METADATA_GLOBAL_H
3358+
3359+/*
3360+ the dovecot include system needs its own special care:
3361+ * include lib.h first, always
3362+ * reset all the symbols we use by including metadata-config.h.in,
3363+ since dovecot's internal config.h leaks through their public headers
3364+*/
3365+#include "lib.h"
3366+
3367+#define ATTR_NONNULL(...) __attribute__((__nonnull__(__VA_ARGS__)))
3368+
3369+/*
3370+ = error values =
3371+ < 0 is always an error
3372+ 0 means success or no-more-data
3373+ > 0 means more-data-available
3374+
3375+ = return types =
3376+ void can never have errors
3377+
3378+ == logical functions ==
3379+ return bool and cannot have errors
3380+
3381+ == functions returning pointers ==
3382+ NULL is an error, everything else not
3383+
3384+ == data handling functions ==
3385+ bool is a continous function which cannot have errors
3386+ int is any function which can have errors
3387+*/
3388+
3389+#endif
3390
3391=== added file 'src/metadata-mail-storage-module.c'
3392--- src/metadata-mail-storage-module.c 1970-01-01 00:00:00 +0000
3393+++ src/metadata-mail-storage-module.c 2011-09-29 19:49:50 +0000
3394@@ -0,0 +1,92 @@
3395+/*
3396+ Copyright (c) 2010 by Dennis Schridde
3397+
3398+ This file is part of dovecot-metadata.
3399+
3400+ dovecot-metadata is free software: you can redistribute it and/or modify
3401+ it under the terms of the GNU Lesser General Public License as published by
3402+ the Free Software Foundation, either version 3 of the License, or
3403+ (at your option) any later version.
3404+
3405+ dovecot-metadata is distributed in the hope that it will be useful,
3406+ but WITHOUT ANY WARRANTY; without even the implied warranty of
3407+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3408+ GNU Lesser General Public License for more details.
3409+
3410+ You should have received a copy of the GNU Lesser General Public License
3411+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
3412+*/
3413+#include "metadata-mail-storage-module.h"
3414+
3415+#include "dict.h"
3416+#include "module-context.h"
3417+#include "mail-storage-private.h"
3418+
3419+#include "mailbox-ext.h"
3420+#include "metadata-mail-user-module-private.h"
3421+
3422+#define METADATA_MAILBOX_CONTEXT(obj) MODULE_CONTEXT(obj, metadata_mailbox_module)
3423+
3424+static MODULE_CONTEXT_DEFINE_INIT(metadata_mailbox_module, &mail_storage_module_register);
3425+
3426+static int
3427+metadata_mailbox_delete(struct mailbox *box) {
3428+ union mailbox_module_context *mbox = METADATA_MAILBOX_CONTEXT(box);
3429+ if (mbox == NULL) {
3430+ i_error("metadata: found NULL mailbox, can't delete it");
3431+ return -1;
3432+ }
3433+
3434+ struct mail_storage *storage = mailbox_get_storage(box);
3435+ struct mail_user *user = mail_storage_get_user(storage);
3436+ struct metadata_mail_user *muser = METADATA_USER_CONTEXT(user);
3437+ if (muser == NULL) {
3438+ i_error("metadata: found NULL user, can't delete mailbox");
3439+ return -1;
3440+ }
3441+
3442+ struct dict_transaction_context *dt = dict_transaction_begin(muser->dict);
3443+
3444+ const char *name;
3445+ const char *value;
3446+
3447+ const char *skey = t_strconcat(DICT_PATH_SHARED, mailbox_get_guid_string(box), NULL);
3448+
3449+ struct dict_iterate_context *siter = dict_iterate_init(muser->dict, skey, DICT_ITERATE_FLAG_RECURSE);
3450+ while (dict_iterate(siter, &name, &value)) {
3451+ dict_unset(dt, name);
3452+ }
3453+ if (dict_iterate_deinit(&siter) < 0) {
3454+ i_error("metadata: dict iteration (" DICT_PATH_SHARED ") failed, can't update dict");
3455+ return -1;
3456+ }
3457+
3458+ const char *pkey = t_strconcat(DICT_PATH_PRIVATE, mailbox_get_guid_string(box), NULL);
3459+
3460+ struct dict_iterate_context *piter = dict_iterate_init(muser->dict, pkey, DICT_ITERATE_FLAG_RECURSE);
3461+ while (dict_iterate(piter, &name, &value)) {
3462+ dict_unset(dt, name);
3463+ }
3464+ if (dict_iterate_deinit(&piter) < 0) {
3465+ i_error("metadata: dict iteration (" DICT_PATH_PRIVATE ") failed, can't update dict");
3466+ return -1;
3467+ }
3468+
3469+ int super_ret = mbox->super.delete(box);
3470+ if (super_ret < 0) {
3471+ dict_transaction_rollback(&dt);
3472+ } else if (dict_transaction_commit(&dt) < 0) {
3473+ i_error("metadata: dict commit failed");
3474+ return -1;
3475+ }
3476+
3477+ return super_ret;
3478+}
3479+
3480+void metadata_mailbox_allocated(struct mailbox *box) {
3481+ union mailbox_module_context *mbox = p_new(box->pool, union mailbox_module_context, 1);
3482+ mbox->super = box->v;
3483+ box->v.delete = metadata_mailbox_delete;
3484+
3485+ MODULE_CONTEXT_SET_SELF(box, metadata_mailbox_module, mbox);
3486+}
3487
3488=== added file 'src/metadata-mail-storage-module.h'
3489--- src/metadata-mail-storage-module.h 1970-01-01 00:00:00 +0000
3490+++ src/metadata-mail-storage-module.h 2011-09-29 19:49:50 +0000
3491@@ -0,0 +1,28 @@
3492+/*
3493+ Copyright (c) 2010 by Dennis Schridde
3494+
3495+ This file is part of dovecot-metadata.
3496+
3497+ dovecot-metadata is free software: you can redistribute it and/or modify
3498+ it under the terms of the GNU Lesser General Public License as published by
3499+ the Free Software Foundation, either version 3 of the License, or
3500+ (at your option) any later version.
3501+
3502+ dovecot-metadata is distributed in the hope that it will be useful,
3503+ but WITHOUT ANY WARRANTY; without even the implied warranty of
3504+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3505+ GNU Lesser General Public License for more details.
3506+
3507+ You should have received a copy of the GNU Lesser General Public License
3508+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
3509+*/
3510+#ifndef DOVECOT_METADATA_MAIL_STORAGE_MODULE_H
3511+#define DOVECOT_METADATA_MAIL_STORAGE_MODULE_H
3512+
3513+#include "metadata-global.h"
3514+
3515+#include "mail-storage.h"
3516+
3517+void metadata_mailbox_allocated(struct mailbox *box);
3518+
3519+#endif
3520
3521=== added file 'src/metadata-mail-user-module-private.h'
3522--- src/metadata-mail-user-module-private.h 1970-01-01 00:00:00 +0000
3523+++ src/metadata-mail-user-module-private.h 2011-09-29 19:49:50 +0000
3524@@ -0,0 +1,40 @@
3525+/*
3526+ Copyright (c) 2010 by Dennis Schridde
3527+
3528+ This file is part of dovecot-metadata.
3529+
3530+ dovecot-metadata is free software: you can redistribute it and/or modify
3531+ it under the terms of the GNU Lesser General Public License as published by
3532+ the Free Software Foundation, either version 3 of the License, or
3533+ (at your option) any later version.
3534+
3535+ dovecot-metadata is distributed in the hope that it will be useful,
3536+ but WITHOUT ANY WARRANTY; without even the implied warranty of
3537+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3538+ GNU Lesser General Public License for more details.
3539+
3540+ You should have received a copy of the GNU Lesser General Public License
3541+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
3542+*/
3543+#ifndef DOVECOT_METADATA_MAIL_USER_MODULE_PRIVATE_H
3544+#define DOVECOT_METADATA_MAIL_USER_MODULE_PRIVATE_H
3545+
3546+#include "metadata-mail-user-module.h"
3547+
3548+#include "dict.h"
3549+#include "module-context.h"
3550+#include "mail-user.h"
3551+
3552+#include "metadata-settings.h"
3553+
3554+#define METADATA_USER_CONTEXT(obj) MODULE_CONTEXT(obj, metadata_mail_user_module)
3555+
3556+extern MODULE_CONTEXT_DEFINE(metadata_mail_user_module, &mail_user_module_register);
3557+
3558+struct metadata_mail_user {
3559+ union mail_user_module_context module_ctx;
3560+ struct dict *dict;
3561+ struct metadata_settings *set;
3562+};
3563+
3564+#endif
3565
3566=== added file 'src/metadata-mail-user-module.c'
3567--- src/metadata-mail-user-module.c 1970-01-01 00:00:00 +0000
3568+++ src/metadata-mail-user-module.c 2011-09-29 19:49:50 +0000
3569@@ -0,0 +1,56 @@
3570+/*
3571+ Copyright (c) 2010 by Dennis Schridde
3572+
3573+ This file is part of dovecot-metadata.
3574+
3575+ dovecot-metadata is free software: you can redistribute it and/or modify
3576+ it under the terms of the GNU Lesser General Public License as published by
3577+ the Free Software Foundation, either version 3 of the License, or
3578+ (at your option) any later version.
3579+
3580+ dovecot-metadata is distributed in the hope that it will be useful,
3581+ but WITHOUT ANY WARRANTY; without even the implied warranty of
3582+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3583+ GNU Lesser General Public License for more details.
3584+
3585+ You should have received a copy of the GNU Lesser General Public License
3586+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
3587+*/
3588+#include "metadata-mail-user-module-private.h"
3589+
3590+struct metadata_mail_user_module metadata_mail_user_module =
3591+ MODULE_CONTEXT_INIT(&mail_user_module_register);
3592+
3593+static void metadata_mail_user_deinit(struct mail_user *user) {
3594+ struct metadata_mail_user *muser = METADATA_USER_CONTEXT(user);
3595+ if (muser == NULL) {
3596+ i_error("metadata: found NULL user, can't deinit it");
3597+ return;
3598+ }
3599+
3600+ if (muser->dict != NULL) {
3601+ dict_deinit(&muser->dict);
3602+ }
3603+
3604+ if (muser->set != NULL) {
3605+ metadata_settings_deinit(&muser->set);
3606+ }
3607+
3608+ return muser->module_ctx.super.deinit(user);
3609+}
3610+
3611+void metadata_mail_user_created(struct mail_user *user) {
3612+ struct metadata_mail_user *muser = p_new(user->pool, struct metadata_mail_user, 1);
3613+ muser->module_ctx.super = user->v;
3614+ user->v.deinit = metadata_mail_user_deinit;
3615+
3616+ metadata_settings_init(&muser->set, user);
3617+
3618+ if (muser->set->dict_uri != NULL) {
3619+ muser->dict = dict_init(muser->set->dict_uri, DICT_DATA_TYPE_STRING, user->username, user->set->base_dir);
3620+ if (muser->dict == NULL)
3621+ i_error("metadata: dict_init(%s) failed", muser->set->dict_uri);
3622+ }
3623+
3624+ MODULE_CONTEXT_SET(user, metadata_mail_user_module, muser);
3625+}
3626
3627=== added file 'src/metadata-mail-user-module.h'
3628--- src/metadata-mail-user-module.h 1970-01-01 00:00:00 +0000
3629+++ src/metadata-mail-user-module.h 2011-09-29 19:49:50 +0000
3630@@ -0,0 +1,28 @@
3631+/*
3632+ Copyright (c) 2010 by Dennis Schridde
3633+
3634+ This file is part of dovecot-metadata.
3635+
3636+ dovecot-metadata is free software: you can redistribute it and/or modify
3637+ it under the terms of the GNU Lesser General Public License as published by
3638+ the Free Software Foundation, either version 3 of the License, or
3639+ (at your option) any later version.
3640+
3641+ dovecot-metadata is distributed in the hope that it will be useful,
3642+ but WITHOUT ANY WARRANTY; without even the implied warranty of
3643+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3644+ GNU Lesser General Public License for more details.
3645+
3646+ You should have received a copy of the GNU Lesser General Public License
3647+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
3648+*/
3649+#ifndef DOVECOT_METADATA_MAIL_USER_MODULE_H
3650+#define DOVECOT_METADATA_MAIL_USER_MODULE_H
3651+
3652+#include "metadata-global.h"
3653+
3654+#include "mail-user.h"
3655+
3656+void metadata_mail_user_created(struct mail_user *user);
3657+
3658+#endif
3659
3660=== modified file 'src/metadata-plugin.c'
3661--- src/metadata-plugin.c 2010-08-11 17:11:01 +0000
3662+++ src/metadata-plugin.c 2011-09-29 19:49:50 +0000
3663@@ -1,352 +1,41 @@
3664-/* Copyright (C) 2008, 2009 by Intevation GmbH
3665- * Authors:
3666- * Bernhard Herzog <bh@intevation.de>
3667- *
3668- * This program is free software under the LGPL (>=v2.1)
3669- * Read the file COPYING coming with the software for details.
3670- */
3671-
3672-#include "lib.h"
3673-#include "dict.h"
3674-#include "common.h"
3675-#include "commands-util.h"
3676-#include "module-context.h"
3677-#include "mailbox-list-private.h"
3678-
3679-#include "metadata-plugin.h"
3680-
3681-#include <stdlib.h>
3682-
3683-
3684-#define METADATA_LIST_CONTEXT(obj) \
3685- MODULE_CONTEXT((obj), metadata_mailbox_list_module)
3686-static MODULE_CONTEXT_DEFINE_INIT(metadata_mailbox_list_module,
3687- &mailbox_list_module_register);
3688-
3689-struct metadata_mailbox_list {
3690- union mailbox_list_module_context module_ctx;
3691+/*
3692+ Copyright (c) 2010 by Dennis Schridde
3693+
3694+ This file is part of dovecot-metadata.
3695+
3696+ dovecot-metadata is free software: you can redistribute it and/or modify
3697+ it under the terms of the GNU Lesser General Public License as published by
3698+ the Free Software Foundation, either version 3 of the License, or
3699+ (at your option) any later version.
3700+
3701+ dovecot-metadata is distributed in the hope that it will be useful,
3702+ but WITHOUT ANY WARRANTY; without even the implied warranty of
3703+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3704+ GNU Lesser General Public License for more details.
3705+
3706+ You should have received a copy of the GNU Lesser General Public License
3707+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
3708+*/
3709+#include "metadata-global.h"
3710+
3711+#include "mail-storage-hooks.h"
3712+
3713+#include "metadata-mail-user-module.h"
3714+#include "metadata-mail-storage-module.h"
3715+
3716+const char *metadata_plugin_version = DOVECOT_VERSION;
3717+
3718+static struct mail_storage_hooks metadata_mail_storage_hooks = {
3719+ .mail_user_created = metadata_mail_user_created,
3720+ .mailbox_allocated = metadata_mailbox_allocated
3721 };
3722
3723-static void (*metadata_next_hook_mailbox_list_created)(struct mailbox_list *);
3724-
3725-
3726-static struct dict *metadata_dict;
3727-static bool metadata_allow_private = FALSE;
3728-static bool metadata_debug = FALSE;
3729-
3730-
3731-bool metadata_private_allowed(void) {
3732- return metadata_allow_private;
3733-}
3734-
3735-
3736-static const char *mailbox_key_path(struct mailbox_list *list,
3737- const char *mailboxname)
3738-{
3739- return mailbox_list_get_path(list, mailboxname,
3740- MAILBOX_LIST_PATH_TYPE_MAILBOX);
3741-}
3742-
3743-
3744-static const char *create_dict_key(struct mail_storage *storage,
3745- const char *mailboxname, const char *entry,
3746- bool private)
3747-{
3748- return t_strconcat(private ? DICT_PATH_PRIVATE : DICT_PATH_SHARED,
3749- mailbox_key_path(mail_storage_get_list(storage),
3750- mailboxname),
3751- "/", entry, NULL);
3752-}
3753-
3754-static const char *renamed_key(const char *oldkey, const char *oldpath,
3755- const char *newpath)
3756-{
3757- const char *first_slash;
3758- size_t oldlen;
3759-
3760- first_slash = strchr(oldkey, '/');
3761- if (!first_slash)
3762- return NULL;
3763-
3764- oldlen = strlen(oldpath);
3765- if (strncmp(first_slash + 1, oldpath, oldlen) == 0) {
3766- return t_strconcat(t_strdup_until(oldkey, first_slash + 1),
3767- newpath,
3768- first_slash + 1 + oldlen,
3769- NULL);
3770- }
3771-
3772- return NULL;
3773-}
3774-
3775-static void rename_metadata_of_mailbox(struct dict_transaction_context *dt,
3776- struct mailbox_list *list,
3777- const char *oldname,
3778- const char *newname)
3779-{
3780- struct dict_iterate_context *iter;
3781- const char *oldpath;
3782- const char *newpath;
3783- const char *key;
3784- const char *value;
3785- const char *newkey;
3786-
3787- oldpath = mailbox_key_path(list, oldname);
3788- newpath = mailbox_key_path(list, newname);
3789-
3790- iter = dict_iterate_init(metadata_dict,
3791- t_strconcat(DICT_PATH_SHARED, oldpath, NULL),
3792- DICT_ITERATE_FLAG_RECURSE
3793- | DICT_ITERATE_FLAG_SORT_BY_KEY);
3794- while (dict_iterate(iter, &key, &value) > 0) {
3795- T_BEGIN {
3796- newkey = renamed_key(key, oldpath, newpath);
3797- dict_set(dt, newkey, value);
3798- dict_unset(dt, key);
3799- } T_END;
3800- }
3801- dict_iterate_deinit(&iter);
3802-}
3803-
3804-static int rename_metadata_of_children(struct dict_transaction_context *dt,
3805- struct mailbox_list *list,
3806- const char *oldname,
3807- const char *newname)
3808-{
3809- struct mailbox_list_iterate_context *iter;
3810- const struct mailbox_info *info;
3811- const char *pattern;
3812- size_t oldnamelen;
3813- int ret;
3814-
3815- ret = 0;
3816-
3817- oldnamelen = strlen(oldname);
3818- pattern = t_strdup_printf("%s%c*", oldname,
3819- mailbox_list_get_hierarchy_sep(list));
3820- iter = mailbox_list_iter_init(list, pattern,
3821- MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
3822- while ((info = mailbox_list_iter_next(iter)) != NULL) {
3823- const char *renamed;
3824-
3825- /* verify that the prefix matches, otherwise we could have
3826- problems with mailbox names containing '%' and '*' chars */
3827- if (strncmp(info->name, oldname, oldnamelen) == 0 &&
3828- info->name[oldnamelen] ==
3829- mailbox_list_get_hierarchy_sep(list)) {
3830- T_BEGIN {
3831- renamed = t_strconcat(newname,
3832- info->name + oldnamelen,
3833- NULL);
3834- rename_metadata_of_mailbox(dt, list,
3835- info->name, renamed);
3836- } T_END;
3837- }
3838- }
3839-
3840- if (mailbox_list_iter_deinit(&iter) < 0) {
3841- ret = -1;
3842- }
3843-
3844- return ret;
3845-}
3846-
3847-static int metadata_mailbox_list_rename(struct mailbox_list *list,
3848- const char *oldname,
3849- const char *newname)
3850-{
3851- struct metadata_mailbox_list *mlist = METADATA_LIST_CONTEXT(list);
3852- struct dict_transaction_context *dt;
3853- int success;
3854-
3855- dt = dict_transaction_begin(metadata_dict);
3856-
3857- T_BEGIN {
3858- rename_metadata_of_mailbox(dt, list, oldname, newname);
3859- rename_metadata_of_children(dt, list, oldname, newname);
3860- } T_END;
3861-
3862-
3863- success = mlist->module_ctx.super.rename_mailbox(list, oldname,
3864- newname);
3865- if (success < 0) {
3866- dict_transaction_rollback(&dt);
3867- } else {
3868- if (dict_transaction_commit(&dt) < 0) {
3869- i_error("metadata_mailbox_list_rename:"
3870- " could not update metadata dict");
3871- }
3872- }
3873-
3874- return success;
3875-}
3876-
3877-static int metadata_mailbox_list_delete(struct mailbox_list *list,
3878- const char *name)
3879-{
3880- struct metadata_mailbox_list *mlist = METADATA_LIST_CONTEXT(list);
3881- struct dict_transaction_context *dt;
3882- struct dict_iterate_context *iter;
3883- const char *keypath;
3884- const char *key;
3885- const char *value;
3886- int success;
3887-
3888- keypath = mailbox_key_path(list, name);
3889-
3890- dt = dict_transaction_begin(metadata_dict);
3891- iter = dict_iterate_init(metadata_dict, t_strconcat(DICT_PATH_SHARED,
3892- keypath, NULL),
3893- DICT_ITERATE_FLAG_RECURSE
3894- | DICT_ITERATE_FLAG_SORT_BY_KEY);
3895- while (dict_iterate(iter, &key, &value) > 0) {
3896- dict_unset(dt, key);
3897- }
3898- dict_iterate_deinit(&iter);
3899-
3900- success = mlist->module_ctx.super.delete_mailbox(list, name);
3901- if (success < 0) {
3902- dict_transaction_rollback(&dt);
3903- } else {
3904- if (dict_transaction_commit(&dt) < 0) {
3905- i_error("metadata_mailbox_list_delete:"
3906- " could not update metadata dict");
3907- }
3908- }
3909-
3910- return success;
3911-}
3912-
3913-
3914-static void metadata_mailbox_list_created(struct mailbox_list *list)
3915-{
3916- struct metadata_mailbox_list *mlist;
3917-
3918- mlist = p_new(list->pool, struct metadata_mailbox_list, 1);
3919- mlist->module_ctx.super = list->v;
3920- list->v.rename_mailbox = metadata_mailbox_list_rename;
3921- list->v.delete_mailbox = metadata_mailbox_list_delete;
3922- MODULE_CONTEXT_SET(list, metadata_mailbox_list_module, mlist);
3923-
3924- if (metadata_next_hook_mailbox_list_created != NULL)
3925- metadata_next_hook_mailbox_list_created(list);
3926-}
3927-
3928-
3929-bool metadata_get_metadata_entry(struct client_command_context *cmd,
3930- const char *mailboxname, const char *entry,
3931- const char **value_r, bool private)
3932-{
3933- int success;
3934- struct mail_storage *storage;
3935- const char *key;
3936-
3937- if (metadata_debug)
3938- i_info("metadata_get_metadata_entry: mailboxname=%s, entry=%s,"
3939- " private=%d", mailboxname, entry, private);
3940-
3941- if (!client_verify_mailbox_name(cmd, mailboxname,
3942- CLIENT_VERIFY_MAILBOX_SHOULD_EXIST))
3943- return FALSE;
3944-
3945- storage = client_find_storage(cmd, &mailboxname);
3946- if (!storage)
3947- return FALSE;
3948-
3949- key = create_dict_key(storage, mailboxname, entry, private);
3950- if (metadata_debug)
3951- i_info("metadata_get_metadata_entry: dict key=%s", key);
3952-
3953- success = dict_lookup(metadata_dict, cmd->pool, key, value_r);
3954-
3955- if (success == 0) {
3956- *value_r = NULL;
3957- } else if (success < 0) {
3958- client_send_tagline(cmd, "NO Lookup failed.");
3959- }
3960-
3961- return success >= 0;
3962-}
3963-
3964-bool metadata_set_metadata_entry(struct client_command_context *cmd,
3965- const char *mailboxname, const char *entry,
3966- const char *value, bool private)
3967-{
3968- struct dict_transaction_context *dt;
3969- struct mail_storage *storage;
3970- const char *key;
3971-
3972- if (metadata_debug)
3973- i_info("metadata_set_metadata_entry: mailboxname=%s, entry=%s,"
3974- " value=%s, private=%d", mailboxname, entry,
3975- value != NULL ? value : "<NULL>", private);
3976-
3977- if (!client_verify_mailbox_name(cmd, mailboxname,
3978- CLIENT_VERIFY_MAILBOX_SHOULD_EXIST))
3979- return FALSE;
3980-
3981- storage = client_find_storage(cmd, &mailboxname);
3982- if (!storage)
3983- return FALSE;
3984-
3985- key = create_dict_key(storage, mailboxname, entry, private);
3986- if (metadata_debug)
3987- i_info("metadata_set_metadata_entry: dict key=%s", key);
3988-
3989- dt = dict_transaction_begin(metadata_dict);
3990-
3991- if (value != NULL) {
3992- dict_set(dt, key, value);
3993- } else {
3994- dict_unset(dt, key);
3995- }
3996-
3997- if (dict_transaction_commit(&dt) < 0)
3998- {
3999- client_send_tagline(cmd, "NO Setting meta-data failed.");
4000- return FALSE;
4001- }
4002-
4003- return TRUE;
4004-}
4005-
4006-void metadata_plugin_init(void)
4007-{
4008- const char *username;
4009- const char *dict_uri;
4010- const char *base_dir;
4011-
4012- username = getenv("USER");
4013- if (username == NULL)
4014- i_fatal("metadata plugin: USER unset");
4015-
4016- dict_uri = getenv("METADATA_DICT");
4017- if (dict_uri == NULL)
4018- i_fatal("metadata plugin: METADATA_DICT unset");
4019-
4020- metadata_allow_private = getenv("METADATA_ALLOW_PRIVATE") != NULL;
4021- metadata_debug = getenv("METADATA_DEBUG") != NULL;
4022-
4023- base_dir = getenv("BASE_DIR");
4024- if (base_dir == NULL)
4025- base_dir = PKG_RUNDIR;
4026- metadata_dict = dict_init(dict_uri, DICT_DATA_TYPE_STRING, username,
4027- base_dir);
4028-
4029- if (metadata_dict == NULL)
4030- i_fatal("metadata plugin: dict_init() failed");
4031-
4032- metadata_next_hook_mailbox_list_created = hook_mailbox_list_created;
4033- hook_mailbox_list_created = metadata_mailbox_list_created;
4034+void metadata_plugin_init(struct module *module)
4035+{
4036+ mail_storage_hooks_add(module, &metadata_mail_storage_hooks);
4037 }
4038
4039 void metadata_plugin_deinit(void)
4040 {
4041- if (metadata_dict != NULL) {
4042- dict_deinit(&metadata_dict);
4043- }
4044- if (metadata_next_hook_mailbox_list_created) {
4045- hook_mailbox_list_created =
4046- metadata_next_hook_mailbox_list_created;
4047- }
4048+ mail_storage_hooks_remove(&metadata_mail_storage_hooks);
4049 }
4050
4051=== removed file 'src/metadata-plugin.h'
4052--- src/metadata-plugin.h 2010-08-11 17:11:01 +0000
4053+++ src/metadata-plugin.h 1970-01-01 00:00:00 +0000
4054@@ -1,17 +0,0 @@
4055-#ifndef __METADATA_PLUGIN
4056-#define __METADATA_PLUGIN
4057-
4058-void metadata_plugin_init(void);
4059-void metadata_plugin_deinit(void);
4060-
4061-bool metadata_private_allowed(void);
4062-
4063-bool metadata_get_metadata_entry(struct client_command_context *cmd,
4064- const char *mailboxname, const char *entry,
4065- const char **value_r, bool private);
4066-
4067-bool metadata_set_metadata_entry(struct client_command_context *cmd,
4068- const char *mailboxname, const char *entry,
4069- const char *value, bool private);
4070-
4071-#endif
4072
4073=== added file 'src/metadata-settings.c'
4074--- src/metadata-settings.c 1970-01-01 00:00:00 +0000
4075+++ src/metadata-settings.c 2011-09-29 19:49:50 +0000
4076@@ -0,0 +1,85 @@
4077+/*
4078+ Copyright (c) 2010 by Dennis Schridde
4079+
4080+ This file is part of dovecot-metadata.
4081+
4082+ dovecot-metadata is free software: you can redistribute it and/or modify
4083+ it under the terms of the GNU Lesser General Public License as published by
4084+ the Free Software Foundation, either version 3 of the License, or
4085+ (at your option) any later version.
4086+
4087+ dovecot-metadata is distributed in the hope that it will be useful,
4088+ but WITHOUT ANY WARRANTY; without even the implied warranty of
4089+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4090+ GNU Lesser General Public License for more details.
4091+
4092+ You should have received a copy of the GNU Lesser General Public License
4093+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
4094+*/
4095+#include "metadata-settings.h"
4096+
4097+#include <stdlib.h>
4098+
4099+#define METADATA_MAXSIZE_DEFAULT 1024
4100+#define METADATA_MAXENTRIES_DEFAULT 65336
4101+
4102+void
4103+metadata_settings_init(struct metadata_settings **set, struct mail_user *user) {
4104+ *set = i_new(struct metadata_settings, 1);
4105+ memset(*set, 0, sizeof(**set));
4106+
4107+
4108+ const char *dict_uri = mail_user_plugin_getenv(user, "metadata_dict");
4109+ if (dict_uri != NULL) {
4110+ (*set)->dict_uri = dict_uri;
4111+ }
4112+ else if (user->mail_debug) {
4113+ i_debug("metadata: No metadata_dict setting - "
4114+ "metadata storage is disabled");
4115+ (*set)->dict_uri = NULL;
4116+ }
4117+
4118+ const char *maxsize = mail_user_plugin_getenv(user, "metadata_maxsize");
4119+ if (maxsize != NULL) {
4120+ (*set)->maxsize = strtol(maxsize, NULL, 10);
4121+ if ((*set)->maxsize < 0 || (*set)->maxsize == LONG_MAX) {
4122+ if (user->mail_debug) {
4123+ i_debug("metadata: Illegal metadata_maxsize setting - "
4124+ "using default of %d", METADATA_MAXSIZE_DEFAULT);
4125+ }
4126+ (*set)->maxsize = METADATA_MAXSIZE_DEFAULT;
4127+ }
4128+ }
4129+ else {
4130+ if (user->mail_debug) {
4131+ i_debug("metadata: No metadata_maxsize setting - "
4132+ "using default of %d", METADATA_MAXSIZE_DEFAULT);
4133+ }
4134+ (*set)->maxsize = METADATA_MAXSIZE_DEFAULT;
4135+ }
4136+
4137+ const char *maxentries = mail_user_plugin_getenv(user, "metadata_maxentries");
4138+ if (maxentries != NULL) {
4139+ (*set)->maxentries = strtol(maxentries, NULL, 10);
4140+ if ((*set)->maxentries < 0 || (*set)->maxentries == LONG_MAX) {
4141+ if (user->mail_debug) {
4142+ i_debug("metadata: Illegal metadata_maxentries setting - "
4143+ "using default of %d", METADATA_MAXENTRIES_DEFAULT);
4144+ }
4145+ (*set)->maxentries = METADATA_MAXENTRIES_DEFAULT;
4146+ }
4147+ }
4148+ else {
4149+ if (user->mail_debug) {
4150+ i_debug("metadata: No metadata_maxentries setting - "
4151+ "using default of %d", METADATA_MAXENTRIES_DEFAULT);
4152+ }
4153+ (*set)->maxentries = METADATA_MAXENTRIES_DEFAULT;
4154+ }
4155+}
4156+
4157+void
4158+metadata_settings_deinit(struct metadata_settings **set) {
4159+ free(*set);
4160+ *set = NULL;
4161+}
4162
4163=== added file 'src/metadata-settings.h'
4164--- src/metadata-settings.h 1970-01-01 00:00:00 +0000
4165+++ src/metadata-settings.h 2011-09-29 19:49:50 +0000
4166@@ -0,0 +1,40 @@
4167+/*
4168+ Copyright (c) 2010 by Dennis Schridde
4169+
4170+ This file is part of dovecot-metadata.
4171+
4172+ dovecot-metadata is free software: you can redistribute it and/or modify
4173+ it under the terms of the GNU Lesser General Public License as published by
4174+ the Free Software Foundation, either version 3 of the License, or
4175+ (at your option) any later version.
4176+
4177+ dovecot-metadata is distributed in the hope that it will be useful,
4178+ but WITHOUT ANY WARRANTY; without even the implied warranty of
4179+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4180+ GNU Lesser General Public License for more details.
4181+
4182+ You should have received a copy of the GNU Lesser General Public License
4183+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
4184+*/
4185+#ifndef DOVECOT_METADATA_SETTINGS_H
4186+#define DOVECOT_METADATA_SETTINGS_H
4187+
4188+#include "metadata-global.h"
4189+
4190+#include "mail-user.h"
4191+
4192+struct metadata_settings {
4193+ bool metadata_debug;
4194+ const char *dict_uri;
4195+ int maxsize;
4196+ int maxentries;
4197+};
4198+
4199+void
4200+metadata_settings_init(struct metadata_settings **set, struct mail_user *user)
4201+ ATTR_NONNULL(1,2);
4202+void
4203+metadata_settings_deinit(struct metadata_settings **set)
4204+ ATTR_NONNULL(1);
4205+
4206+#endif
4207
4208=== added file 'src/str-ext.c'
4209--- src/str-ext.c 1970-01-01 00:00:00 +0000
4210+++ src/str-ext.c 2011-09-29 19:49:50 +0000
4211@@ -0,0 +1,42 @@
4212+/*
4213+ Copyright (c) 2010 by Dennis Schridde
4214+
4215+ This file is part of dovecot-metadata.
4216+
4217+ dovecot-metadata is free software: you can redistribute it and/or modify
4218+ it under the terms of the GNU Lesser General Public License as published by
4219+ the Free Software Foundation, either version 3 of the License, or
4220+ (at your option) any later version.
4221+
4222+ dovecot-metadata is distributed in the hope that it will be useful,
4223+ but WITHOUT ANY WARRANTY; without even the implied warranty of
4224+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4225+ GNU Lesser General Public License for more details.
4226+
4227+ You should have received a copy of the GNU Lesser General Public License
4228+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
4229+*/
4230+#include "str-ext.h"
4231+
4232+#include <string.h>
4233+
4234+int
4235+strchr_num(const char *s, int c) {
4236+ int num = 0;
4237+ s = strchr(s, c);
4238+ while (s != NULL) {
4239+ num++;
4240+ s = strchr(s+1, c);
4241+ }
4242+ return num;
4243+}
4244+
4245+
4246+bool
4247+str_has_wildcards(const char *s) {
4248+ for (; *s != '\0'; s++) {
4249+ if (*s == '*' || *s == '%')
4250+ return TRUE;
4251+ }
4252+ return FALSE;
4253+}
4254
4255=== added file 'src/str-ext.h'
4256--- src/str-ext.h 1970-01-01 00:00:00 +0000
4257+++ src/str-ext.h 2011-09-29 19:49:50 +0000
4258@@ -0,0 +1,38 @@
4259+/*
4260+ Copyright (c) 2010 by Dennis Schridde
4261+
4262+ This file is part of dovecot-metadata.
4263+
4264+ dovecot-metadata is free software: you can redistribute it and/or modify
4265+ it under the terms of the GNU Lesser General Public License as published by
4266+ the Free Software Foundation, either version 3 of the License, or
4267+ (at your option) any later version.
4268+
4269+ dovecot-metadata is distributed in the hope that it will be useful,
4270+ but WITHOUT ANY WARRANTY; without even the implied warranty of
4271+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4272+ GNU Lesser General Public License for more details.
4273+
4274+ You should have received a copy of the GNU Lesser General Public License
4275+ along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
4276+*/
4277+#ifndef DOVECOT_STR_EXT_H
4278+#define DOVECOT_STR_EXT_H
4279+
4280+#include "metadata-global.h"
4281+
4282+#include <stdbool.h>
4283+
4284+#include "str.h"
4285+
4286+#define str_append_printf str_printfa
4287+
4288+int
4289+strchr_num(const char *s, int c)
4290+ ATTR_NONNULL(1);
4291+
4292+bool
4293+str_has_wildcards(const char *s)
4294+ ATTR_NONNULL(1);
4295+
4296+#endif

Subscribers

People subscribed via source and target branches

to all changes: