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
=== modified file '.hg_archival.txt'
--- .hg_archival.txt 2010-08-11 17:11:01 +0000
+++ .hg_archival.txt 2011-09-29 19:49:50 +0000
@@ -1,5 +1,4 @@
1repo: ae7e8348c8755ea0c8209c5b5efcc81228530b5e1repo: ae7e8348c8755ea0c8209c5b5efcc81228530b5e
2node: 4372f4ab8b0ee66b8c8e88e3661a67d441537b2d2node: bdf2445e101fefc55ffa7bb2c4c5807af0799bd2
3branch: default3branch: default
4latesttag: works-with-dovecot-1.14tag: v8
5latesttagdistance: 10
65
=== modified file '.hgignore'
--- .hgignore 2010-08-11 17:11:01 +0000
+++ .hgignore 2011-09-29 19:49:50 +0000
@@ -1,17 +1,26 @@
1Makefile$1Makefile$
2Makefile\.in$2Makefile\.in$
3^stamp-h1$
4^metadata-config\.h$
5^metadata-config\.h\.in$
6^ChangeLog$
7^\.cscope
3^aclocal\.m4$8^aclocal\.m4$
4^autom4te\.cache9^autom4te\.cache
5^configure$10^configure$
11^configure.lineno$
6^config\.12^config\.
7^depcomp$13^depcomp$
8^libtool$14^libtool$
9^install-sh15^install-sh
10^ltmain\.sh$16^ltmain\.sh$
17^m4
11^missing$18^missing$
12^src/.deps19^patches
13^src/.libs20^src/\.deps
21^src/\.libs
14~$22~$
23\.tar\.gz$
15\.pyc$24\.pyc$
16\.lo$25\.lo$
17\.la$26\.la$
1827
=== added file '.hgsigs'
--- .hgsigs 1970-01-01 00:00:00 +0000
+++ .hgsigs 2011-09-29 19:49:50 +0000
@@ -0,0 +1,1 @@
1fc360cf4ef81fe0b54ea56074d6b6ee9dcb1c3d5 0 iEYEABECAAYFAk30p6cACgkQjqfyF1DtJW689QCfU6UysrMwwqCycZ9EFbyN9Iof5D0AoL/np56Phl9xCZN9hzeaq0qIcszU
02
=== modified file '.hgtags'
--- .hgtags 2010-08-11 17:11:01 +0000
+++ .hgtags 2011-09-29 19:49:50 +0000
@@ -1,1 +1,2 @@
1604f64c0f4852e3c0b5fd20b46cb7372791ec91b works-with-dovecot-1.11604f64c0f4852e3c0b5fd20b46cb7372791ec91b works-with-dovecot-1.1
2fc360cf4ef81fe0b54ea56074d6b6ee9dcb1c3d5 v7
23
=== modified file 'AUTHORS'
--- AUTHORS 2010-08-11 17:11:01 +0000
+++ AUTHORS 2011-09-29 19:49:50 +0000
@@ -1,1 +1,2 @@
1Bernhard Herzog <bh@intevation.de>1Bernhard Herzog <bh@intevation.de>
2Dennis Schridde <devurandom@gna.org>
23
=== modified file 'INSTALL'
--- INSTALL 2010-08-11 17:11:01 +0000
+++ INSTALL 2011-09-29 19:49:50 +0000
@@ -1,16 +1,25 @@
1Installation Instructions1Installation Instructions
2*************************2*************************
33
4Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005 Free4Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005,
5Software Foundation, Inc.52006, 2007, 2008, 2009 Free Software Foundation, Inc.
66
7This file is free documentation; the Free Software Foundation gives7 Copying and distribution of this file, with or without modification,
8unlimited permission to copy, distribute and modify it.8are permitted in any medium without royalty provided the copyright
9notice and this notice are preserved. This file is offered as-is,
10without warranty of any kind.
911
10Basic Installation12Basic Installation
11==================13==================
1214
13These are generic installation instructions.15 Briefly, the shell commands `./configure; make; make install' should
16configure, build, and install this package. The following
17more-detailed instructions are generic; see the `README' file for
18instructions specific to this package. Some packages provide this
19`INSTALL' file but do not implement all of the features documented
20below. The lack of an optional feature in a given package is not
21necessarily a bug. More recommendations for GNU packages can be found
22in *note Makefile Conventions: (standards)Makefile Conventions.
1423
15 The `configure' shell script attempts to guess correct values for24 The `configure' shell script attempts to guess correct values for
16various system-dependent variables used during compilation. It uses25various system-dependent variables used during compilation. It uses
@@ -23,9 +32,9 @@
2332
24 It can also use an optional file (typically called `config.cache'33 It can also use an optional file (typically called `config.cache'
25and enabled with `--cache-file=config.cache' or simply `-C') that saves34and enabled with `--cache-file=config.cache' or simply `-C') that saves
26the results of its tests to speed up reconfiguring. (Caching is35the results of its tests to speed up reconfiguring. Caching is
27disabled by default to prevent problems with accidental use of stale36disabled by default to prevent problems with accidental use of stale
28cache files.)37cache files.
2938
30 If you need to do unusual things to compile the package, please try39 If you need to do unusual things to compile the package, please try
31to figure out how `configure' could check whether to do them, and mail40to figure out how `configure' could check whether to do them, and mail
@@ -35,30 +44,37 @@
35may remove or edit it.44may remove or edit it.
3645
37 The file `configure.ac' (or `configure.in') is used to create46 The file `configure.ac' (or `configure.in') is used to create
38`configure' by a program called `autoconf'. You only need47`configure' by a program called `autoconf'. You need `configure.ac' if
39`configure.ac' if you want to change it or regenerate `configure' using48you want to change it or regenerate `configure' using a newer version
40a newer version of `autoconf'.49of `autoconf'.
4150
42The simplest way to compile this package is:51 The simplest way to compile this package is:
4352
44 1. `cd' to the directory containing the package's source code and type53 1. `cd' to the directory containing the package's source code and type
45 `./configure' to configure the package for your system. If you're54 `./configure' to configure the package for your system.
46 using `csh' on an old version of System V, you might need to type
47 `sh ./configure' instead to prevent `csh' from trying to execute
48 `configure' itself.
4955
50 Running `configure' takes awhile. While running, it prints some56 Running `configure' might take a while. While running, it prints
51 messages telling which features it is checking for.57 some messages telling which features it is checking for.
5258
53 2. Type `make' to compile the package.59 2. Type `make' to compile the package.
5460
55 3. Optionally, type `make check' to run any self-tests that come with61 3. Optionally, type `make check' to run any self-tests that come with
56 the package.62 the package, generally using the just-built uninstalled binaries.
5763
58 4. Type `make install' to install the programs and any data files and64 4. Type `make install' to install the programs and any data files and
59 documentation.65 documentation. When installing into a prefix owned by root, it is
6066 recommended that the package be configured and built as a regular
61 5. You can remove the program binaries and object files from the67 user, and only the `make install' phase executed with root
68 privileges.
69
70 5. Optionally, type `make installcheck' to repeat any self-tests, but
71 this time using the binaries in their final installed location.
72 This target does not install anything. Running this target as a
73 regular user, particularly if the prior `make install' required
74 root privileges, verifies that the installation completed
75 correctly.
76
77 6. You can remove the program binaries and object files from the
62 source code directory by typing `make clean'. To also remove the78 source code directory by typing `make clean'. To also remove the
63 files that `configure' created (so you can compile the package for79 files that `configure' created (so you can compile the package for
64 a different kind of computer), type `make distclean'. There is80 a different kind of computer), type `make distclean'. There is
@@ -67,45 +83,69 @@
67 all sorts of other programs in order to regenerate files that came83 all sorts of other programs in order to regenerate files that came
68 with the distribution.84 with the distribution.
6985
86 7. Often, you can also type `make uninstall' to remove the installed
87 files again. In practice, not all packages have tested that
88 uninstallation works correctly, even though it is required by the
89 GNU Coding Standards.
90
91 8. Some packages, particularly those that use Automake, provide `make
92 distcheck', which can by used by developers to test that all other
93 targets like `make install' and `make uninstall' work correctly.
94 This target is generally not run by end users.
95
70Compilers and Options96Compilers and Options
71=====================97=====================
7298
73Some systems require unusual options for compilation or linking that the99 Some systems require unusual options for compilation or linking that
74`configure' script does not know about. Run `./configure --help' for100the `configure' script does not know about. Run `./configure --help'
75details on some of the pertinent environment variables.101for details on some of the pertinent environment variables.
76102
77 You can give `configure' initial values for configuration parameters103 You can give `configure' initial values for configuration parameters
78by setting variables in the command line or in the environment. Here104by setting variables in the command line or in the environment. Here
79is an example:105is an example:
80106
81 ./configure CC=c89 CFLAGS=-O2 LIBS=-lposix107 ./configure CC=c99 CFLAGS=-g LIBS=-lposix
82108
83 *Note Defining Variables::, for more details.109 *Note Defining Variables::, for more details.
84110
85Compiling For Multiple Architectures111Compiling For Multiple Architectures
86====================================112====================================
87113
88You can compile the package for more than one kind of computer at the114 You can compile the package for more than one kind of computer at the
89same time, by placing the object files for each architecture in their115same time, by placing the object files for each architecture in their
90own directory. To do this, you must use a version of `make' that116own directory. To do this, you can use GNU `make'. `cd' to the
91supports the `VPATH' variable, such as GNU `make'. `cd' to the
92directory where you want the object files and executables to go and run117directory where you want the object files and executables to go and run
93the `configure' script. `configure' automatically checks for the118the `configure' script. `configure' automatically checks for the
94source code in the directory that `configure' is in and in `..'.119source code in the directory that `configure' is in and in `..'. This
95120is known as a "VPATH" build.
96 If you have to use a `make' that does not support the `VPATH'121
97variable, you have to compile the package for one architecture at a122 With a non-GNU `make', it is safer to compile the package for one
98time in the source code directory. After you have installed the123architecture at a time in the source code directory. After you have
99package for one architecture, use `make distclean' before reconfiguring124installed the package for one architecture, use `make distclean' before
100for another architecture.125reconfiguring for another architecture.
126
127 On MacOS X 10.5 and later systems, you can create libraries and
128executables that work on multiple system types--known as "fat" or
129"universal" binaries--by specifying multiple `-arch' options to the
130compiler but only a single `-arch' option to the preprocessor. Like
131this:
132
133 ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
134 CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \
135 CPP="gcc -E" CXXCPP="g++ -E"
136
137 This is not guaranteed to produce working output in all cases, you
138may have to build one architecture at a time and combine the results
139using the `lipo' tool if you have problems.
101140
102Installation Names141Installation Names
103==================142==================
104143
105By default, `make install' installs the package's commands under144 By default, `make install' installs the package's commands under
106`/usr/local/bin', include files under `/usr/local/include', etc. You145`/usr/local/bin', include files under `/usr/local/include', etc. You
107can specify an installation prefix other than `/usr/local' by giving146can specify an installation prefix other than `/usr/local' by giving
108`configure' the option `--prefix=PREFIX'.147`configure' the option `--prefix=PREFIX', where PREFIX must be an
148absolute file name.
109149
110 You can specify separate installation prefixes for150 You can specify separate installation prefixes for
111architecture-specific files and architecture-independent files. If you151architecture-specific files and architecture-independent files. If you
@@ -116,16 +156,47 @@
116 In addition, if you use an unusual directory layout you can give156 In addition, if you use an unusual directory layout you can give
117options like `--bindir=DIR' to specify different values for particular157options like `--bindir=DIR' to specify different values for particular
118kinds of files. Run `configure --help' for a list of the directories158kinds of files. Run `configure --help' for a list of the directories
119you can set and what kinds of files go in them.159you can set and what kinds of files go in them. In general, the
160default for these options is expressed in terms of `${prefix}', so that
161specifying just `--prefix' will affect all of the other directory
162specifications that were not explicitly provided.
163
164 The most portable way to affect installation locations is to pass the
165correct locations to `configure'; however, many packages provide one or
166both of the following shortcuts of passing variable assignments to the
167`make install' command line to change installation locations without
168having to reconfigure or recompile.
169
170 The first method involves providing an override variable for each
171affected directory. For example, `make install
172prefix=/alternate/directory' will choose an alternate location for all
173directory configuration variables that were expressed in terms of
174`${prefix}'. Any directories that were specified during `configure',
175but not in terms of `${prefix}', must each be overridden at install
176time for the entire installation to be relocated. The approach of
177makefile variable overrides for each directory variable is required by
178the GNU Coding Standards, and ideally causes no recompilation.
179However, some platforms have known limitations with the semantics of
180shared libraries that end up requiring recompilation when using this
181method, particularly noticeable in packages that use GNU Libtool.
182
183 The second method involves providing the `DESTDIR' variable. For
184example, `make install DESTDIR=/alternate/directory' will prepend
185`/alternate/directory' before all installation names. The approach of
186`DESTDIR' overrides is not required by the GNU Coding Standards, and
187does not work on platforms that have drive letters. On the other hand,
188it does better at avoiding recompilation issues, and works well even
189when some directory options were not specified in terms of `${prefix}'
190at `configure' time.
191
192Optional Features
193=================
120194
121 If the package supports it, you can cause programs to be installed195 If the package supports it, you can cause programs to be installed
122with an extra prefix or suffix on their names by giving `configure' the196with an extra prefix or suffix on their names by giving `configure' the
123option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.197option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
124198
125Optional Features199 Some packages pay attention to `--enable-FEATURE' options to
126=================
127
128Some packages pay attention to `--enable-FEATURE' options to
129`configure', where FEATURE indicates an optional part of the package.200`configure', where FEATURE indicates an optional part of the package.
130They may also pay attention to `--with-PACKAGE' options, where PACKAGE201They may also pay attention to `--with-PACKAGE' options, where PACKAGE
131is something like `gnu-as' or `x' (for the X Window System). The202is something like `gnu-as' or `x' (for the X Window System). The
@@ -137,14 +208,53 @@
137you can use the `configure' options `--x-includes=DIR' and208you can use the `configure' options `--x-includes=DIR' and
138`--x-libraries=DIR' to specify their locations.209`--x-libraries=DIR' to specify their locations.
139210
211 Some packages offer the ability to configure how verbose the
212execution of `make' will be. For these packages, running `./configure
213--enable-silent-rules' sets the default to minimal output, which can be
214overridden with `make V=1'; while running `./configure
215--disable-silent-rules' sets the default to verbose, which can be
216overridden with `make V=0'.
217
218Particular systems
219==================
220
221 On HP-UX, the default C compiler is not ANSI C compatible. If GNU
222CC is not installed, it is recommended to use the following options in
223order to use an ANSI C compiler:
224
225 ./configure CC="cc -Ae -D_XOPEN_SOURCE=500"
226
227and if that doesn't work, install pre-built binaries of GCC for HP-UX.
228
229 On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot
230parse its `<wchar.h>' header file. The option `-nodtk' can be used as
231a workaround. If GNU CC is not installed, it is therefore recommended
232to try
233
234 ./configure CC="cc"
235
236and if that doesn't work, try
237
238 ./configure CC="cc -nodtk"
239
240 On Solaris, don't put `/usr/ucb' early in your `PATH'. This
241directory contains several dysfunctional programs; working variants of
242these programs are available in `/usr/bin'. So, if you need `/usr/ucb'
243in your `PATH', put it _after_ `/usr/bin'.
244
245 On Haiku, software installed for all users goes in `/boot/common',
246not `/usr/local'. It is recommended to use the following options:
247
248 ./configure --prefix=/boot/common
249
140Specifying the System Type250Specifying the System Type
141==========================251==========================
142252
143There may be some features `configure' cannot figure out automatically,253 There may be some features `configure' cannot figure out
144but needs to determine by the type of machine the package will run on.254automatically, but needs to determine by the type of machine the package
145Usually, assuming the package is built to be run on the _same_255will run on. Usually, assuming the package is built to be run on the
146architectures, `configure' can figure that out, but if it prints a256_same_ architectures, `configure' can figure that out, but if it prints
147message saying it cannot guess the machine type, give it the257a message saying it cannot guess the machine type, give it the
148`--build=TYPE' option. TYPE can either be a short name for the system258`--build=TYPE' option. TYPE can either be a short name for the system
149type, such as `sun4', or a canonical name which has the form:259type, such as `sun4', or a canonical name which has the form:
150260
@@ -152,7 +262,8 @@
152262
153where SYSTEM can have one of these forms:263where SYSTEM can have one of these forms:
154264
155 OS KERNEL-OS265 OS
266 KERNEL-OS
156267
157 See the file `config.sub' for the possible values of each field. If268 See the file `config.sub' for the possible values of each field. If
158`config.sub' isn't included in this package, then this package doesn't269`config.sub' isn't included in this package, then this package doesn't
@@ -170,9 +281,9 @@
170Sharing Defaults281Sharing Defaults
171================282================
172283
173If you want to set default values for `configure' scripts to share, you284 If you want to set default values for `configure' scripts to share,
174can create a site shell script called `config.site' that gives default285you can create a site shell script called `config.site' that gives
175values for variables like `CC', `cache_file', and `prefix'.286default values for variables like `CC', `cache_file', and `prefix'.
176`configure' looks for `PREFIX/share/config.site' if it exists, then287`configure' looks for `PREFIX/share/config.site' if it exists, then
177`PREFIX/etc/config.site' if it exists. Or, you can set the288`PREFIX/etc/config.site' if it exists. Or, you can set the
178`CONFIG_SITE' environment variable to the location of the site script.289`CONFIG_SITE' environment variable to the location of the site script.
@@ -181,7 +292,7 @@
181Defining Variables292Defining Variables
182==================293==================
183294
184Variables not defined in a site shell script can be set in the295 Variables not defined in a site shell script can be set in the
185environment passed to `configure'. However, some packages may run296environment passed to `configure'. However, some packages may run
186configure again during the build, and the customized values of these297configure again during the build, and the customized values of these
187variables may be lost. In order to avoid this problem, you should set298variables may be lost. In order to avoid this problem, you should set
@@ -190,21 +301,29 @@
190 ./configure CC=/usr/local2/bin/gcc301 ./configure CC=/usr/local2/bin/gcc
191302
192causes the specified `gcc' to be used as the C compiler (unless it is303causes the specified `gcc' to be used as the C compiler (unless it is
193overridden in the site shell script). Here is a another example:304overridden in the site shell script).
194305
195 /bin/bash ./configure CONFIG_SHELL=/bin/bash306Unfortunately, this technique does not work for `CONFIG_SHELL' due to
196307an Autoconf bug. Until the bug is fixed you can use this workaround:
197Here the `CONFIG_SHELL=/bin/bash' operand causes subsequent308
198configuration-related scripts to be executed by `/bin/bash'.309 CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash
199310
200`configure' Invocation311`configure' Invocation
201======================312======================
202313
203`configure' recognizes the following options to control how it operates.314 `configure' recognizes the following options to control how it
315operates.
204316
205`--help'317`--help'
206`-h'318`-h'
207 Print a summary of the options to `configure', and exit.319 Print a summary of all of the options to `configure', and exit.
320
321`--help=short'
322`--help=recursive'
323 Print a summary of the options unique to this package's
324 `configure', and exit. The `short' variant lists options used
325 only in the top level, while the `recursive' variant lists options
326 also present in any nested packages.
208327
209`--version'328`--version'
210`-V'329`-V'
@@ -231,6 +350,16 @@
231 Look for the package's source code in directory DIR. Usually350 Look for the package's source code in directory DIR. Usually
232 `configure' can determine that directory automatically.351 `configure' can determine that directory automatically.
233352
353`--prefix=DIR'
354 Use DIR as the installation prefix. *note Installation Names::
355 for more details, including other options available for fine-tuning
356 the installation locations.
357
358`--no-create'
359`-n'
360 Run the configure checks, but stop before creating any output
361 files.
362
234`configure' also accepts some other, not widely useful, options. Run363`configure' also accepts some other, not widely useful, options. Run
235`configure --help' for more details.364`configure --help' for more details.
236365
237366
=== modified file 'Makefile.am'
--- Makefile.am 2010-08-11 17:11:01 +0000
+++ Makefile.am 2011-09-29 19:49:50 +0000
@@ -1,3 +1,5 @@
1ACLOCAL_AMFLAGS = -I m4
2
1SUBDIRS = src3SUBDIRS = src
24
3EXTRA_DIST = \5EXTRA_DIST = \
46
=== modified file 'NEWS'
--- NEWS 2010-08-11 17:11:01 +0000
+++ NEWS 2011-09-29 19:49:50 +0000
@@ -1,2 +1,9 @@
1There hasn't been a release yet. See the mercurial logs details on the12011-06-12: Version 8
2changes that happened so far.2 * Fix compliance with RFC 5464 Section 4.2.2
3 * Minor cleanups
4
52011-06-10: Version 7 - "Works with Akonadi"
6 * RFC 5464 compatible
7 * imap-annotatemore is a partial implementation of draft-daboo-imap-annotatemore-08
8 Based on work by Bernhard Herzog (Intevation GmbH)
9 * imap-metadata is a complete implementation of RFC 5464
310
=== added file 'TODO'
--- TODO 1970-01-01 00:00:00 +0000
+++ TODO 2011-09-29 19:49:50 +0000
@@ -0,0 +1,1 @@
1* Handle mailbox moves/renames
02
=== modified file 'autogen.sh'
--- autogen.sh 2010-08-11 17:11:01 +0000
+++ autogen.sh 2011-09-29 19:49:50 +0000
@@ -1,12 +1,7 @@
1#!/bin/sh1#!/bin/sh
22
3# If you've non-standard directories, set these3echo '+ creating m4/ ...'
4#ACLOCAL_DIR=4test -d m4 || mkdir m4
5#GETTEXT_DIR=5
66echo '+ running autoreconf ...'
7if test "$ACLOCAL_DIR" != ""; then7autoreconf --force --install
8 ACLOCAL="aclocal -I $ACLOCAL_DIR"
9 export ACLOCAL
10fi
11
12autoreconf -i
138
=== added file 'configure.ac'
--- configure.ac 1970-01-01 00:00:00 +0000
+++ configure.ac 2011-09-29 19:49:50 +0000
@@ -0,0 +1,20 @@
1AC_PREREQ(2.65)
2AC_INIT([dovecot-metadata],[8],[devurandom@gmx.net])
3
4AM_INIT_AUTOMAKE([1.10 silent-rules])
5AM_MAINTAINER_MODE
6
7AC_CONFIG_MACRO_DIR([m4])
8AC_CONFIG_SRCDIR([src])
9
10AC_PROG_CC_STDC
11LT_INIT
12
13DC_DOVECOT([2.0])
14
15AC_CONFIG_HEADERS([metadata-config.h])
16AC_CONFIG_FILES([
17 Makefile
18 src/Makefile
19])
20AC_OUTPUT
021
=== removed file 'configure.in'
--- configure.in 2010-08-11 17:11:01 +0000
+++ configure.in 1970-01-01 00:00:00 +0000
@@ -1,56 +0,0 @@
1AC_INIT(dovecot-metadata, 0.0.1, [bh@intevation.de])
2AC_CONFIG_SRCDIR([src])
3
4AC_CONFIG_HEADERS([metadata-config.h])
5AM_INIT_AUTOMAKE
6
7AM_MAINTAINER_MODE
8
9AC_PROG_CC
10AM_PROG_LIBTOOL
11
12AC_ARG_WITH(dovecot,
13[ --with-dovecot[=DIR] Dovecot base directory (../dovecot)],
14 dovecotdir="$withval",
15 dovecotdir=../dovecot
16)
17old=`pwd`
18cd $dovecotdir
19dovecotdir=`pwd`
20cd $old
21AC_SUBST(dovecotdir)
22
23if ! test -f "$dovecotdir/dovecot-config"; then
24 echo
25 echo "dovecot-config not found from $dovecotdir, use --with-dovecot=PATH"
26 echo "to give path to compiled Dovecot sources or to a directory with the"
27 echo "installed dovecot-config file."
28 AC_MSG_ERROR([dovecot-config not found])
29fi
30
31if test -d "$dovecotdir/src"; then
32 # compiling against sources
33 have_dovecot_libs=yes
34else
35 # compiling against installed headers
36 have_dovecot_libs=no
37fi
38AM_CONDITIONAL(HAVE_DOVECOT_LIBS, test "$have_dovecot_libs" = "yes")
39
40dnl replace relative ../ paths in the file with full paths
41eval `cat $dovecotdir/dovecot-config|sed 's,\$(top_builddir)/,$dovecotdir/,g'`
42
43if test $have_dovecot_libs = yes; then
44 dovecot_incdir="$dovecotdir"
45fi
46
47
48AC_SUBST(dovecot_incdir)
49AC_SUBST(moduledir)
50
51AC_CONFIG_FILES([
52Makefile
53src/Makefile
54])
55
56AC_OUTPUT
570
=== modified file 'debian/changelog'
--- debian/changelog 2010-08-11 17:11:01 +0000
+++ debian/changelog 2011-09-29 19:49:50 +0000
@@ -1,3 +1,14 @@
1dovecot-metadata-plugin (8-0ubuntu1) oneiric; urgency=low
2
3 * New upstream release, fixes FTBFS with dovecot >= 2.0.0 (LP: #831179).
4 - d/control: Switched upstream source to http://hg.dovecot.org as original
5 source appears to be inactive.
6 - d/control: Bumped Standards-Version, no changes.
7 - d/copyright: Updated and converted to DEP-5.
8 - d/rules: Cleared dependency_libs in .la files.
9
10 -- James Page <james.page@ubuntu.com> Thu, 29 Sep 2011 20:35:51 +0100
11
1dovecot-metadata-plugin (0.0.1~hg144-0ubuntu1) maverick; urgency=low12dovecot-metadata-plugin (0.0.1~hg144-0ubuntu1) maverick; urgency=low
213
3 * Initial release to support Kolab server14 * Initial release to support Kolab server
415
=== modified file 'debian/control'
--- debian/control 2010-08-11 17:11:01 +0000
+++ debian/control 2011-09-29 19:49:50 +0000
@@ -4,14 +4,14 @@
4Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>4Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com>
5XSBC-Original-Maintainer: Scott Kitterman <scott@kitterman.com>5XSBC-Original-Maintainer: Scott Kitterman <scott@kitterman.com>
6Build-Depends: debhelper (>= 7.0.50), automake, libtool, dovecot-dev, libldap2-dev6Build-Depends: debhelper (>= 7.0.50), automake, libtool, dovecot-dev, libldap2-dev
7Standards-Version: 3.9.17Standards-Version: 3.9.2
8Homepage: http://hg.intevation.org/kolab/dovecot-metadata-plugin8Homepage: http://hg.dovecot.org/dovecot-metadata-plugin
99
10Package: dovecot-metadata-plugin10Package: dovecot-metadata-plugin
11Architecture: any11Architecture: any
12Depends: ${shlibs:Depends}, ${misc:Depends}12Depends: ${shlibs:Depends}, ${misc:Depends}
13Description: Experimental IMAP METADATA Extension for Dovecot13Description: Experimental IMAP METADATA Extension for Dovecot
14 This is a set of plugins for the Dovecot IMAP server version 1.2 that14 This is a set of plugins for the Dovecot IMAP server version 2.0 that
15 implement the IMAP METADATA Extension. The goal of the development is15 implement the IMAP METADATA Extension. The goal of the development is
16 to extend dovecot so that it can be used as the IMAP component instead16 to extend dovecot so that it can be used as the IMAP component instead
17 of Cyrus IMAPd in the Kolab server.17 of Cyrus IMAPd in the Kolab server.
1818
=== modified file 'debian/copyright'
--- debian/copyright 2010-08-11 17:11:01 +0000
+++ debian/copyright 2011-09-29 19:49:50 +0000
@@ -1,46 +1,25 @@
1This work was packaged for Ubuntu by:1Format: http://dep.debian.net/deps/dep5/
22Upstream-Name: Dovecot Metadata Plugin
3 Scott Kitterman <scott@kitterman.com> on Wed, 11 Aug 2010 17:11:01 -04003Source: http://hg.dovecot.org/dovecot-metadata-plugin
44
5It was downloaded from:5Files: *
66Copyright: 2008, by Intevation GmbH
7http://hg.intevation.org/kolab/dovecot-metadata-plugin/archive/tip.tar.gz7 2010, Dennis Schridde
88 2008, Bernhard Herzog <bh@intevation.de>
9Upstream Author:9License: LGPL-3
1010
11 Bernhard Herzog <bh@intevation.de>11Files: test/*
1212Copyright: 2004-2008, by Intevation GmbH
13Copyright:13License: LGPL-2.1
1414
15 src/imap-annotatemore-plugin.c:/* Copyright (C) 2008 by Intevation GmbH15Files: debian/*
16 src/metadata-plugin.c:/* Copyright (C) 2008, 2009 by Intevation GmbH16Copyright: 2010 Scott Kitterman <scott@kitterman.com>
17 test/imapsupport.py:# Copyright (C) 2008 by Intevation GmbH17License: LGPL-2.1
18 test/test_annotatemore.py:# Copyright (C) 2008 by Intevation GmbH18
19 test/runtests.py:# Copyright (C) 2004, 2005, 2006, 2007, 2008 by Intevation GmbH19License: LGPL-3
20 test/test_annotatemore_private.py:# Copyright (C) 2008 by Intevation GmbH20 On Debian systems, the complete text of the GNU Lesser General Public License
2121 3 can be found in '/usr/share/common-licenses/GPL/LGPL-3
22License:22
2323License: LGPL-2.1
24 This library is free software; you can redistribute it and/or24 On Debian systems, the complete text of the GNU Lesser General Public License
25 modify it under the terms of the GNU Lesser General Public25 2.1 can be found in '/usr/share/common-licenses/GPL/LGPL-2.1
26 License as published by the Free Software Foundation; either
27 version 2.1 of the License, or (at your option) any later version.
28
29 This library is distributed in the hope that it will be useful,
30 but WITHOUT ANY WARRANTY; without even the implied warranty of
31 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
32 Lesser General Public License for more details.
33
34 You should have received a copy of the GNU Lesser General Public
35 License along with this library; if not, write to the Free Software
36 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
37
38On Debian systems, the complete text of the GNU Lesser General Public License
392.1 can be found in '/usr/share/common-licenses/GPL/LGPL-2.1
40
41The Debian packaging is:
42
43 Copyright (C) 2010 Scott Kitterman <scott@kitterman.com>
44
45and is licensed under the smae terms as the upstream source. See above.
46
4726
=== modified file 'debian/rules'
--- debian/rules 2010-08-11 17:11:01 +0000
+++ debian/rules 2011-09-29 19:49:50 +0000
@@ -13,6 +13,10 @@
13 dh $@13 dh $@
1414
15override_dh_auto_configure:15override_dh_auto_configure:
16 test -d m4 || mkdir m4
16 autoreconf -f -i -Wall,no-obsolete17 autoreconf -f -i -Wall,no-obsolete
17 dh_auto_configure -- --with-dovecot=/usr/lib/dovecot/18 dh_auto_configure -- --with-dovecot=/usr/lib/dovecot/
1819
20override_dh_install:
21 sed -i "/dependency_libs/ s/'.*'/''/" `find . -name '*.la'`
22 dh_install
1923
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2010-08-11 17:11:01 +0000
+++ src/Makefile.am 2011-09-29 19:49:50 +0000
@@ -1,26 +1,50 @@
1AM_CPPFLAGS = \1AM_CPPFLAGS = $(LIBDOVECOT_INCLUDE) $(LIBDOVECOT_STORAGE_INCLUDE) $(LIBDOVECOT_IMAP_INCLUDE)
2 -I$(dovecot_incdir) \2
3 -I$(dovecot_incdir)/src/lib \3imap_moduledir = $(dovecot_moduledir)
4 -I$(dovecot_incdir)/src/lib-mail \
5 -I$(dovecot_incdir)/src/lib-imap \
6 -I$(dovecot_incdir)/src/lib-storage \
7 -I$(dovecot_incdir)/src/lib-dict \
8 -I$(dovecot_incdir)/src/imap \
9 -DPKG_RUNDIR=\""$(rundir)"\"
10
11
12imap_moduledir = $(moduledir)/imap
13
14imap_module_LTLIBRARIES = \4imap_module_LTLIBRARIES = \
15 lib80_metadata_plugin.la \5 lib80_metadata_plugin.la \
6 lib90_imap_metadata_plugin.la \
16 lib90_imap_annotatemore_plugin.la7 lib90_imap_annotatemore_plugin.la
178
18lib80_metadata_plugin_la_LDFLAGS = -module -avoid-version9lib80_metadata_plugin_la_LDFLAGS = -module -avoid-version
19
20lib80_metadata_plugin_la_SOURCES = \10lib80_metadata_plugin_la_SOURCES = \
21 metadata-plugin.c11 metadata-plugin.c \
12 metadata-backend.c \
13 metadata-entry.c \
14 metadata-mail-user-module.c \
15 metadata-mail-storage-module.c \
16 metadata-settings.c \
17 dict-ext.c \
18 imap-arg-ext.c \
19 mailbox-ext.c \
20 str-ext.c
21
22lib90_imap_metadata_plugin_la_LDFLAGS = -module -avoid-version
23lib90_imap_metadata_plugin_la_SOURCES = \
24 imap-metadata-plugin.c
25if DOVECOT_PLUGIN_DEPS
26lib90_imap_metadata_plugin_la_LIBADD = \
27 lib80_metadata_plugin.la
28endif
2229
23lib90_imap_annotatemore_plugin_la_LDFLAGS = -module -avoid-version30lib90_imap_annotatemore_plugin_la_LDFLAGS = -module -avoid-version
24
25lib90_imap_annotatemore_plugin_la_SOURCES = \31lib90_imap_annotatemore_plugin_la_SOURCES = \
26 imap-annotatemore-plugin.c32 imap-annotatemore-plugin.c
33if DOVECOT_PLUGIN_DEPS
34lib90_imap_annotatemore_plugin_la_LIBADD = \
35 lib80_metadata_plugin.la
36endif
37
38noinst_HEADERS = \
39 metadata-backend.h \
40 metadata-entry.h \
41 metadata-entry-private.h \
42 metadata-global.h \
43 metadata-mail-user-module.h \
44 metadata-mail-user-module-private.h \
45 metadata-mail-storage-module.h \
46 metadata-settings.h \
47 dict-ext.h \
48 imap-arg-ext.h \
49 mailbox-ext.h \
50 str-ext.h
2751
=== added file 'src/dict-ext.c'
--- src/dict-ext.c 1970-01-01 00:00:00 +0000
+++ src/dict-ext.c 2011-09-29 19:49:50 +0000
@@ -0,0 +1,139 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "dict-ext.h"
20
21#include "str.h"
22#include "dict.h"
23
24enum dict_scope
25dict_get_scope(const char *path) {
26 if (strncasecmp(DICT_PATH_SHARED, path, sizeof(DICT_PATH_SHARED)) == 0)
27 return DICT_SCOPE_SHARED;
28 else if (strncasecmp(DICT_PATH_PRIVATE, path, sizeof(DICT_PATH_PRIVATE)) == 0)
29 return DICT_SCOPE_PRIVATE;
30
31 return DICT_SCOPE_INVALID;
32}
33
34const char *
35dict_path_from_scope(enum dict_scope scope) {
36 switch (scope) {
37 case DICT_SCOPE_SHARED:
38 return DICT_PATH_SHARED;
39 case DICT_SCOPE_PRIVATE:
40 return DICT_PATH_PRIVATE;
41 case DICT_SCOPE_INVALID:
42 return "";
43 }
44
45 return "";
46}
47
48struct dict_iterate_multiscope_context {
49 struct dict_iterate_context *dict_ctx;
50 struct dict *dict;
51 enum dict_iterate_flags flags;
52 string_t *path;
53 bool do_shared;
54 bool failed;
55};
56
57struct dict_iterate_multiscope_context *
58dict_iterate_multiscope_init(struct dict *dict, const char *path, enum dict_iterate_multiscope_flags flags) {
59 struct dict_iterate_multiscope_context *ctx = i_new(struct dict_iterate_multiscope_context, 1);
60 memset(ctx, 0, sizeof(*ctx));
61
62 ctx->dict = dict;
63 ctx->flags = flags & ~DICT_ITERATE_MULTISCOPE_FLAG_MULTISCOPE;
64
65 ctx->path = str_new(default_pool, 128);
66 if (flags & DICT_ITERATE_MULTISCOPE_FLAG_MULTISCOPE)
67 str_append(ctx->path, DICT_PATH_PRIVATE);
68 str_append(ctx->path, path);
69
70 ctx->do_shared = (flags & DICT_ITERATE_MULTISCOPE_FLAG_MULTISCOPE);
71 ctx->failed = false;
72
73 ctx->dict_ctx = dict_iterate_init(ctx->dict, str_c(ctx->path), ctx->flags);
74
75 return ctx;
76}
77
78bool
79dict_iterate_multiscope(struct dict_iterate_multiscope_context *ctx, const char **name, const char **value) {
80 i_assert(ctx != NULL);
81 if (ctx == NULL)
82 return false;
83
84 if (ctx->failed)
85 return false;
86
87 *name = NULL;
88 while (*name == NULL) {
89 bool ret = dict_iterate(ctx->dict_ctx, name, value);
90
91 /* we have no more shared/ keys and priv/ was also already iterated over */
92 if (!ret && !ctx->do_shared) {
93 return false;
94 }
95 /* no more priv/ keys, continue with shared/ */
96 else if (!ret && ctx->do_shared) {
97 if (dict_iterate_deinit(&ctx->dict_ctx) < 0) {
98 ctx->failed = true;
99 return false;
100 }
101
102 /* replace priv/ with shared/ */
103 str_delete(ctx->path, 0, sizeof(DICT_PATH_SHARED));
104 str_insert(ctx->path, 0, DICT_PATH_PRIVATE);
105
106 ctx->do_shared = false;
107
108 ctx->dict_ctx = dict_iterate_init(ctx->dict, str_c(ctx->path), ctx->flags);
109 if (ctx->dict_ctx == NULL) {
110 ctx->failed = true;
111 }
112
113 continue;
114 }
115 }
116
117 return true;
118}
119
120int
121dict_iterate_multiscope_deinit(struct dict_iterate_multiscope_context **ctx) {
122 i_assert(ctx != NULL);
123 if (ctx == NULL)
124 return -1;
125
126 i_assert(*ctx != NULL);
127 if (*ctx == NULL)
128 return -1;
129
130 int ret = (*ctx)->failed ? -1 : 0;
131
132 if (dict_iterate_deinit(&(*ctx)->dict_ctx) < 0)
133 ret = -1;
134
135 str_free(&(*ctx)->path);
136 i_free(*ctx);
137
138 return ret;
139}
0140
=== added file 'src/dict-ext.h'
--- src/dict-ext.h 1970-01-01 00:00:00 +0000
+++ src/dict-ext.h 2011-09-29 19:49:50 +0000
@@ -0,0 +1,58 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef DOVECOT_DICT_EXT_H
20#define DOVECOT_DICT_EXT_H
21
22#include "metadata-global.h"
23
24#include <stdbool.h>
25
26#include "dict.h"
27
28enum dict_scope {
29 DICT_SCOPE_SHARED,
30 DICT_SCOPE_PRIVATE,
31 DICT_SCOPE_INVALID
32};
33
34enum dict_scope
35dict_get_scope(const char *key)
36 ATTR_NONNULL(1);
37
38const char *
39dict_path_from_scope(enum dict_scope scope);
40
41struct dict_iterate_multiscope_context;
42
43enum dict_iterate_multiscope_flags {
44 DICT_ITERATE_MULTISCOPE_FLAG_MULTISCOPE = 0x1000
45};
46
47struct dict_iterate_multiscope_context *
48dict_iterate_multiscope_init(struct dict *dict, const char *path, enum dict_iterate_multiscope_flags flags)
49 ATTR_NONNULL(1,2);
50bool
51dict_iterate_multiscope(struct dict_iterate_multiscope_context *ctx, const char **name, const char **value)
52 ATTR_NONNULL(1,2,3);
53int
54dict_iterate_multiscope_deinit(struct dict_iterate_multiscope_context **ctx)
55 ATTR_NONNULL(1);
56
57
58#endif
059
=== modified file 'src/imap-annotatemore-plugin.c'
--- src/imap-annotatemore-plugin.c 2010-08-11 17:11:01 +0000
+++ src/imap-annotatemore-plugin.c 2011-09-29 19:49:50 +0000
@@ -1,20 +1,43 @@
1/* Copyright (C) 2008 by Intevation GmbH1/*
2 * Authors:2 Copyright (c) 2008 by Intevation GmbH / Bernhard Herzog <bh@intevation.de>
3 * Bernhard Herzog <bh@intevation.de>3 Copyright (c) 2010 by Dennis Schridde
4 *4
5 * This program is free software under the LGPL (>=v2.1)5 This file is part of dovecot-metadata.
6 * Read the file COPYING coming with the software for details.6
7 */7 dovecot-metadata is free software: you can redistribute it and/or modify
88 it under the terms of the GNU Lesser General Public License as published by
9#include "lib.h"9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11
12 dovecot-metadata is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
19*/
20#include "metadata-global.h"
21
10#include "str.h"22#include "str.h"
11#include "common.h"23#include "imap-common.h"
24#include "imap-client.h"
12#include "imap-quote.h"25#include "imap-quote.h"
26#include "mailbox-list.h"
1327
14#include <string.h>28#include <string.h>
1529
16#include "metadata-plugin.h"30#include "str-ext.h"
17#include "imap-annotatemore-plugin.h"31#include "metadata-entry-private.h"
32#include "metadata-backend.h"
33
34/* The IMAP Annotatemore plugin is a partial implementation of draft-daboo-imap-annotatemore-08 */
35
36const char *imap_annotatemore_plugin_version = DOVECOT_VERSION;
37const char *imap_annotatemore_plugin_dependencies[] = { "metadata", NULL };
38
39static struct module *imap_annotatemore_module;
40static void (*next_hook_client_created)(struct client **client);
1841
19enum attribute_properties {42enum attribute_properties {
20 ATTR_INVALID = 0x0001,43 ATTR_INVALID = 0x0001,
@@ -23,30 +46,32 @@
23 ATTR_BOTH = ATTR_PUBLIC | ATTR_PRIVATE,46 ATTR_BOTH = ATTR_PUBLIC | ATTR_PRIVATE,
24};47};
2548
49
26static bool validate_entry_name(struct client_command_context *cmd,50static bool validate_entry_name(struct client_command_context *cmd,
27 const char *entry)51 const char *entry)
28{52{
29 if (entry == NULL) {53 if (entry == NULL) {
30 client_send_tagline(cmd, "BAD Missing entry name.");54 client_send_command_error(cmd, "Missing entry name.");
31 return FALSE;55 return FALSE;
32 }56 }
3357
34 if (entry[0] != '/') {58 if (entry[0] != '/' && entry[0] != '*' && entry[0] != '%') {
35 client_send_tagline(cmd,59 client_send_command_error(cmd,
36 "BAD entry name must start with slash.");60 "Entry name must start with slash or be a glob.");
37 return FALSE;61 return FALSE;
38 }62 }
3963
40 return TRUE;64 return TRUE;
41}65}
4266
67
43static enum attribute_properties68static enum attribute_properties
44validate_attribute_name(struct client_command_context *cmd,69validate_attribute_name(struct client_command_context *cmd,
45 const char *attribute)70 const char *attribute)
46{71{
47 if (attribute == NULL) {72 if (attribute == NULL) {
48 client_send_tagline(cmd,73 client_send_command_error(cmd,
49 "BAD Missing or NIL attribute name.");74 "Missing or NIL attribute name.");
50 return ATTR_INVALID;75 return ATTR_INVALID;
51 }76 }
5277
@@ -56,64 +81,273 @@
56 return ATTR_PRIVATE;81 return ATTR_PRIVATE;
57 } else if (strcmp(attribute, "value") == 0) {82 } else if (strcmp(attribute, "value") == 0) {
58 return ATTR_BOTH;83 return ATTR_BOTH;
84 } else if (strchr(attribute, '*')) {
85 return ATTR_BOTH;
86 } else if (strchr(attribute, '%')) {
87 client_send_command_error(cmd, "'%' globs not supported.");
88 return ATTR_INVALID;
59 } else {89 } else {
60 client_send_tagline(cmd,90 client_send_command_error(cmd,
61 "BAD only 'value.shared' and"91 "Only 'value.shared' and 'value.priv' attributes"
62 " 'value.priv' attributes are"92 " are supported '.'");
63 " supported '.'");
64 return ATTR_INVALID;93 return ATTR_INVALID;
65 }94 }
66}95}
6796
6897
98static const char *
99entry_scopes[ENTRY_SCOPE_MAX] = {
100 "private/", /* ENTRY_SCOPE_PRIVATE */
101 "shared/" /* ENTRY_SCOPE_SHARED */
102};
103
104static const char *
105entry_types[ENTRY_TYPE_MAX] = {
106 "vendor/", /* ENTRY_TYPE_VENDOR */
107 "", /* ENTRY_TYPE_RFC */
108};
109
110static const char **
111entry_subtypes_rfc[ENTRY_SUBJECT_MAX] = {
112 (const char*[]){ // server
113 "comment",
114 "admin",
115 NULL
116 },
117 (const char*[]){ // mailbox
118 "comment",
119 NULL
120 }
121};
122
123
124/* validates that the part after /vendor conforms to the RFC */
125static ATTR_NONNULL(1)
126bool is_valid_annotatemore_vendor_name(const char *name) {
127 const char *lastslash = NULL, *lastcr = NULL;
128 int num_components = 2;
129
130 for (const char *c = name; *c != '\0'; c++) {
131 switch (*c) {
132 case '/':
133 // Two consecutive slashes, or a slash at the end are an error
134 if (lastslash == c-1 || *(c+1) == '\0') {
135 return false;
136 }
137 lastslash = c;
138 num_components++;
139 break;
140 case '*':
141 case '%':
142 return false;
143 case '\r':
144 lastcr = c;
145 break;
146 case '\n':
147 // line ending has to be CRLF
148 if (lastcr != c-1) {
149 return false;
150 }
151 break;
152 default:
153 break;
154 }
155 }
156
157 return num_components >= 4;
158}
159
160
161static ATTR_NONNULL(1)
162bool is_valid_annotatemore_subtype_name(const char *name, enum metadata_entry_subject subject) {
163 bool found_subtype = false;
164
165 i_assert(subject > 0 && subject < ENTRY_SUBJECT_MAX);
166
167 for (const char **subtype = entry_subtypes_rfc[subject]; *subtype != NULL; subtype++) {
168 size_t subtype_len = strlen(*subtype);
169
170 if (strncasecmp(name, *subtype, subtype_len) == 0) {
171 found_subtype = true;
172 }
173 }
174
175 return found_subtype;
176}
177
178
179/* sets entry->type and returns remaining string */
180static ATTR_NONNULL(1)
181enum metadata_entry_type
182parse_entry_type(const char **name, enum metadata_entry_subject subject) {
183 if (**name == '\0')
184 return ENTRY_TYPE_NONE;
185
186 for (int type = 0; type < ENTRY_TYPE_MAX; type++) {
187 size_t type_len = strlen(entry_types[type]);
188
189 if (strncasecmp(*name, entry_types[type], type_len) == 0) {
190 *name += type_len;
191
192 switch (type) {
193 case ENTRY_TYPE_RFC:
194 if (!is_valid_annotatemore_subtype_name(*name, subject))
195 return ENTRY_TYPE_MAX;
196 break;
197 case ENTRY_TYPE_VENDOR:
198 if (!is_valid_annotatemore_vendor_name(*name))
199 return ENTRY_TYPE_MAX;
200 break;
201 }
202
203 return type;
204 }
205 }
206
207 return ENTRY_TYPE_MAX;
208}
209
210
211static ATTR_NONNULL(2)
212const char *
213backend_name(enum metadata_entry_scope scope, const char *name) {
214 if (name == NULL)
215 return NULL;
216
217 string_t *backend_name = t_str_new(128);
218 str_append(backend_name, "/");
219 str_append(backend_name, entry_scopes[scope]);
220 str_append(backend_name, name);
221
222 return str_c(backend_name);
223}
224
225
226/* fill entry with data parsed from entry->full_name */
227static ATTR_NONNULL(3)
228struct metadata_entry *
229parse_entry(struct mailbox *box, enum metadata_entry_scope scope, const char *name, const char *value) {
230 if (name == NULL || *name == '\0' || *name++ != '/')
231 return NULL;
232
233 string_t *backend_name = t_str_new(128);
234 str_append(backend_name, "/");
235 str_append(backend_name, entry_scopes[scope]);
236 str_append(backend_name, name);
237
238 enum metadata_entry_type type = parse_entry_type(&name, box ? ENTRY_SUBJECT_MAILBOX : ENTRY_SUBJECT_SERVER);
239 if (type >= ENTRY_TYPE_MAX)
240 return NULL;
241
242 return metadata_entry_alloc(box, str_c(backend_name), value);
243}
244
245
69static void send_annotation_line(struct client_command_context *cmd,246static void send_annotation_line(struct client_command_context *cmd,
70 const char *mailbox,247 struct mailbox *box,
71 const char *entry,248 const char *entry_name,
72 const char *value,249 const char *value,
73 bool private)250 bool private)
74{251{
75 if (value != NULL) {252 if (value != NULL) {
76 string_t *str = t_str_new(128);253 const char *mailbox_name = mailbox_get_vname(box);
77 str_append(str, "* ANNOTATION ");254 const char *str = t_strdup_printf(
78 imap_quote_append_string(str, mailbox, FALSE);255 "* ANNOTATION %s %s (value.%s %s)",
79 str_append(str, " ");256 mailbox_name, entry_name, private ? "priv" : "shared", value
80 imap_quote_append_string(str, entry, FALSE);257 );
81 str_printfa(str, " (\"value.%s\" ",258
82 private ? "priv" : "shared");259 client_send_line(cmd->client, str);
83 imap_quote_append_string(str, value, FALSE);
84 str_append(str, ")");
85 client_send_line(cmd->client, str_c(str));
86 }260 }
87}261}
88262
89263
90static bool get_and_send_annotation(struct client_command_context *cmd,264static void get_and_send_annotation(struct client_command_context *cmd,
91 const char *mailbox,265 struct mailbox *box,
92 const char *entry,266 const char *entry_name,
93 enum attribute_properties scope)267 enum attribute_properties scope)
94{268{
95 const char *value;269 if (strchr(entry_name, '*')) {
96 bool success = TRUE;270 int entrylastchar = strlen(entry_name);
97271 if (entrylastchar > 0)
98 if ((scope & ATTR_PUBLIC) != 0) {272 entrylastchar--;
99 value = NULL;273
100 success = metadata_get_metadata_entry(cmd, mailbox, entry,274 /* We do not support more than one glob, and at no other location than the end */
101 &value, FALSE);275 if (strchr_num(entry_name, '*') == 1 && entry_name[entrylastchar] == '*') {
102 send_annotation_line(cmd, mailbox, entry, value, FALSE);276 const char *entrypattern = t_strdup_until(entry_name, &entry_name[entrylastchar]);
103 }277
104278 if ((scope & ATTR_PUBLIC) != 0) {
105 if (!success) {279 struct metadata_entry *entry = metadata_entry_alloc(box, backend_name(ENTRY_SCOPE_SHARED, entrypattern), NULL);
106 return FALSE;280
107 }281 struct metadata_iterate_context *iter = metadata_iterate_init(box, entry, METADATA_ITERATE_DEPTH_INF);
108282 while (metadata_iterate(iter, entry)) {
109 if ((scope & ATTR_PRIVATE) != 0) {283 const char *name = metadata_entry_get_name(entry) + strlen(entry_scopes[ENTRY_SCOPE_SHARED]);
110 value = NULL;284 const char *value = metadata_entry_get_value(entry);
111 success = metadata_get_metadata_entry(cmd, mailbox, entry,285
112 &value, TRUE);286 send_annotation_line(cmd, box, name, value, FALSE);
113 send_annotation_line(cmd, mailbox, entry, value, TRUE);287 }
114 }288 if (metadata_iterate_deinit(&iter) < 0) {
115289 client_send_tagline(cmd, "NO Iterating metadata failed.");
116 return success;290 return;
291 }
292 }
293
294 if ((scope & ATTR_PRIVATE) != 0) {
295 struct metadata_entry *entry = metadata_entry_alloc(box, backend_name(ENTRY_SCOPE_PRIVATE, entrypattern), NULL);
296
297 struct metadata_iterate_context *iter = metadata_iterate_init(box, entry, METADATA_ITERATE_DEPTH_INF);
298 while (metadata_iterate(iter, entry)) {
299 const char *name = metadata_entry_get_name(entry) + strlen(entry_scopes[ENTRY_SCOPE_SHARED]);
300 const char *value = metadata_entry_get_value(entry);
301
302 send_annotation_line(cmd, box, name, value, TRUE);
303 }
304 if (metadata_iterate_deinit(&iter) < 0) {
305 client_send_tagline(cmd, "NO Iterating metadata failed.");
306 return;
307 }
308 }
309 } else {
310 client_send_command_error(cmd, "'*' globs only supported at end of pattern.");
311 return;
312 }
313 } else if (strchr(entry_name, '%')) {
314 client_send_command_error(cmd, "'%' globs not supported.");
315 return;
316 } else {
317 if ((scope & ATTR_PUBLIC) != 0) {
318 struct metadata_entry *entry = metadata_entry_alloc(box, backend_name(ENTRY_SCOPE_SHARED, entry_name), NULL);
319 if (entry == NULL) {
320 client_send_tagline(cmd, "NO Allocating entry failed.");
321 return;
322 }
323
324 int success = metadata_get_entry(entry, cmd->client->user);
325 if (success < 0) {
326 client_send_tagline(cmd, "NO Getting entry failed.");
327 return;
328 }
329 else if (success > 0) {
330 send_annotation_line(cmd, box, entry->name, entry->value, FALSE);
331 }
332 }
333
334 if ((scope & ATTR_PRIVATE) != 0) {
335 struct metadata_entry *entry = metadata_entry_alloc(box, backend_name(ENTRY_SCOPE_PRIVATE, entry_name), NULL);
336 if (entry == NULL) {
337 client_send_tagline(cmd, "NO Allocating entry failed.");
338 return;
339 }
340
341 int success = metadata_get_entry(entry, cmd->client->user);
342 if (success < 0) {
343 client_send_tagline(cmd, "NO Getting entry failed.");
344 return;
345 }
346 else if (success > 0) {
347 send_annotation_line(cmd, box, entry->name, entry->value, TRUE);
348 }
349 }
350 }
117}351}
118352
119353
@@ -121,11 +355,22 @@
121 const struct imap_arg *attribute,355 const struct imap_arg *attribute,
122 const char **value_r)356 const char **value_r)
123{357{
124 const struct imap_arg *attrlist;358 const struct imap_arg *attrlist = NULL;
125359 unsigned int attrcount = 0;
126 if (IMAP_ARG_LIST_COUNT(attribute) == 1) {360
127 attrlist = IMAP_ARG_LIST_ARGS(attribute);361 if (!imap_arg_get_list_full(attribute, &attrlist, &attrcount)) {
128 *value_r = IMAP_ARG_STR(&attrlist[0]);362 // Actually this should never happen, since we first test args[1].type == IMAP_ARG_LIST ! */
363 i_error("metadata: got attributes of non-list type after confirming they were of correct type!");
364 client_send_command_error(cmd, "Attributes must be of list type.");
365 return FALSE;
366 }
367
368 if (attrcount == 1) {
369 if (!imap_arg_get_astring(&attrlist[0], value_r)) {
370 client_send_command_error(cmd,
371 "Value must be of string type.");
372 return FALSE;
373 }
129 return TRUE;374 return TRUE;
130 } else {375 } else {
131 client_send_tagline(cmd,376 client_send_tagline(cmd,
@@ -138,22 +383,26 @@
138static bool cmd_getannotation(struct client_command_context *cmd)383static bool cmd_getannotation(struct client_command_context *cmd)
139{384{
140 const struct imap_arg *args;385 const struct imap_arg *args;
141 const char *mailbox;386 const char *mailbox_name;
142 const char *entry;387 const char *entry_name;
143 const char *attribute;388 const char *attribute;
144 enum attribute_properties attribute_properties;389 enum attribute_properties attribute_properties;
145390
146 if (!client_read_args(cmd, 3, 0, &args))391 if (!client_read_args(cmd, 3, 0, &args))
147 return FALSE;392 return FALSE;
148393
149 mailbox = IMAP_ARG_STR(&args[0]);394 if (!imap_arg_get_astring(&args[0], &mailbox_name)) {
150 if (mailbox == NULL) {395 client_send_command_error(cmd,
151 client_send_tagline(cmd,396 "Mailbox name must be of string type.");
152 "BAD Missing mailbox name.");397 return TRUE;
398 }
399 if (mailbox_name == NULL) {
400 client_send_command_error(cmd,
401 "Missing mailbox name.");
153 return TRUE;402 return TRUE;
154 }403 }
155404
156 if (*mailbox == '\0') {405 if (*mailbox_name == '\0') {
157 client_send_tagline(cmd,406 client_send_tagline(cmd,
158 "NO Server annotations not yet"407 "NO Server annotations not yet"
159 " implemented.");408 " implemented.");
@@ -161,26 +410,77 @@
161 }410 }
162411
163 if (args[1].type == IMAP_ARG_LIST) {412 if (args[1].type == IMAP_ARG_LIST) {
164 if (!extract_single_value(cmd, &args[1], &entry))413 if (!extract_single_value(cmd, &args[1], &entry_name))
165 return TRUE;414 return TRUE;
166 } else415 } else {
167 entry = IMAP_ARG_STR(&args[1]);416 if (!imap_arg_get_astring(&args[1], &entry_name)) {
417 client_send_command_error(cmd,
418 "Entry name must be of string type.");
419 return TRUE;
420 }
421 }
168422
169 if (!validate_entry_name(cmd, entry))423 if (!validate_entry_name(cmd, entry_name))
170 return TRUE;424 return TRUE;
171425
172 if (args[2].type == IMAP_ARG_LIST) {426 if (args[2].type == IMAP_ARG_LIST) {
173 if (!extract_single_value(cmd, &args[2], &attribute))427 if (!extract_single_value(cmd, &args[2], &attribute))
174 return TRUE;428 return TRUE;
175 } else429 } else {
176 attribute = IMAP_ARG_STR(&args[2]);430 if (!imap_arg_get_astring(&args[2], &attribute)) {
431 client_send_command_error(cmd,
432 "Attribute must be of string type.");
433 return TRUE;
434 }
435 }
177436
178 attribute_properties = validate_attribute_name(cmd, attribute);437 attribute_properties = validate_attribute_name(cmd, attribute);
179 if (attribute_properties & ATTR_INVALID)438 if (attribute_properties & ATTR_INVALID)
180 return TRUE;439 return TRUE;
181440
182 if (get_and_send_annotation(cmd, mailbox, entry, attribute_properties))441 if (str_has_wildcards(mailbox_name)) {
183 client_send_tagline(cmd, "OK Completed.");442 for (const struct mail_namespace *ns = cmd->client->user->namespaces; ns != NULL; ns = ns->next) {
443 const struct mailbox_info *info = NULL;
444
445 struct mailbox_list_iterate_context *ctx = mailbox_list_iter_init(ns->list, mailbox_name, 0);
446 while ((info = mailbox_list_iter_next(ctx)) != NULL) {
447 i_debug("Getting info for mailbox '%s'", info->name);
448
449 struct mailbox *box = mailbox_alloc(ns->list, info->name, MAILBOX_FLAG_READONLY);
450 if (box == NULL) {
451 client_send_tagline(cmd, "NO Allocating mailbox failed.");
452 return TRUE;
453 }
454
455 get_and_send_annotation(cmd, box, entry_name, attribute_properties);
456
457 mailbox_free(&box);
458 }
459 if (mailbox_list_iter_deinit(&ctx) < 0) {
460 client_send_tagline(cmd, "NO Iterating mailboxes failed.");
461 }
462 }
463 } else {
464 struct mail_namespace *ns = mail_namespace_find(cmd->client->user->namespaces, &mailbox_name);
465 if (ns == NULL) {
466 client_send_tagline(cmd,
467 "NO Mailbox not found.");
468 return TRUE;
469 }
470
471 struct mailbox *box = mailbox_alloc(ns->list, mailbox_name, MAILBOX_FLAG_READONLY);
472 if (box == NULL) {
473 client_send_tagline(cmd,
474 "NO Allocating mailbox failed.");
475 return TRUE;
476 }
477
478 get_and_send_annotation(cmd, box, entry_name, attribute_properties);
479
480 mailbox_free(&box);
481 }
482
483 client_send_tagline(cmd, "OK Completed.");
184484
185 return TRUE;485 return TRUE;
186}486}
@@ -194,44 +494,50 @@
194 const struct imap_arg *pairs;494 const struct imap_arg *pairs;
195 unsigned int count;495 unsigned int count;
196496
197 if (attributes->type != IMAP_ARG_LIST) {497 if (!imap_arg_get_list_full(attributes, &pairs, &count)) {
198 client_send_tagline(cmd,498 client_send_command_error(cmd,
199 "BAD attributes parameter must be a list"499 "Attributes parameter must be a list"
200 " of attribute value pairs.");500 " of attribute value pairs.");
201 return FALSE;501 return FALSE;
202 }502 }
203503
204 count = IMAP_ARG_LIST_COUNT(attributes);
205 pairs = IMAP_ARG_LIST_ARGS(attributes);
206
207 if (count % 2 != 0) {504 if (count % 2 != 0) {
208 client_send_tagline(cmd,505 client_send_command_error(cmd,
209 "BAD list of attribute value pairs"506 "List of attribute value pairs"
210 " must have an even number of elements");507 " must have an even number of elements");
211 return FALSE;508 return FALSE;
212 }509 }
213510
214 if (count == 0) {511 if (count == 0) {
215 client_send_tagline(cmd,512 client_send_command_error(cmd,
216 "BAD list of attribute value pairs"513 "List of attribute value pairs"
217 " is empty");514 " is empty");
218 return FALSE;515 return FALSE;
219 }516 }
220517
221 if (count == 2) {518 if (count == 2) {
222 enum attribute_properties properties;519 enum attribute_properties properties;
223 properties = validate_attribute_name(cmd,520 const char *tmp;
224 IMAP_ARG_STR(&pairs[0]));521 if (!imap_arg_get_astring(&pairs[0], &tmp)) {
522 client_send_command_error(cmd,
523 "Attribute must be of string type.");
524 return FALSE;
525 }
526 properties = validate_attribute_name(cmd, tmp);
225 if ((properties & ATTR_INVALID) != 0)527 if ((properties & ATTR_INVALID) != 0)
226 return FALSE;528 return FALSE;
227 if ((properties & ATTR_BOTH) == ATTR_BOTH) {529 if ((properties & ATTR_BOTH) == ATTR_BOTH) {
228 client_send_tagline(cmd,530 client_send_command_error(cmd,
229 "BAD attribute must end in .priv"531 "Attribute must end in .priv"
230 " or .shared for SETANNOTATION");532 " or .shared for SETANNOTATION");
231 return FALSE;533 return FALSE;
232 }534 }
233535
234 *value_r = IMAP_ARG_STR(&pairs[1]);536 if (!imap_arg_get_astring(&pairs[1], value_r)) {
537 client_send_command_error(cmd,
538 "Value must be of string type.");
539 return FALSE;
540 }
235 *private_r = ((properties & ATTR_PRIVATE) != 0);541 *private_r = ((properties & ATTR_PRIVATE) != 0);
236 return TRUE;542 return TRUE;
237 }543 }
@@ -244,22 +550,25 @@
244static bool cmd_setannotation(struct client_command_context *cmd)550static bool cmd_setannotation(struct client_command_context *cmd)
245{551{
246 const struct imap_arg *args;552 const struct imap_arg *args;
247 const char *mailbox;553 const char *mailbox_name;
248 const char *entry;554 const char *entry_name;
249 const char *value;555 const char *value;
250 bool private;556 bool private;
251 bool success;
252557
253 if (!client_read_args(cmd, 3, 0, &args))558 if (!client_read_args(cmd, 3, 0, &args))
254 return FALSE;559 return FALSE;
255560
256 mailbox = IMAP_ARG_STR(&args[0]);561 if (!imap_arg_get_astring(&args[0], &mailbox_name)) {
257 if (mailbox == NULL) {562 client_send_command_error(cmd,
258 client_send_tagline(cmd,563 "Mailbox name must be of string type.");
259 "BAD Missing mailbox name.");564 return TRUE;
260 return TRUE;565 }
261 }566 if (mailbox_name == NULL) {
262 if (*mailbox == '\0') {567 client_send_command_error(cmd,
568 "Missing mailbox name.");
569 return TRUE;
570 }
571 if (*mailbox_name == '\0') {
263 client_send_tagline(cmd,572 client_send_tagline(cmd,
264 "NO Server annotations not yet"573 "NO Server annotations not yet"
265 " implemented.");574 " implemented.");
@@ -271,38 +580,84 @@
271 "NO Lists of entries not yet implemented.");580 "NO Lists of entries not yet implemented.");
272 return TRUE;581 return TRUE;
273 }582 }
274 entry = IMAP_ARG_STR(&args[1]);583 if (!imap_arg_get_astring(&args[1], &entry_name)) {
275 if (!validate_entry_name(cmd, entry))584 client_send_command_error(cmd,
585 "Entry name must be of string type.");
586 return TRUE;
587 }
588 if (entry_name == NULL) {
589 client_send_tagline(cmd, "NO Entry name is NULL.");
590 return true;
591 }
592
593 if (!validate_entry_name(cmd, entry_name))
276 return TRUE;594 return TRUE;
277595
278 if (!pair_extract_value(cmd, &args[2], &value, &private))596 if (!pair_extract_value(cmd, &args[2], &value, &private))
279 return TRUE;597 return TRUE;
280598
281 if (private && !metadata_private_allowed()) {599 struct mail_namespace *ns = mail_namespace_find(cmd->client->user->namespaces, &mailbox_name);
282 client_send_tagline(cmd,600 if (ns == NULL) {
283 "NO private annotations not supported.");601 client_send_tagline(cmd,
284 return TRUE;602 "NO Mailbox not found.");
285 }603 return TRUE;
286604 }
287 success = metadata_set_metadata_entry(cmd, mailbox, entry, value,605
288 private);606 struct mailbox *box = mailbox_alloc(ns->list, mailbox_name, MAILBOX_FLAG_READONLY);
289 if (success) {607 if (box == NULL) {
290 client_send_tagline(cmd, "OK Completed.");608 client_send_tagline(cmd,
291 }609 "NO Allocating mailbox failed.");
610 return TRUE;
611 }
612
613 struct metadata_entry *entry = parse_entry(box, private ? ENTRY_SCOPE_PRIVATE : ENTRY_SCOPE_SHARED, entry_name, value);
614 if (entry == NULL) {
615 client_send_tagline(cmd,
616 "NO Parsing entry failed.");
617 mailbox_free(&box);
618 return TRUE;
619 }
620
621 if (metadata_set_entry(entry, cmd->client->user) < 0) {
622 client_send_tagline(cmd,
623 "NO Setting entry failed.");
624 mailbox_free(&box);
625 return TRUE;
626 }
627
628 client_send_tagline(cmd, "OK Completed.");
629
630 mailbox_free(&box);
292631
293 return TRUE;632 return TRUE;
294}633}
295634
296635
297void imap_annotatemore_plugin_init(void)636static void imap_annotatemore_client_created(struct client **client)
637{
638 if (mail_user_is_plugin_loaded((*client)->user, imap_annotatemore_module))
639 str_append((*client)->capability_string, " ANNOTATEMORE");
640
641 if (next_hook_client_created != NULL)
642 next_hook_client_created(client);
643}
644
645
646void imap_annotatemore_plugin_init(struct module *module)
298{647{
299 command_register("GETANNOTATION", cmd_getannotation, 0);648 command_register("GETANNOTATION", cmd_getannotation, 0);
300 command_register("SETANNOTATION", cmd_setannotation, 0);649 command_register("SETANNOTATION", cmd_setannotation, 0);
301 str_append(capability_string, " ANNOTATEMORE");650
651 imap_annotatemore_module = module;
652 next_hook_client_created = hook_client_created;
653 hook_client_created = imap_annotatemore_client_created;
302}654}
303655
656
304void imap_annotatemore_plugin_deinit(void)657void imap_annotatemore_plugin_deinit(void)
305{658{
306 command_unregister("SETANNOTATION");659 command_unregister("SETANNOTATION");
307 command_unregister("GETANNOTATION");660 command_unregister("GETANNOTATION");
661
662 hook_client_created = next_hook_client_created;
308}663}
309664
=== removed file 'src/imap-annotatemore-plugin.h'
--- src/imap-annotatemore-plugin.h 2010-08-11 17:11:01 +0000
+++ src/imap-annotatemore-plugin.h 1970-01-01 00:00:00 +0000
@@ -1,7 +0,0 @@
1#ifndef __ANNOTATEMORE_PLUGIN
2#define __ANNOTATEMORE_PLUGIN
3
4void imap_annotatemore_plugin_init(void);
5void imap_annotatemore_plugin_deinit(void);
6
7#endif
80
=== added file 'src/imap-arg-ext.c'
--- src/imap-arg-ext.c 1970-01-01 00:00:00 +0000
+++ src/imap-arg-ext.c 2011-09-29 19:49:50 +0000
@@ -0,0 +1,83 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "imap-arg-ext.h"
20
21#include <stdlib.h>
22
23bool
24imap_arg_get_astringlist(const struct imap_arg *arg, const char ***list_r) {
25 i_assert(list_r != NULL);
26 if (list_r == NULL) {
27 return false;
28 }
29
30 if (*list_r != NULL) {
31 free(*list_r);
32 *list_r = NULL;
33 }
34
35 size_t list_size = 0;
36
37 if (arg->type == IMAP_ARG_LIST) { // entries
38 const struct imap_arg *arglist = NULL;
39 if (!imap_arg_get_list(arg, &arglist)) {
40 return false;
41 }
42
43 while (arglist[list_size].type != IMAP_ARG_EOL) {
44 const char *astring = NULL;
45 if (!imap_arg_get_astring(&arglist[list_size], &astring)) {
46 free(*list_r);
47 *list_r = NULL;
48 return false;
49 }
50
51 *list_r = realloc(*list_r, (list_size+2)*sizeof(*list_r));
52 if (*list_r == NULL) {
53 return false;
54 }
55
56 (*list_r)[list_size] = astring;
57
58 list_size++;
59 }
60 }
61 else if (IMAP_ARG_TYPE_IS_ASTRING(arg->type)) {
62 const char *astring = NULL;
63 if (!imap_arg_get_astring(arg, &astring)) {
64 return false;
65 }
66
67 *list_r = realloc(*list_r, (list_size+2)*sizeof(*list_r));
68 if (*list_r == NULL) {
69 return false;
70 }
71
72 (*list_r)[list_size] = astring;
73
74 list_size++;
75 }
76 else {
77 return false;
78 }
79
80 (*list_r)[list_size] = NULL;
81
82 return true;
83}
084
=== added file 'src/imap-arg-ext.h'
--- src/imap-arg-ext.h 1970-01-01 00:00:00 +0000
+++ src/imap-arg-ext.h 2011-09-29 19:49:50 +0000
@@ -0,0 +1,32 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef DOVECOT_IMAP_ARG_EXT_H
20#define DOVECOT_IMAP_ARG_EXT_H
21
22#include "metadata-global.h"
23
24#include <stdbool.h>
25
26#include "imap-arg.h"
27
28bool
29imap_arg_get_astringlist(const struct imap_arg *arg, const char ***list_r)
30 ATTR_WARN_UNUSED_RESULT;
31
32#endif
033
=== added file 'src/imap-metadata-plugin.c'
--- src/imap-metadata-plugin.c 1970-01-01 00:00:00 +0000
+++ src/imap-metadata-plugin.c 2011-09-29 19:49:50 +0000
@@ -0,0 +1,699 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "metadata-global.h"
20
21#include "imap-common.h"
22#include "imap-client.h"
23#include "imap-quote.h"
24
25#include <stdbool.h>
26#include <stdlib.h>
27
28#include "str-ext.h"
29#include "imap-arg-ext.h"
30#include "dict-ext.h"
31#include "metadata-entry.h"
32#include "metadata-backend.h"
33#include "metadata-mail-user-module-private.h"
34
35/* The IMAP Metadata plugin is an implementation of RFC 5464 */
36
37const char *imap_metadata_plugin_version = DOVECOT_VERSION;
38const char *imap_metadata_plugin_dependencies[] = { "metadata", NULL };
39
40static struct module *imap_metadata_module;
41static void (*next_hook_client_created)(struct client **client);
42
43
44static const char *
45entry_scopes[ENTRY_SCOPE_MAX] = {
46 "private/", /* ENTRY_SCOPE_PRIVATE */
47 "shared/" /* ENTRY_SCOPE_SHARED */
48};
49
50static const char *
51entry_types[ENTRY_TYPE_MAX] = {
52 "vendor/", /* ENTRY_TYPE_VENDOR */
53 "", /* ENTRY_TYPE_RFC */
54};
55
56static const char **
57entry_subtypes_rfc[ENTRY_SUBJECT_MAX] = {
58 (const char*[]){ // server
59 "comment",
60 "admin",
61 NULL
62 },
63 (const char*[]){ // mailbox
64 "comment",
65 NULL
66 }
67};
68
69
70enum getmetadata_option {
71 GETMETADATA_OPTION_MAXSIZE,
72 GETMEDADATA_OPTION_DEPTH
73};
74
75struct option_definition {
76 const char *name;
77 int num_values;
78 enum getmetadata_option option;
79};
80
81static
82struct option_definition
83getmetadata_options[] = {
84 {"maxsize", 1, GETMETADATA_OPTION_MAXSIZE},
85 {"depth", 1, GETMEDADATA_OPTION_DEPTH},
86 {NULL, 0, 0}
87};
88
89
90static
91struct option_definition *
92parse_getmetadata_option(const char *option) {
93 struct option_definition *optdef = getmetadata_options;
94 while (optdef->name != NULL) {
95 if (strcasecmp(optdef->name, option) == 0) {
96 return optdef;
97 }
98 optdef++;
99 }
100 return NULL;
101}
102
103
104static int
105parse_getmetadata_depth(const char *value) {
106 if (!str_is_numeric(value, '\0')) {
107 if (strcasecmp(value, "infinity"))
108 return METADATA_ITERATE_DEPTH_INF;
109
110 return -1;
111 }
112
113 char *end = NULL;
114 long int val = strtol(value, &end, 10);
115
116 if (end == value)
117 return -2;
118 if (val == LONG_MAX)
119 return -3;
120 if (val < 0)
121 return -4;
122
123 return val;
124}
125
126
127static int
128parse_getmetadata_maxsize(const char *value) {
129 if (!str_is_numeric(value, '\0'))
130 return -1;
131
132 char *end = NULL;
133 long int val = strtol(value, &end, 10);
134
135 if (end == value)
136 return -2;
137 if (val == LONG_MAX)
138 return -3;
139 if (val < 0)
140 return -4;
141
142 return val;
143}
144
145
146/* validates that the part after /vendor conforms to the RFC */
147static ATTR_NONNULL(1)
148bool
149is_valid_rfc5464_vendor_name(const char *name) {
150 const char *lastslash = NULL, *lastcr = NULL;
151 int num_components = 3; // "vendor/" already includes the slash of component No3
152
153 for (const char *c = name; *c != '\0'; c++) {
154 switch (*c) {
155 case '/':
156 // Two consecutive slashes, or a slash at the end are an error
157 if (lastslash == c-1 || *(c+1) == '\0') {
158 return false;
159 }
160 lastslash = c;
161 num_components++;
162 break;
163 case '*':
164 case '%':
165 return false;
166 case '\r':
167 lastcr = c;
168 break;
169 case '\n':
170 // line ending has to be CRLF
171 if (lastcr != c-1) {
172 return false;
173 }
174 break;
175 default:
176 break;
177 }
178 }
179
180 return num_components >= 4;
181}
182
183
184static ATTR_NONNULL(1)
185bool
186is_valid_rfc5464_subtype_name(const char *name, enum metadata_entry_subject subject) {
187 bool found_subtype = false;
188
189 i_assert(subject > 0 && subject < ENTRY_SUBJECT_MAX);
190
191 for (const char **subtype = entry_subtypes_rfc[subject]; *subtype != NULL; subtype++) {
192 size_t subtype_len = strlen(*subtype);
193
194 if (strncasecmp(name, *subtype, subtype_len) == 0
195 && name[subtype_len] == '\0') {
196 found_subtype = true;
197 }
198 }
199
200 return found_subtype;
201}
202
203
204/* sets entry->scope and returns remaining string */
205static ATTR_NONNULL(1)
206enum metadata_entry_scope
207parse_entry_scope(const char **name) {
208 for (int scope = 0; scope < ENTRY_SCOPE_MAX; scope++) {
209 size_t scope_len = strlen(entry_scopes[scope]);
210
211 if (strncasecmp(*name, entry_scopes[scope], scope_len) == 0) {
212 *name += scope_len;
213 return scope;
214 }
215 }
216
217 return ENTRY_SCOPE_MAX;
218}
219
220
221/* sets entry->type and returns remaining string */
222static ATTR_NONNULL(1)
223enum metadata_entry_type
224parse_entry_type(const char **name, enum metadata_entry_subject subject) {
225 for (int type = 0; type < ENTRY_TYPE_MAX; type++) {
226 size_t type_len = strlen(entry_types[type]);
227
228 if (strncasecmp(*name, entry_types[type], type_len) == 0) {
229 *name += type_len;
230
231 switch (type) {
232 case ENTRY_TYPE_RFC:
233 if (!is_valid_rfc5464_subtype_name(*name, subject))
234 return ENTRY_TYPE_MAX;
235 break;
236 case ENTRY_TYPE_VENDOR:
237 if (!is_valid_rfc5464_vendor_name(*name))
238 return ENTRY_TYPE_MAX;
239 break;
240 }
241
242 return type;
243 }
244 }
245
246 return ENTRY_TYPE_MAX;
247}
248
249
250/* fill entry with data parsed from entry->full_name */
251static ATTR_NONNULL(2)
252struct metadata_entry *
253parse_entry(struct mailbox *box, const char *name, const char *value) {
254 const char *name_tmp = name;
255 if (name_tmp == NULL || *name_tmp++ != '/')
256 return NULL;
257
258 enum metadata_entry_scope scope = parse_entry_scope(&name_tmp);
259 if (scope >= ENTRY_SCOPE_MAX)
260 return NULL;
261
262 enum metadata_entry_type type = parse_entry_type(&name_tmp, box ? ENTRY_SUBJECT_MAILBOX : ENTRY_SUBJECT_SERVER);
263 if (type >= ENTRY_TYPE_MAX)
264 return NULL;
265
266 return metadata_entry_alloc(box, name, value);
267}
268
269
270static int
271get_and_send_entry(struct client_command_context *cmd, struct mailbox *box, const char *name, int depth, int maxsize, int *longentries) {
272 if (str_has_wildcards(name)) {
273 client_send_tagline(cmd, "NO Wildcards in entry name not allowed.");
274 return -1;
275 }
276
277 if (depth == 0) {
278 struct metadata_entry *entry = parse_entry(box, name, NULL);
279 if (entry == NULL) {
280 client_send_tagline(cmd, "NO Parsing entry failed.");
281 return -1;
282 }
283
284 int success = metadata_get_entry(entry, cmd->client->user);
285 if (success < 0) {
286 i_assert(0);
287 client_send_tagline(cmd, "NO Getting entry failed.");
288 return -1;
289 }
290 else if (success > 0) {
291 const char *str = t_strdup_printf(
292 "* METADATA %s (%s %s)",
293 mailbox_get_vname(box), metadata_entry_get_name(entry), metadata_entry_get_value(entry)
294 );
295
296 return client_send_line(cmd->client, str);
297 }
298
299 return 0;
300 }
301
302 struct metadata_entry *entry = metadata_entry_alloc(box, name, NULL);
303
304 int num_entries = 0;
305
306 string_t *str = t_str_new(128);
307 str_append_printf(str, "* METADATA %s (", mailbox_get_vname(box));
308
309 struct metadata_iterate_context *iter = metadata_iterate_init(box, entry, depth);
310 while (metadata_iterate(iter, entry)) {
311 const char *name = metadata_entry_get_name(entry);
312 const char *value = metadata_entry_get_value(entry);
313
314 /* only respect maxsize if it is not 'undefined' */
315 if (maxsize > 0) {
316 size_t val_len = strlen(value);
317 if (val_len > maxsize && val_len > *longentries) {
318 *longentries = val_len;
319 continue;
320 }
321 }
322
323 str_append_printf(str, "%s %s ", name, value);
324
325 num_entries++;
326 }
327 if (metadata_iterate_deinit(&iter) < 0) {
328 client_send_tagline(cmd, "NO Iterating metadata failed.");
329 return -1;
330 }
331
332 str_append(str, ")");
333
334 if (num_entries > 0) {
335 return client_send_line(cmd->client, str_c(str));
336 }
337
338 return 0;
339}
340
341
342static bool
343cmd_getmetadata(struct client_command_context *cmd) {
344 const struct imap_arg *args;
345 int maxsize = 0, depth = 0;
346
347 if (!client_read_args(cmd, 0, 0, &args))
348 return false;
349
350 if (args[0].type == IMAP_ARG_LIST) { // options
351 const struct imap_arg *arglist = NULL;
352 if (!imap_arg_get_list(&args[0], &arglist)) {
353 client_send_command_error(cmd, "Cannot read options, list expected.");
354 return true;
355 }
356
357 while (arglist->type != IMAP_ARG_EOL) {
358 if (!IMAP_ARG_TYPE_IS_ASTRING(arglist->type)) {
359 client_send_command_error(cmd, "Option not a string.");
360 return true;
361 }
362
363 const char *option = NULL;
364 if (!imap_arg_get_astring(arglist, &option)){
365 client_send_command_error(cmd, "Cannot read option, string expected.");
366 return true;
367 }
368
369 struct option_definition *optdef = parse_getmetadata_option(option);
370 if (optdef == NULL) {
371 const char *estr = t_strdup_printf("Unknown option: %s.", option);
372 client_send_command_error(cmd, estr);
373 return true;
374 }
375
376 arglist++;
377
378 const char *values[optdef->num_values];
379 memset(values, 0, sizeof(*values) * optdef->num_values);
380
381 for (int i = 0; i < optdef->num_values; i++) {
382 if (!IMAP_ARG_TYPE_IS_ASTRING(arglist[i].type)) {
383 const char *estr = t_strdup_printf(
384 "Value %d/%d of %s not a string.",
385 i, optdef->num_values, option
386 );
387 client_send_command_error(cmd, estr);
388 return true;
389 }
390
391 if (!imap_arg_get_astring(&arglist[i], &values[i])){
392 const char *estr = t_strdup_printf(
393 "Cannot read value %d/%d of %s, string expected.",
394 i, optdef->num_values, option
395 );
396 client_send_command_error(cmd, estr);
397 return true;
398 }
399 }
400
401 switch (optdef->option) {
402 case GETMEDADATA_OPTION_DEPTH:
403 depth = parse_getmetadata_depth(values[0]);
404 if (depth < 0) {
405 client_send_command_error(cmd, "Value 1/1 of DEPTH is not numeric and positive or \"infinity\".");
406 return true;
407 }
408 break;
409 case GETMETADATA_OPTION_MAXSIZE:
410 maxsize = parse_getmetadata_maxsize(values[0]);
411 if (maxsize < 0) {
412 client_send_command_error(cmd, "Value 1/1 of MAXSIZE is not numeric and positive.");
413 return true;
414 }
415 break;
416 }
417
418 arglist += optdef->num_values;
419 }
420
421 args++;
422 }
423
424 if (!IMAP_ARG_TYPE_IS_ASTRING(args[0].type)) { // mailbox name
425 client_send_command_error(cmd, "Mailbox name not a string.");
426 return true;
427 }
428
429 const char *mailbox_name = NULL;
430 if (!imap_arg_get_astring(&args[0], &mailbox_name)){
431 client_send_command_error(cmd, "Cannot read mailbox name, string expected.");
432 return true;
433 }
434
435 if (mailbox_name == NULL) {
436 client_send_tagline(cmd, "NO Mailbox name is NULL.");
437 return true;
438 }
439
440 const char **entry_names = NULL;
441 if (!imap_arg_get_astringlist(&args[1], &entry_names)) {
442 client_send_command_error(cmd, "Cannot read entries, string or list of strings expected.");
443 return true;
444 }
445
446 int warn_longentries = 0;
447
448 if (str_has_wildcards(mailbox_name)) {
449 for (const struct mail_namespace *ns = cmd->client->user->namespaces; ns != NULL; ns = ns->next) {
450 const struct mailbox_info *info = NULL;
451
452 struct mailbox_list_iterate_context *ctx = mailbox_list_iter_init(ns->list, mailbox_name, 0);
453 while ((info = mailbox_list_iter_next(ctx)) != NULL) {
454 i_debug("Getting info for mailbox '%s'", info->name);
455
456 struct mailbox *box = mailbox_alloc(ns->list, info->name, MAILBOX_FLAG_READONLY);
457 if (box == NULL) {
458 client_send_tagline(cmd, "NO Allocating mailbox failed.");
459 return TRUE;
460 }
461
462 const char **entry_name = entry_names;
463 while (*entry_name != NULL) {
464 if (get_and_send_entry(cmd, box, *entry_name, depth, maxsize, &warn_longentries) < 0) {
465 /* get_and_send_entry outputs the response for the client, already */
466 mailbox_free(&box);
467 return true;
468 }
469
470 entry_name++;
471 }
472
473 mailbox_free(&box);
474 }
475
476 if (mailbox_list_iter_deinit(&ctx) < 0) {
477 client_send_tagline(cmd, "NO Iterating mailboxes failed.");
478 }
479 }
480 }
481 else {
482 struct mailbox *box = NULL;
483 /* empty mailbox_name -> box=NULL -> server scope */
484 if (*mailbox_name != '\0') {
485 struct mail_namespace *ns = mail_namespace_find(cmd->client->user->namespaces, &mailbox_name);
486 if (ns == NULL) {
487 client_send_tagline(cmd, "NO Mailbox not found.");
488 return true;
489 }
490
491 box = mailbox_alloc(ns->list, mailbox_name, MAILBOX_FLAG_READONLY);
492 if (box == NULL) {
493 client_send_tagline(cmd, "NO Allocating mailbox failed.");
494 return true;
495 }
496 }
497
498 const char **entry_name = entry_names;
499 while (*entry_name != NULL) {
500 if (get_and_send_entry(cmd, box, *entry_name, depth, maxsize, &warn_longentries) < 0) {
501 /* get_and_send_entry outputs the response for the client, already */
502 mailbox_free(&box);
503 return true;
504 }
505
506 entry_name++;
507 }
508
509 mailbox_free(&box);
510 }
511
512 free(entry_names);
513
514 const char *response;
515 if (warn_longentries > 0) {
516 response = t_strdup_printf("OK [METADATA LONGENTRIES %d] Completed.", warn_longentries);
517 }
518 else {
519 response = "OK Completed.";
520 }
521 client_send_tagline(cmd, response);
522
523 return true;
524}
525
526
527static bool
528cmd_setmetadata(struct client_command_context *cmd) {
529 const struct imap_arg *args;
530
531 if (!client_read_args(cmd, 0, 0, &args))
532 return false;
533
534 if (!IMAP_ARG_TYPE_IS_ASTRING(args[0].type)) { // mailbox name
535 client_send_command_error(cmd, "Mailbox name not a string.");
536 return true;
537 }
538
539 const char *mailbox_name = NULL;
540 if (!imap_arg_get_astring(&args[0], &mailbox_name)){
541 client_send_command_error(cmd, "Cannot read mailbox name, string expected.");
542 return true;
543 }
544
545 if (mailbox_name == NULL) {
546 client_send_tagline(cmd, "NO Mailbox name is NULL.");
547 return true;
548 }
549
550 struct mailbox *box = NULL;
551 /* empty name -> box=NULL -> server scope */
552 if (*mailbox_name != '\0') {
553 struct mail_namespace *ns = mail_namespace_find(cmd->client->user->namespaces, &mailbox_name);
554 if (ns == NULL) {
555 client_send_tagline(cmd, "NO Mailbox not found.");
556 return true;
557 }
558
559 box = mailbox_alloc(ns->list, mailbox_name, MAILBOX_FLAG_READONLY);
560 if (box == NULL) {
561 client_send_tagline(cmd, "NO Allocating mailbox failed.");
562 return true;
563 }
564 }
565
566 bool warn_maxsize = false, warn_toomany = false, warn_noprivate = false;
567
568 if (args[1].type == IMAP_ARG_LIST) { // entries
569 const struct imap_arg *arglist = NULL;
570 if (!imap_arg_get_list(&args[1], &arglist)){
571 client_send_command_error(cmd, "Cannot read entries, list expected.");
572 mailbox_free(&box);
573 return true;
574 }
575
576 while (arglist[0].type != IMAP_ARG_EOL) {
577 if (!IMAP_ARG_TYPE_IS_ASTRING(arglist[0].type)) {
578 client_send_command_error(cmd, "Entry name not a string.");
579 mailbox_free(&box);
580 return true;
581 }
582
583 const char *name = NULL;
584 if (!imap_arg_get_astring(&arglist[0], &name)){
585 client_send_command_error(cmd, "Cannot read entry name, string expected.");
586 mailbox_free(&box);
587 return true;
588 }
589
590 if (name == NULL) {
591 client_send_tagline(cmd, "NO Entry name is NULL.");
592 mailbox_free(&box);
593 return true;
594 }
595
596 const char *value;
597 if (arglist[1].type == IMAP_ARG_NIL) {
598 value = NULL;
599 }
600 else if (IMAP_ARG_TYPE_IS_ASTRING(arglist[1].type)) {
601 if (!imap_arg_get_astring(&arglist[1], &value)){
602 client_send_command_error(cmd, "Cannot read value, string expected.");
603 mailbox_free(&box);
604 return true;
605 }
606 }
607 else {
608 client_send_command_error(cmd, "Value not nil or string.");
609 mailbox_free(&box);
610 return true;
611 }
612
613 struct metadata_entry *entry = parse_entry(box, name, value);
614 if (entry == NULL) {
615 client_send_tagline(cmd, "NO Parsing entry failed.");
616 mailbox_free(&box);
617 return true;
618 }
619
620 int ret = metadata_set_entry(entry, cmd->client->user);
621 if (ret == -2) {
622 warn_maxsize = true;
623 }
624 else if (ret == -3) {
625 warn_toomany = true;
626 }
627 else if (ret < 0) {
628 client_send_tagline(cmd, "NO Setting entry failed.");
629 mailbox_free(&box);
630 return true;
631 }
632
633 /* skip this name/value pair */
634 arglist += 2;
635 }
636 }
637 else {
638 client_send_command_error(cmd, "Entries not a list.");
639 mailbox_free(&box);
640 return true;
641 }
642
643 const char *response;
644 if (warn_maxsize) {
645 struct metadata_mail_user *muser = METADATA_USER_CONTEXT(cmd->client->user);
646 if (muser == NULL) {
647 i_error("metadata: found NULL user, can't set metadata");
648 client_send_tagline(cmd, "NO Internal error.");
649 mailbox_free(&box);
650 return true;
651 }
652
653 response = t_strdup_printf("OK [METADATA MAXSIZE %d] Completed.", muser->set->maxsize);
654 }
655 else if (warn_toomany) {
656 response = t_strdup_printf("OK [METADATA TOOMANY] Completed.");
657 }
658 else if (warn_noprivate) {
659 response = t_strdup_printf("OK [METADATA NOPRIVATE] Completed.");
660 }
661 else {
662 response = "OK Completed.";
663 }
664 client_send_tagline(cmd, response);
665
666 mailbox_free(&box);
667
668 return true;
669}
670
671
672static void imap_metadata_client_created(struct client **client)
673{
674 if (mail_user_is_plugin_loaded((*client)->user, imap_metadata_module))
675 str_append((*client)->capability_string, " METADATA");
676
677 if (next_hook_client_created != NULL)
678 next_hook_client_created(client);
679}
680
681
682void imap_metadata_plugin_init(struct module *module)
683{
684 command_register("GETMETADATA", cmd_getmetadata, 0);
685 command_register("SETMETADATA", cmd_setmetadata, 0);
686
687 imap_metadata_module = module;
688 next_hook_client_created = hook_client_created;
689 hook_client_created = imap_metadata_client_created;
690}
691
692
693void imap_metadata_plugin_deinit(void)
694{
695 command_unregister("SETMETADATA");
696 command_unregister("GETMETADATA");
697
698 hook_client_created = next_hook_client_created;
699}
0700
=== added file 'src/mailbox-ext.c'
--- src/mailbox-ext.c 1970-01-01 00:00:00 +0000
+++ src/mailbox-ext.c 2011-09-29 19:49:50 +0000
@@ -0,0 +1,30 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "mailbox-ext.h"
20
21#include "hex-binary.h"
22
23const char *
24mailbox_get_guid_string(struct mailbox *box) {
25 uint8_t guid[MAIL_GUID_128_SIZE];
26 if (mailbox_get_guid(box, guid) < 0)
27 return NULL;
28
29 return binary_to_hex(guid, sizeof(guid));
30}
031
=== added file 'src/mailbox-ext.h'
--- src/mailbox-ext.h 1970-01-01 00:00:00 +0000
+++ src/mailbox-ext.h 2011-09-29 19:49:50 +0000
@@ -0,0 +1,30 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef DOVECOT_MAILBOX_EXT_H
20#define DOVECOT_MAILBOX_EXT_H
21
22#include "metadata-global.h"
23
24#include "mail-storage.h"
25
26const char *
27mailbox_get_guid_string(struct mailbox *box)
28 ATTR_NONNULL(1);
29
30#endif
031
=== added file 'src/metadata-backend.c'
--- src/metadata-backend.c 1970-01-01 00:00:00 +0000
+++ src/metadata-backend.c 2011-09-29 19:49:50 +0000
@@ -0,0 +1,296 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "metadata-backend.h"
20
21#include "dict.h"
22#include "mail-storage.h"
23
24#include <string.h>
25
26#include "str-ext.h"
27#include "dict-ext.h"
28#include "mailbox-ext.h"
29#include "metadata-entry-private.h"
30#include "metadata-mail-user-module-private.h"
31
32static const char *
33dict_subjects[ENTRY_SUBJECT_MAX+1] = {
34 "server/", /* ENTRY_SUBJECT_SERVER */
35 "mailbox/", /* ENTRY_SUBJECT_MAILBOX */
36 NULL
37};
38
39static const char*
40dictsubject_from_entry(struct metadata_entry *entry) {
41 switch (metadata_entry_get_subject(entry)) {
42 case ENTRY_SUBJECT_SERVER:
43 return dict_subjects[ENTRY_SUBJECT_SERVER];
44 case ENTRY_SUBJECT_MAILBOX:
45 return t_strconcat(dict_subjects[ENTRY_SUBJECT_MAILBOX], entry->mailbox_guid, "/", NULL);
46 case ENTRY_SUBJECT_MAX:
47 return NULL;
48 }
49
50 return NULL;
51}
52
53static const char*
54t_dictkey_from_entry(struct metadata_entry *entry) {
55 const char *subject = dictsubject_from_entry(entry);
56 if (subject == NULL)
57 return NULL;
58
59 const char *path_prefix = NULL;
60 switch (metadata_entry_get_scope(entry)) {
61 case ENTRY_SCOPE_SHARED:
62 path_prefix = DICT_PATH_SHARED;
63 break;
64 case ENTRY_SCOPE_PRIVATE:
65 path_prefix = DICT_PATH_PRIVATE;
66 break;
67 case ENTRY_SCOPE_INVALID:
68 case ENTRY_SCOPE_NONE:
69 return NULL;
70 }
71
72 // -> "prefix/" "subject/" "name"
73 return t_strconcat(path_prefix, subject, &entry->name[1], NULL);
74}
75
76static int
77count_entries(struct metadata_mail_user *muser) {
78 struct dict_iterate_context *iter;
79 const char *key;
80 const char *value;
81 int num = 0;
82
83 iter = dict_iterate_init(muser->dict, DICT_PATH_SHARED, DICT_ITERATE_FLAG_RECURSE);
84 while (dict_iterate(iter, &key, &value)) {
85 num++;
86 }
87 if (dict_iterate_deinit(&iter) < 0) {
88 i_error("metadata: dict iteration failed, can't count shared entries");
89 return -1;
90 }
91 iter = dict_iterate_init(muser->dict, DICT_PATH_PRIVATE, DICT_ITERATE_FLAG_RECURSE);
92 while (dict_iterate(iter, &key, &value)) {
93 num++;
94 }
95 if (dict_iterate_deinit(&iter) < 0) {
96 i_error("metadata: dict iteration failed, can't count private entries");
97 return -1;
98 }
99
100 return num;
101}
102
103int
104metadata_set_entry(struct metadata_entry *entry, struct mail_user *user) {
105 struct metadata_mail_user *muser = METADATA_USER_CONTEXT(user);
106 if (muser == NULL) {
107 i_error("metadata: found NULL user, can't set their metadata");
108 return -1;
109 }
110
111 if (!metadata_entry_is_valid(entry))
112 return -4;
113 if (strlen(entry->name) > muser->set->maxsize)
114 return -2;
115 if (count_entries(muser) > muser->set->maxentries)
116 return -3;
117
118 const char *key = t_dictkey_from_entry(entry);
119 if (key == NULL)
120 return -1;
121
122 struct dict_transaction_context *dt = dict_transaction_begin(muser->dict);
123
124 if (entry->value == NULL)
125 dict_unset(dt, key);
126 else
127 dict_set(dt, key, entry->value);
128
129 if (dict_transaction_commit(&dt) < 0) {
130 i_error("metadata: dict commit failed");
131 return -1;
132 }
133
134 return 0;
135}
136
137int
138metadata_get_entry(struct metadata_entry *entry, struct mail_user *user) {
139 struct metadata_mail_user *muser = METADATA_USER_CONTEXT(user);
140 if (muser == NULL) {
141 i_error("metadata: found NULL user, can't get their metadata");
142 return -1;
143 }
144
145 if (!metadata_entry_is_valid(entry))
146 return -4;
147
148 const char *key = t_dictkey_from_entry(entry);
149 if (key == NULL)
150 return -1;
151
152 return dict_lookup(muser->dict, user->pool, key, &entry->value);
153}
154
155struct metadata_iterate_context {
156 struct dict_iterate_multiscope_context *dict_ctx;
157 int depth;
158 bool failed;
159};
160
161struct metadata_iterate_context*
162metadata_iterate_init(struct mailbox *mailbox, struct metadata_entry *entry, int depth) {
163 struct metadata_iterate_context *ctx = i_new(struct metadata_iterate_context, 1);
164 memset(ctx, 0, sizeof(*ctx));
165
166 struct mail_storage *storage = mailbox_get_storage(mailbox);
167 struct mail_user *user = mail_storage_get_user(storage);
168 struct metadata_mail_user *muser = METADATA_USER_CONTEXT(user);
169 if (muser == NULL) {
170 i_error("metadata: found NULL user, can't iterate over their metadata");
171 ctx->failed = true;
172 return ctx;
173 }
174
175 i_assert(entry != NULL);
176 if (entry == NULL) {
177 ctx->failed = true;
178 return ctx;
179 }
180
181 const char *entry_name = metadata_entry_get_name(entry);
182 const int root_depth = strchr_num(entry_name, '/');
183 ctx->depth = root_depth + depth;
184
185 enum dict_iterate_multiscope_flags flags = 0;
186 if (depth != 0)
187 flags |= DICT_ITERATE_FLAG_RECURSE;
188
189 switch (metadata_entry_get_scope(entry)) {
190 case ENTRY_SCOPE_SHARED:
191 case ENTRY_SCOPE_PRIVATE:
192 break;
193 case ENTRY_SCOPE_INVALID:
194 ctx->failed = true;
195 return ctx;
196 case ENTRY_SCOPE_NONE:
197 flags |= DICT_ITERATE_MULTISCOPE_FLAG_MULTISCOPE;
198 break;
199 }
200
201 const char *key = t_dictkey_from_entry(entry);
202 if (key == NULL) {
203 ctx->failed = true;
204 return ctx;
205 }
206
207 ctx->dict_ctx = dict_iterate_multiscope_init(muser->dict, key, flags);
208 if (ctx->dict_ctx == NULL) {
209 ctx->failed = true;
210 return ctx;
211 }
212
213 return ctx;
214}
215
216static ATTR_NONNULL(2)
217const char *
218entry_name_from_dict_name(enum metadata_entry_subject subject, const char *dict_name) {
219 /* skip dict internal prefixes: priv/ or shared/ */
220 const char *name_after_scope = strchr(dict_name, '/');
221 if (name_after_scope == NULL) {
222 return NULL;
223 }
224
225 /* skip '/' */
226 name_after_scope++;
227
228 /* skip dict internal prefixes: server/ or mailbox/ */
229 const char *name_after_subject = strchr(name_after_scope, '/');
230 if (name_after_subject == NULL) {
231 return NULL;
232 }
233
234 /* do not skip '/', the name needs to start with a '/'! */
235
236 /* skip dict internal prefixes: <mailbox_guid>/ (for mailboxes only) */
237 if (subject == ENTRY_SUBJECT_MAILBOX) {
238 name_after_subject = strchr(name_after_subject+1, '/');
239 if (name_after_subject == NULL) {
240 return NULL;
241 }
242 }
243
244 return name_after_subject;
245}
246
247bool
248metadata_iterate(struct metadata_iterate_context *ctx, struct metadata_entry *entry) {
249 i_assert(ctx != NULL);
250 if (ctx == NULL)
251 return false;
252
253 if (ctx->failed)
254 return false;
255
256 entry->name = NULL;
257 while (entry->name == NULL) {
258 const char *dict_name = NULL, *dict_value = NULL;
259 if (!dict_iterate_multiscope(ctx->dict_ctx, &dict_name, &dict_value))
260 return false;
261
262 const char *entry_name = entry_name_from_dict_name(metadata_entry_get_subject(entry), dict_name);
263 if (entry_name == NULL) {
264 ctx->failed = true;
265 return false;
266 }
267
268 if (ctx->depth != METADATA_ITERATE_DEPTH_INF && strchr_num(entry_name, '/') > ctx->depth)
269 continue;
270
271 entry->name = i_strdup(entry_name);
272 entry->value = i_strdup(dict_value);
273 }
274
275 return true;
276}
277
278int
279metadata_iterate_deinit(struct metadata_iterate_context **ctx) {
280 i_assert(ctx != NULL);
281 if (ctx == NULL)
282 return -1;
283
284 i_assert(*ctx != NULL);
285 if (*ctx == NULL)
286 return -1;
287
288 int ret = (*ctx)->failed ? -1 : 0;
289
290 if ((*ctx)->dict_ctx != NULL && dict_iterate_multiscope_deinit(&(*ctx)->dict_ctx) < 0)
291 ret = -1;
292
293 i_free(*ctx);
294
295 return ret;
296}
0297
=== added file 'src/metadata-backend.h'
--- src/metadata-backend.h 1970-01-01 00:00:00 +0000
+++ src/metadata-backend.h 2011-09-29 19:49:50 +0000
@@ -0,0 +1,49 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef DOVECOT_METADATA_BACKEND_H
20#define DOVECOT_METADATA_BACKEND_H
21
22#include "metadata-global.h"
23
24#include <stdbool.h>
25
26#include "metadata-entry.h"
27
28int
29metadata_get_entry(struct metadata_entry *entry, struct mail_user *user)
30 ATTR_NONNULL(1,2);
31int
32metadata_set_entry(struct metadata_entry *entry, struct mail_user *user)
33 ATTR_NONNULL(1,2);
34
35const int METADATA_ITERATE_DEPTH_INF = INT_MAX;
36
37struct metadata_iterate_context;
38
39struct metadata_iterate_context *
40metadata_iterate_init(struct mailbox *mailbox, struct metadata_entry *entry, int depth)
41 ATTR_NONNULL(1,2);
42bool
43metadata_iterate(struct metadata_iterate_context *ctx, struct metadata_entry *entry)
44 ATTR_NONNULL(1,2);
45int
46metadata_iterate_deinit(struct metadata_iterate_context **ctx)
47 ATTR_NONNULL(1);
48
49#endif
050
=== added file 'src/metadata-entry-private.h'
--- src/metadata-entry-private.h 1970-01-01 00:00:00 +0000
+++ src/metadata-entry-private.h 2011-09-29 19:49:50 +0000
@@ -0,0 +1,32 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef DOVECOT_METADATA_ENTRY_PRIVATE_H
20#define DOVECOT_METADATA_ENTRY_PRIVATE_H
21
22#include "metadata-entry.h"
23
24struct metadata_entry {
25 enum metadata_entry_scope scope;
26 enum metadata_entry_type type;
27 const char *mailbox_guid; // implicitly defines the subject!
28 const char *name;
29 const char *value;
30};
31
32#endif
033
=== added file 'src/metadata-entry.c'
--- src/metadata-entry.c 1970-01-01 00:00:00 +0000
+++ src/metadata-entry.c 2011-09-29 19:49:50 +0000
@@ -0,0 +1,173 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "metadata-entry-private.h"
20
21#include <stdlib.h>
22#include <string.h>
23
24
25#include "mailbox-ext.h"
26
27static
28const char *
29entry_scopes[ENTRY_SCOPE_MAX+1] = {
30 "private/", /* ENTRY_SCOPE_PRIVATE */
31 "shared/", /* ENTRY_SCOPE_SHARED */
32 NULL
33};
34
35static
36enum metadata_entry_scope
37parse_scope(const char *name) {
38 if (name == NULL)
39 return ENTRY_SCOPE_INVALID;
40
41 /* scope must be empty or begin with '/' */
42 if (name[0] != '/') {
43 if (name[0] == '\0')
44 return ENTRY_SCOPE_NONE;
45
46 return ENTRY_SCOPE_INVALID;
47 }
48
49 /* skip '/' */
50 name++;
51
52 /* scope is the first component */
53 for (int i = 0; i < ENTRY_SCOPE_MAX; i++) {
54 if (strncasecmp(entry_scopes[i], name, strlen(entry_scopes[i])) == 0)
55 return i;
56 }
57
58 return ENTRY_SCOPE_INVALID;
59}
60
61static
62const char *
63entry_types[ENTRY_TYPE_MAX+1] = {
64 "vendor/", /* ENTRY_TYPE_VENDOR */
65 "", /* ENTRY_TYPE_RFC */
66 NULL
67};
68
69static
70enum metadata_entry_type
71parse_type(const char *name) {
72 /* lazy evaluation of scope existance */
73 if (name == NULL || *name++ != '/')
74 return ENTRY_TYPE_INVALID;
75
76 /* type is the second component */
77 name = strchr(name, '/');
78 if (name++ == NULL)
79 return ENTRY_TYPE_NONE;
80
81 for (int i = 0; i < ENTRY_TYPE_MAX; i++) {
82 if (strncasecmp(entry_types[i], name, strlen(entry_types[i])) == 0)
83 return i;
84 }
85
86 return ENTRY_TYPE_INVALID;
87}
88
89/* create entry on mailbox with name=value */
90struct metadata_entry *
91metadata_entry_alloc(struct mailbox *mailbox, const char *name, const char *value) {
92 struct metadata_entry *entry = i_new(struct metadata_entry, 1);
93 memset(entry, 0, sizeof(*entry));
94
95 if (mailbox != NULL) {
96 const char *mailbox_guid = mailbox_get_guid_string(mailbox);
97 if (mailbox_guid != NULL)
98 entry->mailbox_guid = strdup(mailbox_guid);
99 }
100
101 entry->scope = parse_scope(name);
102 entry->type = parse_type(name);
103 if (metadata_entry_is_valid(entry)) {
104 if (name != NULL)
105 entry->name = strdup(name);
106 if (value != NULL)
107 entry->value = strdup(value);
108 }
109
110 return entry;
111}
112
113/* free structures allocated for entry and invalidate it */
114void
115entry_free(struct metadata_entry *entry) {
116 free((char*)entry->value);
117 free((char*)entry->name);
118 memset(entry, 0, sizeof(*entry));
119}
120
121bool
122metadata_entry_is_valid(struct metadata_entry *entry) {
123 i_assert(entry != NULL);
124 if (entry == NULL)
125 return false;
126
127 return entry->scope < ENTRY_SCOPE_MAX && entry->type < ENTRY_TYPE_MAX;
128}
129
130const char *
131metadata_entry_get_name(struct metadata_entry *entry) {
132 i_assert(entry != NULL);
133 if (entry == NULL)
134 return NULL;
135
136 return entry->name;
137}
138
139const char *
140metadata_entry_get_value(struct metadata_entry *entry) {
141 i_assert(entry != NULL);
142 if (entry == NULL)
143 return NULL;
144
145 return entry->value;
146}
147
148enum metadata_entry_subject
149metadata_entry_get_subject(struct metadata_entry *entry) {
150 i_assert(entry != NULL);
151 if (entry == NULL)
152 return ENTRY_SUBJECT_INVALID;
153
154 return entry->mailbox_guid ? ENTRY_SUBJECT_MAILBOX : ENTRY_SUBJECT_SERVER;
155}
156
157enum metadata_entry_scope
158metadata_entry_get_scope(struct metadata_entry *entry) {
159 i_assert(entry != NULL);
160 if (entry == NULL)
161 return ENTRY_SCOPE_INVALID;
162
163 return entry->scope;
164}
165
166enum metadata_entry_type
167metadata_entry_get_type(struct metadata_entry *entry) {
168 i_assert(entry != NULL);
169 if (entry == NULL)
170 return ENTRY_TYPE_INVALID;
171
172 return entry->type;
173}
0174
=== added file 'src/metadata-entry.h'
--- src/metadata-entry.h 1970-01-01 00:00:00 +0000
+++ src/metadata-entry.h 2011-09-29 19:49:50 +0000
@@ -0,0 +1,83 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef DOVECOT_METADATA_ENTRY_H
20#define DOVECOT_METADATA_ENTRY_H
21
22#include "metadata-global.h"
23
24#include <stdbool.h>
25
26#include "mail-storage.h"
27
28enum metadata_entry_subject {
29 ENTRY_SUBJECT_SERVER = 0,
30 ENTRY_SUBJECT_MAILBOX = 1,
31 ENTRY_SUBJECT_MAX,
32 ENTRY_SUBJECT_INVALID = ENTRY_SUBJECT_MAX
33};
34
35enum metadata_entry_scope {
36 ENTRY_SCOPE_PRIVATE = 0,
37 ENTRY_SCOPE_SHARED = 1,
38 ENTRY_SCOPE_MAX,
39 ENTRY_SCOPE_INVALID = ENTRY_SCOPE_MAX,
40 ENTRY_SCOPE_NONE
41};
42
43enum metadata_entry_type {
44 ENTRY_TYPE_VENDOR = 0,
45 ENTRY_TYPE_RFC = 1,
46 ENTRY_TYPE_MAX,
47 ENTRY_TYPE_INVALID = ENTRY_TYPE_MAX,
48 ENTRY_TYPE_NONE
49};
50
51struct metadata_entry *
52metadata_entry_alloc(struct mailbox *mailbox, const char *name, const char *value)
53 ATTR_NONNULL(2);
54
55void
56metadata_entry_free(struct metadata_entry *entry)
57 ATTR_NONNULL(1);
58
59bool
60metadata_entry_is_valid(struct metadata_entry *entry)
61 ATTR_NONNULL(1);
62
63const char *
64metadata_entry_get_name(struct metadata_entry *entry)
65 ATTR_NONNULL(1);
66
67const char *
68metadata_entry_get_value(struct metadata_entry *entry)
69 ATTR_NONNULL(1);
70
71enum metadata_entry_subject
72metadata_entry_get_subject(struct metadata_entry *entry)
73 ATTR_NONNULL(1);
74
75enum metadata_entry_scope
76metadata_entry_get_scope(struct metadata_entry *entry)
77 ATTR_NONNULL(1);
78
79enum metadata_entry_type
80metadata_entry_get_type(struct metadata_entry *entry)
81 ATTR_NONNULL(1);
82
83#endif
084
=== added file 'src/metadata-global.h'
--- src/metadata-global.h 1970-01-01 00:00:00 +0000
+++ src/metadata-global.h 2011-09-29 19:49:50 +0000
@@ -0,0 +1,52 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef DOVECOT_METADATA_GLOBAL_H
20#define DOVECOT_METADATA_GLOBAL_H
21
22/*
23 the dovecot include system needs its own special care:
24 * include lib.h first, always
25 * reset all the symbols we use by including metadata-config.h.in,
26 since dovecot's internal config.h leaks through their public headers
27*/
28#include "lib.h"
29
30#define ATTR_NONNULL(...) __attribute__((__nonnull__(__VA_ARGS__)))
31
32/*
33 = error values =
34 < 0 is always an error
35 0 means success or no-more-data
36 > 0 means more-data-available
37
38 = return types =
39 void can never have errors
40
41 == logical functions ==
42 return bool and cannot have errors
43
44 == functions returning pointers ==
45 NULL is an error, everything else not
46
47 == data handling functions ==
48 bool is a continous function which cannot have errors
49 int is any function which can have errors
50*/
51
52#endif
053
=== added file 'src/metadata-mail-storage-module.c'
--- src/metadata-mail-storage-module.c 1970-01-01 00:00:00 +0000
+++ src/metadata-mail-storage-module.c 2011-09-29 19:49:50 +0000
@@ -0,0 +1,92 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "metadata-mail-storage-module.h"
20
21#include "dict.h"
22#include "module-context.h"
23#include "mail-storage-private.h"
24
25#include "mailbox-ext.h"
26#include "metadata-mail-user-module-private.h"
27
28#define METADATA_MAILBOX_CONTEXT(obj) MODULE_CONTEXT(obj, metadata_mailbox_module)
29
30static MODULE_CONTEXT_DEFINE_INIT(metadata_mailbox_module, &mail_storage_module_register);
31
32static int
33metadata_mailbox_delete(struct mailbox *box) {
34 union mailbox_module_context *mbox = METADATA_MAILBOX_CONTEXT(box);
35 if (mbox == NULL) {
36 i_error("metadata: found NULL mailbox, can't delete it");
37 return -1;
38 }
39
40 struct mail_storage *storage = mailbox_get_storage(box);
41 struct mail_user *user = mail_storage_get_user(storage);
42 struct metadata_mail_user *muser = METADATA_USER_CONTEXT(user);
43 if (muser == NULL) {
44 i_error("metadata: found NULL user, can't delete mailbox");
45 return -1;
46 }
47
48 struct dict_transaction_context *dt = dict_transaction_begin(muser->dict);
49
50 const char *name;
51 const char *value;
52
53 const char *skey = t_strconcat(DICT_PATH_SHARED, mailbox_get_guid_string(box), NULL);
54
55 struct dict_iterate_context *siter = dict_iterate_init(muser->dict, skey, DICT_ITERATE_FLAG_RECURSE);
56 while (dict_iterate(siter, &name, &value)) {
57 dict_unset(dt, name);
58 }
59 if (dict_iterate_deinit(&siter) < 0) {
60 i_error("metadata: dict iteration (" DICT_PATH_SHARED ") failed, can't update dict");
61 return -1;
62 }
63
64 const char *pkey = t_strconcat(DICT_PATH_PRIVATE, mailbox_get_guid_string(box), NULL);
65
66 struct dict_iterate_context *piter = dict_iterate_init(muser->dict, pkey, DICT_ITERATE_FLAG_RECURSE);
67 while (dict_iterate(piter, &name, &value)) {
68 dict_unset(dt, name);
69 }
70 if (dict_iterate_deinit(&piter) < 0) {
71 i_error("metadata: dict iteration (" DICT_PATH_PRIVATE ") failed, can't update dict");
72 return -1;
73 }
74
75 int super_ret = mbox->super.delete(box);
76 if (super_ret < 0) {
77 dict_transaction_rollback(&dt);
78 } else if (dict_transaction_commit(&dt) < 0) {
79 i_error("metadata: dict commit failed");
80 return -1;
81 }
82
83 return super_ret;
84}
85
86void metadata_mailbox_allocated(struct mailbox *box) {
87 union mailbox_module_context *mbox = p_new(box->pool, union mailbox_module_context, 1);
88 mbox->super = box->v;
89 box->v.delete = metadata_mailbox_delete;
90
91 MODULE_CONTEXT_SET_SELF(box, metadata_mailbox_module, mbox);
92}
093
=== added file 'src/metadata-mail-storage-module.h'
--- src/metadata-mail-storage-module.h 1970-01-01 00:00:00 +0000
+++ src/metadata-mail-storage-module.h 2011-09-29 19:49:50 +0000
@@ -0,0 +1,28 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef DOVECOT_METADATA_MAIL_STORAGE_MODULE_H
20#define DOVECOT_METADATA_MAIL_STORAGE_MODULE_H
21
22#include "metadata-global.h"
23
24#include "mail-storage.h"
25
26void metadata_mailbox_allocated(struct mailbox *box);
27
28#endif
029
=== added file 'src/metadata-mail-user-module-private.h'
--- src/metadata-mail-user-module-private.h 1970-01-01 00:00:00 +0000
+++ src/metadata-mail-user-module-private.h 2011-09-29 19:49:50 +0000
@@ -0,0 +1,40 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef DOVECOT_METADATA_MAIL_USER_MODULE_PRIVATE_H
20#define DOVECOT_METADATA_MAIL_USER_MODULE_PRIVATE_H
21
22#include "metadata-mail-user-module.h"
23
24#include "dict.h"
25#include "module-context.h"
26#include "mail-user.h"
27
28#include "metadata-settings.h"
29
30#define METADATA_USER_CONTEXT(obj) MODULE_CONTEXT(obj, metadata_mail_user_module)
31
32extern MODULE_CONTEXT_DEFINE(metadata_mail_user_module, &mail_user_module_register);
33
34struct metadata_mail_user {
35 union mail_user_module_context module_ctx;
36 struct dict *dict;
37 struct metadata_settings *set;
38};
39
40#endif
041
=== added file 'src/metadata-mail-user-module.c'
--- src/metadata-mail-user-module.c 1970-01-01 00:00:00 +0000
+++ src/metadata-mail-user-module.c 2011-09-29 19:49:50 +0000
@@ -0,0 +1,56 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "metadata-mail-user-module-private.h"
20
21struct metadata_mail_user_module metadata_mail_user_module =
22 MODULE_CONTEXT_INIT(&mail_user_module_register);
23
24static void metadata_mail_user_deinit(struct mail_user *user) {
25 struct metadata_mail_user *muser = METADATA_USER_CONTEXT(user);
26 if (muser == NULL) {
27 i_error("metadata: found NULL user, can't deinit it");
28 return;
29 }
30
31 if (muser->dict != NULL) {
32 dict_deinit(&muser->dict);
33 }
34
35 if (muser->set != NULL) {
36 metadata_settings_deinit(&muser->set);
37 }
38
39 return muser->module_ctx.super.deinit(user);
40}
41
42void metadata_mail_user_created(struct mail_user *user) {
43 struct metadata_mail_user *muser = p_new(user->pool, struct metadata_mail_user, 1);
44 muser->module_ctx.super = user->v;
45 user->v.deinit = metadata_mail_user_deinit;
46
47 metadata_settings_init(&muser->set, user);
48
49 if (muser->set->dict_uri != NULL) {
50 muser->dict = dict_init(muser->set->dict_uri, DICT_DATA_TYPE_STRING, user->username, user->set->base_dir);
51 if (muser->dict == NULL)
52 i_error("metadata: dict_init(%s) failed", muser->set->dict_uri);
53 }
54
55 MODULE_CONTEXT_SET(user, metadata_mail_user_module, muser);
56}
057
=== added file 'src/metadata-mail-user-module.h'
--- src/metadata-mail-user-module.h 1970-01-01 00:00:00 +0000
+++ src/metadata-mail-user-module.h 2011-09-29 19:49:50 +0000
@@ -0,0 +1,28 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef DOVECOT_METADATA_MAIL_USER_MODULE_H
20#define DOVECOT_METADATA_MAIL_USER_MODULE_H
21
22#include "metadata-global.h"
23
24#include "mail-user.h"
25
26void metadata_mail_user_created(struct mail_user *user);
27
28#endif
029
=== modified file 'src/metadata-plugin.c'
--- src/metadata-plugin.c 2010-08-11 17:11:01 +0000
+++ src/metadata-plugin.c 2011-09-29 19:49:50 +0000
@@ -1,352 +1,41 @@
1/* Copyright (C) 2008, 2009 by Intevation GmbH1/*
2 * Authors:2 Copyright (c) 2010 by Dennis Schridde
3 * Bernhard Herzog <bh@intevation.de>3
4 *4 This file is part of dovecot-metadata.
5 * This program is free software under the LGPL (>=v2.1)5
6 * Read the file COPYING coming with the software for details.6 dovecot-metadata is free software: you can redistribute it and/or modify
7 */7 it under the terms of the GNU Lesser General Public License as published by
88 the Free Software Foundation, either version 3 of the License, or
9#include "lib.h"9 (at your option) any later version.
10#include "dict.h"10
11#include "common.h"11 dovecot-metadata is distributed in the hope that it will be useful,
12#include "commands-util.h"12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13#include "module-context.h"13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14#include "mailbox-list-private.h"14 GNU Lesser General Public License for more details.
1515
16#include "metadata-plugin.h"16 You should have received a copy of the GNU Lesser General Public License
1717 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18#include <stdlib.h>18*/
1919#include "metadata-global.h"
2020
21#define METADATA_LIST_CONTEXT(obj) \21#include "mail-storage-hooks.h"
22 MODULE_CONTEXT((obj), metadata_mailbox_list_module)22
23static MODULE_CONTEXT_DEFINE_INIT(metadata_mailbox_list_module,23#include "metadata-mail-user-module.h"
24 &mailbox_list_module_register);24#include "metadata-mail-storage-module.h"
2525
26struct metadata_mailbox_list {26const char *metadata_plugin_version = DOVECOT_VERSION;
27 union mailbox_list_module_context module_ctx;27
28static struct mail_storage_hooks metadata_mail_storage_hooks = {
29 .mail_user_created = metadata_mail_user_created,
30 .mailbox_allocated = metadata_mailbox_allocated
28};31};
2932
30static void (*metadata_next_hook_mailbox_list_created)(struct mailbox_list *);33void metadata_plugin_init(struct module *module)
3134{
3235 mail_storage_hooks_add(module, &metadata_mail_storage_hooks);
33static struct dict *metadata_dict;
34static bool metadata_allow_private = FALSE;
35static bool metadata_debug = FALSE;
36
37
38bool metadata_private_allowed(void) {
39 return metadata_allow_private;
40}
41
42
43static const char *mailbox_key_path(struct mailbox_list *list,
44 const char *mailboxname)
45{
46 return mailbox_list_get_path(list, mailboxname,
47 MAILBOX_LIST_PATH_TYPE_MAILBOX);
48}
49
50
51static const char *create_dict_key(struct mail_storage *storage,
52 const char *mailboxname, const char *entry,
53 bool private)
54{
55 return t_strconcat(private ? DICT_PATH_PRIVATE : DICT_PATH_SHARED,
56 mailbox_key_path(mail_storage_get_list(storage),
57 mailboxname),
58 "/", entry, NULL);
59}
60
61static const char *renamed_key(const char *oldkey, const char *oldpath,
62 const char *newpath)
63{
64 const char *first_slash;
65 size_t oldlen;
66
67 first_slash = strchr(oldkey, '/');
68 if (!first_slash)
69 return NULL;
70
71 oldlen = strlen(oldpath);
72 if (strncmp(first_slash + 1, oldpath, oldlen) == 0) {
73 return t_strconcat(t_strdup_until(oldkey, first_slash + 1),
74 newpath,
75 first_slash + 1 + oldlen,
76 NULL);
77 }
78
79 return NULL;
80}
81
82static void rename_metadata_of_mailbox(struct dict_transaction_context *dt,
83 struct mailbox_list *list,
84 const char *oldname,
85 const char *newname)
86{
87 struct dict_iterate_context *iter;
88 const char *oldpath;
89 const char *newpath;
90 const char *key;
91 const char *value;
92 const char *newkey;
93
94 oldpath = mailbox_key_path(list, oldname);
95 newpath = mailbox_key_path(list, newname);
96
97 iter = dict_iterate_init(metadata_dict,
98 t_strconcat(DICT_PATH_SHARED, oldpath, NULL),
99 DICT_ITERATE_FLAG_RECURSE
100 | DICT_ITERATE_FLAG_SORT_BY_KEY);
101 while (dict_iterate(iter, &key, &value) > 0) {
102 T_BEGIN {
103 newkey = renamed_key(key, oldpath, newpath);
104 dict_set(dt, newkey, value);
105 dict_unset(dt, key);
106 } T_END;
107 }
108 dict_iterate_deinit(&iter);
109}
110
111static int rename_metadata_of_children(struct dict_transaction_context *dt,
112 struct mailbox_list *list,
113 const char *oldname,
114 const char *newname)
115{
116 struct mailbox_list_iterate_context *iter;
117 const struct mailbox_info *info;
118 const char *pattern;
119 size_t oldnamelen;
120 int ret;
121
122 ret = 0;
123
124 oldnamelen = strlen(oldname);
125 pattern = t_strdup_printf("%s%c*", oldname,
126 mailbox_list_get_hierarchy_sep(list));
127 iter = mailbox_list_iter_init(list, pattern,
128 MAILBOX_LIST_ITER_RETURN_NO_FLAGS);
129 while ((info = mailbox_list_iter_next(iter)) != NULL) {
130 const char *renamed;
131
132 /* verify that the prefix matches, otherwise we could have
133 problems with mailbox names containing '%' and '*' chars */
134 if (strncmp(info->name, oldname, oldnamelen) == 0 &&
135 info->name[oldnamelen] ==
136 mailbox_list_get_hierarchy_sep(list)) {
137 T_BEGIN {
138 renamed = t_strconcat(newname,
139 info->name + oldnamelen,
140 NULL);
141 rename_metadata_of_mailbox(dt, list,
142 info->name, renamed);
143 } T_END;
144 }
145 }
146
147 if (mailbox_list_iter_deinit(&iter) < 0) {
148 ret = -1;
149 }
150
151 return ret;
152}
153
154static int metadata_mailbox_list_rename(struct mailbox_list *list,
155 const char *oldname,
156 const char *newname)
157{
158 struct metadata_mailbox_list *mlist = METADATA_LIST_CONTEXT(list);
159 struct dict_transaction_context *dt;
160 int success;
161
162 dt = dict_transaction_begin(metadata_dict);
163
164 T_BEGIN {
165 rename_metadata_of_mailbox(dt, list, oldname, newname);
166 rename_metadata_of_children(dt, list, oldname, newname);
167 } T_END;
168
169
170 success = mlist->module_ctx.super.rename_mailbox(list, oldname,
171 newname);
172 if (success < 0) {
173 dict_transaction_rollback(&dt);
174 } else {
175 if (dict_transaction_commit(&dt) < 0) {
176 i_error("metadata_mailbox_list_rename:"
177 " could not update metadata dict");
178 }
179 }
180
181 return success;
182}
183
184static int metadata_mailbox_list_delete(struct mailbox_list *list,
185 const char *name)
186{
187 struct metadata_mailbox_list *mlist = METADATA_LIST_CONTEXT(list);
188 struct dict_transaction_context *dt;
189 struct dict_iterate_context *iter;
190 const char *keypath;
191 const char *key;
192 const char *value;
193 int success;
194
195 keypath = mailbox_key_path(list, name);
196
197 dt = dict_transaction_begin(metadata_dict);
198 iter = dict_iterate_init(metadata_dict, t_strconcat(DICT_PATH_SHARED,
199 keypath, NULL),
200 DICT_ITERATE_FLAG_RECURSE
201 | DICT_ITERATE_FLAG_SORT_BY_KEY);
202 while (dict_iterate(iter, &key, &value) > 0) {
203 dict_unset(dt, key);
204 }
205 dict_iterate_deinit(&iter);
206
207 success = mlist->module_ctx.super.delete_mailbox(list, name);
208 if (success < 0) {
209 dict_transaction_rollback(&dt);
210 } else {
211 if (dict_transaction_commit(&dt) < 0) {
212 i_error("metadata_mailbox_list_delete:"
213 " could not update metadata dict");
214 }
215 }
216
217 return success;
218}
219
220
221static void metadata_mailbox_list_created(struct mailbox_list *list)
222{
223 struct metadata_mailbox_list *mlist;
224
225 mlist = p_new(list->pool, struct metadata_mailbox_list, 1);
226 mlist->module_ctx.super = list->v;
227 list->v.rename_mailbox = metadata_mailbox_list_rename;
228 list->v.delete_mailbox = metadata_mailbox_list_delete;
229 MODULE_CONTEXT_SET(list, metadata_mailbox_list_module, mlist);
230
231 if (metadata_next_hook_mailbox_list_created != NULL)
232 metadata_next_hook_mailbox_list_created(list);
233}
234
235
236bool metadata_get_metadata_entry(struct client_command_context *cmd,
237 const char *mailboxname, const char *entry,
238 const char **value_r, bool private)
239{
240 int success;
241 struct mail_storage *storage;
242 const char *key;
243
244 if (metadata_debug)
245 i_info("metadata_get_metadata_entry: mailboxname=%s, entry=%s,"
246 " private=%d", mailboxname, entry, private);
247
248 if (!client_verify_mailbox_name(cmd, mailboxname,
249 CLIENT_VERIFY_MAILBOX_SHOULD_EXIST))
250 return FALSE;
251
252 storage = client_find_storage(cmd, &mailboxname);
253 if (!storage)
254 return FALSE;
255
256 key = create_dict_key(storage, mailboxname, entry, private);
257 if (metadata_debug)
258 i_info("metadata_get_metadata_entry: dict key=%s", key);
259
260 success = dict_lookup(metadata_dict, cmd->pool, key, value_r);
261
262 if (success == 0) {
263 *value_r = NULL;
264 } else if (success < 0) {
265 client_send_tagline(cmd, "NO Lookup failed.");
266 }
267
268 return success >= 0;
269}
270
271bool metadata_set_metadata_entry(struct client_command_context *cmd,
272 const char *mailboxname, const char *entry,
273 const char *value, bool private)
274{
275 struct dict_transaction_context *dt;
276 struct mail_storage *storage;
277 const char *key;
278
279 if (metadata_debug)
280 i_info("metadata_set_metadata_entry: mailboxname=%s, entry=%s,"
281 " value=%s, private=%d", mailboxname, entry,
282 value != NULL ? value : "<NULL>", private);
283
284 if (!client_verify_mailbox_name(cmd, mailboxname,
285 CLIENT_VERIFY_MAILBOX_SHOULD_EXIST))
286 return FALSE;
287
288 storage = client_find_storage(cmd, &mailboxname);
289 if (!storage)
290 return FALSE;
291
292 key = create_dict_key(storage, mailboxname, entry, private);
293 if (metadata_debug)
294 i_info("metadata_set_metadata_entry: dict key=%s", key);
295
296 dt = dict_transaction_begin(metadata_dict);
297
298 if (value != NULL) {
299 dict_set(dt, key, value);
300 } else {
301 dict_unset(dt, key);
302 }
303
304 if (dict_transaction_commit(&dt) < 0)
305 {
306 client_send_tagline(cmd, "NO Setting meta-data failed.");
307 return FALSE;
308 }
309
310 return TRUE;
311}
312
313void metadata_plugin_init(void)
314{
315 const char *username;
316 const char *dict_uri;
317 const char *base_dir;
318
319 username = getenv("USER");
320 if (username == NULL)
321 i_fatal("metadata plugin: USER unset");
322
323 dict_uri = getenv("METADATA_DICT");
324 if (dict_uri == NULL)
325 i_fatal("metadata plugin: METADATA_DICT unset");
326
327 metadata_allow_private = getenv("METADATA_ALLOW_PRIVATE") != NULL;
328 metadata_debug = getenv("METADATA_DEBUG") != NULL;
329
330 base_dir = getenv("BASE_DIR");
331 if (base_dir == NULL)
332 base_dir = PKG_RUNDIR;
333 metadata_dict = dict_init(dict_uri, DICT_DATA_TYPE_STRING, username,
334 base_dir);
335
336 if (metadata_dict == NULL)
337 i_fatal("metadata plugin: dict_init() failed");
338
339 metadata_next_hook_mailbox_list_created = hook_mailbox_list_created;
340 hook_mailbox_list_created = metadata_mailbox_list_created;
341}36}
34237
343void metadata_plugin_deinit(void)38void metadata_plugin_deinit(void)
344{39{
345 if (metadata_dict != NULL) {40 mail_storage_hooks_remove(&metadata_mail_storage_hooks);
346 dict_deinit(&metadata_dict);
347 }
348 if (metadata_next_hook_mailbox_list_created) {
349 hook_mailbox_list_created =
350 metadata_next_hook_mailbox_list_created;
351 }
352}41}
35342
=== removed file 'src/metadata-plugin.h'
--- src/metadata-plugin.h 2010-08-11 17:11:01 +0000
+++ src/metadata-plugin.h 1970-01-01 00:00:00 +0000
@@ -1,17 +0,0 @@
1#ifndef __METADATA_PLUGIN
2#define __METADATA_PLUGIN
3
4void metadata_plugin_init(void);
5void metadata_plugin_deinit(void);
6
7bool metadata_private_allowed(void);
8
9bool metadata_get_metadata_entry(struct client_command_context *cmd,
10 const char *mailboxname, const char *entry,
11 const char **value_r, bool private);
12
13bool metadata_set_metadata_entry(struct client_command_context *cmd,
14 const char *mailboxname, const char *entry,
15 const char *value, bool private);
16
17#endif
180
=== added file 'src/metadata-settings.c'
--- src/metadata-settings.c 1970-01-01 00:00:00 +0000
+++ src/metadata-settings.c 2011-09-29 19:49:50 +0000
@@ -0,0 +1,85 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "metadata-settings.h"
20
21#include <stdlib.h>
22
23#define METADATA_MAXSIZE_DEFAULT 1024
24#define METADATA_MAXENTRIES_DEFAULT 65336
25
26void
27metadata_settings_init(struct metadata_settings **set, struct mail_user *user) {
28 *set = i_new(struct metadata_settings, 1);
29 memset(*set, 0, sizeof(**set));
30
31
32 const char *dict_uri = mail_user_plugin_getenv(user, "metadata_dict");
33 if (dict_uri != NULL) {
34 (*set)->dict_uri = dict_uri;
35 }
36 else if (user->mail_debug) {
37 i_debug("metadata: No metadata_dict setting - "
38 "metadata storage is disabled");
39 (*set)->dict_uri = NULL;
40 }
41
42 const char *maxsize = mail_user_plugin_getenv(user, "metadata_maxsize");
43 if (maxsize != NULL) {
44 (*set)->maxsize = strtol(maxsize, NULL, 10);
45 if ((*set)->maxsize < 0 || (*set)->maxsize == LONG_MAX) {
46 if (user->mail_debug) {
47 i_debug("metadata: Illegal metadata_maxsize setting - "
48 "using default of %d", METADATA_MAXSIZE_DEFAULT);
49 }
50 (*set)->maxsize = METADATA_MAXSIZE_DEFAULT;
51 }
52 }
53 else {
54 if (user->mail_debug) {
55 i_debug("metadata: No metadata_maxsize setting - "
56 "using default of %d", METADATA_MAXSIZE_DEFAULT);
57 }
58 (*set)->maxsize = METADATA_MAXSIZE_DEFAULT;
59 }
60
61 const char *maxentries = mail_user_plugin_getenv(user, "metadata_maxentries");
62 if (maxentries != NULL) {
63 (*set)->maxentries = strtol(maxentries, NULL, 10);
64 if ((*set)->maxentries < 0 || (*set)->maxentries == LONG_MAX) {
65 if (user->mail_debug) {
66 i_debug("metadata: Illegal metadata_maxentries setting - "
67 "using default of %d", METADATA_MAXENTRIES_DEFAULT);
68 }
69 (*set)->maxentries = METADATA_MAXENTRIES_DEFAULT;
70 }
71 }
72 else {
73 if (user->mail_debug) {
74 i_debug("metadata: No metadata_maxentries setting - "
75 "using default of %d", METADATA_MAXENTRIES_DEFAULT);
76 }
77 (*set)->maxentries = METADATA_MAXENTRIES_DEFAULT;
78 }
79}
80
81void
82metadata_settings_deinit(struct metadata_settings **set) {
83 free(*set);
84 *set = NULL;
85}
086
=== added file 'src/metadata-settings.h'
--- src/metadata-settings.h 1970-01-01 00:00:00 +0000
+++ src/metadata-settings.h 2011-09-29 19:49:50 +0000
@@ -0,0 +1,40 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef DOVECOT_METADATA_SETTINGS_H
20#define DOVECOT_METADATA_SETTINGS_H
21
22#include "metadata-global.h"
23
24#include "mail-user.h"
25
26struct metadata_settings {
27 bool metadata_debug;
28 const char *dict_uri;
29 int maxsize;
30 int maxentries;
31};
32
33void
34metadata_settings_init(struct metadata_settings **set, struct mail_user *user)
35 ATTR_NONNULL(1,2);
36void
37metadata_settings_deinit(struct metadata_settings **set)
38 ATTR_NONNULL(1);
39
40#endif
041
=== added file 'src/str-ext.c'
--- src/str-ext.c 1970-01-01 00:00:00 +0000
+++ src/str-ext.c 2011-09-29 19:49:50 +0000
@@ -0,0 +1,42 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "str-ext.h"
20
21#include <string.h>
22
23int
24strchr_num(const char *s, int c) {
25 int num = 0;
26 s = strchr(s, c);
27 while (s != NULL) {
28 num++;
29 s = strchr(s+1, c);
30 }
31 return num;
32}
33
34
35bool
36str_has_wildcards(const char *s) {
37 for (; *s != '\0'; s++) {
38 if (*s == '*' || *s == '%')
39 return TRUE;
40 }
41 return FALSE;
42}
043
=== added file 'src/str-ext.h'
--- src/str-ext.h 1970-01-01 00:00:00 +0000
+++ src/str-ext.h 2011-09-29 19:49:50 +0000
@@ -0,0 +1,38 @@
1/*
2 Copyright (c) 2010 by Dennis Schridde
3
4 This file is part of dovecot-metadata.
5
6 dovecot-metadata is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 dovecot-metadata is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with dovecot-metadata. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef DOVECOT_STR_EXT_H
20#define DOVECOT_STR_EXT_H
21
22#include "metadata-global.h"
23
24#include <stdbool.h>
25
26#include "str.h"
27
28#define str_append_printf str_printfa
29
30int
31strchr_num(const char *s, int c)
32 ATTR_NONNULL(1);
33
34bool
35str_has_wildcards(const char *s)
36 ATTR_NONNULL(1);
37
38#endif

Subscribers

People subscribed via source and target branches

to all changes: