[exim.git] / doc / doc-misc / Ext-maildir++
1 Maildir++
3 In this document:
4 * HOWTO.maildirquota
5 * Mission statement
6 * Definitions and goals
7 * Contents of a maildirsize
8 * Calculating maildirsize
9 * Calculating the quota for a Maildir++
10 * Delivering to a Maildir++
11 * Reading from a Maildir++
12 * Bugs
16 The remaining portion of this document is a technical description of
17 the maildir quota extension. This section is a brief overview of this
18 extension.
20 What is a maildirquota?
22 If you would like to have a quota on your maildir mailboxes, the best
23 solution is to always use filesystem-based quotas: per-user usage
24 quotas that is enforced by the operating system.
26 This is the best solution when the default Maildir is located in each
27 account's home directory. This solution will NOT work if Maildirs are
28 stored elsewhere, or if you have a large virtual domain setup where a
29 single userid is used to hold many individual Maildirs, one for each
30 virtual user.
32 This extension to the maildir format allows a "voluntary" maildir
33 quota implementation that does not rely on filesystem-based quotas.
35 When maildirquota will not work.
37 For this quota mechanism to work, all software that accesses a maildir
38 must observe this quota protocol. It follows that this quota mechanism
39 can be easily circumvented if users have direct (shell) access to the
40 filesystem containing the users' maildirs.
42 Furthermore, this quota mechanism is not 100% effective. It is
43 possible to have a situation where someone may go over quota. This
4c04137d 44 quota implementation uses a deliberate trade-off. It is necessary to
45 use some form of locking in order to have a complete bulletproof quota
46 enforcement, but maildirs mail stores were explicitly designed to
47 avoid any kind of locking. This quota approach does not use locking,
48 and the tradeoff is that sometimes it is possible for a few extra
49 messages to be delivered to the maildir, before the door is
50 permanently shot.
52 For best performance, all maildir clients should support this quota
53 extension, however there's a wide degree of tolerance here. As long as
54 the mail delivery agent that puts new messages into a Maildir uses
55 this extension, the quota will be enforced without excessive
56 degradation.
58 In the worst case scenario, quotas are automatically recalculated
59 every fifteen minutes. If a maildir goes over quota, and a mail client
60 that does not support this quota extension removes enough mail from
61 the maildir, the mail delivery agent will not be immediately informed
62 that the maildir is now under quota. However, eventually the correct
63 quota will be recalculated and mail delivery will resume.
65 Mail user agents sometimes put messages into the maildir themselves.
66 Messages added to a maildir by a mail user agent that does not
67 understand the quota extension will not be immediately counted towards
68 the overall quota, and may not be counted for an extensive period of
69 time. Additionally, if there are a lot of messages that have been
70 added to a maildir from these mail user agents, quota recalculation
71 may impose non-trivial load on the system, as the quota recalculator
72 will have to issue the stat system call for each message.
74 How to implement the quota
76 The best way to do that is to modify your mail server to implement the
77 protocol defined by this document. Not everyone, of course, has this
78 ability. Therefore, an alternate approach is available.
80 This package creates a very short utility called "deliverquota". It
81 will NOT be installed anywhere by default, unless this maildir quota
82 implementation is a part of a larger package, in which case the parent
83 package may install this utility somewhere. If you obtained the
84 maildir package separately, you will need to compile it by running the
85 configure script, then by running make.
87 deliverquota takes two arguments. deliverquota reads the message from
88 standard input, then delivers it to the maildir specified by the first
89 argument to deliverquota. The second argument specifies the actual
90 quota for this maildir, as defined elsewhere in this document.
91 deliverquota will deliver the message to the maildir, making a best
92 effort not to exceed the stated quota. If the maildir is over quota,
93 deliverquota terminates with exit code 77. Otherwise, it delivers the
94 message, updates the quota, and terminates with exit code 0.
96 Therefore, proceed as follows:
97 * Copy deliverquota to some convenient location, say /usr/local/bin.
98 * Configure your mail server to use deliverquota. For example, if
99 you use Qmail and your maildirs are all located in $HOME/Maildir,
100 replace the './Maildir/' argument to qmail-start with the
101 following:
102'| /usr/local/bin/deliverquota ./Maildir 1000000S'
107 This sets a one million byte limit on all Maildirs. As I
108 mentioned, this is meaningless if login access is available,
109 because the individual account owner can create his own
110 $HOME/.qmail file, and ignore deliverquota. Note that in this
111 case, you MUST use apostrophes on the qmail-start command line, in
112 order to quote this as one argument.
114 If you would like to use different quotas for different users, you
115 will have to put together a separate process or a script that looks up
116 the appropriate quota for the recipient, and runs deliverquota
117 specifying the quota. If no login access to the mail server is
118 available, you can simply create a separate $HOME/.qmail for every
119 recipient.
121 That's pretty much it. If you handle a moderate amount of mail, I have
122 one more suggestion. For the first couple of weeks, run deliverquota
123 setting the second argument to an empty string. This disables quota
124 enforcement, however it still activates certain optimizations that
125 permit very fast quota recalculation. Messages delivered by
126 deliverquota have their message size encoded in their filename; this
127 makes it possible to avoid stat-ing the message in the Maildir, when
128 recalculating the quota. Then, after most messages in your maildirs
129 have been delivered by deliverquota, activate the quotas!!!
131 maildirquota-enhanced applications
133 This is a list of applications that have been enhanced to support the
134 maildirquota extension:
135 * maildrop - mail delivery agent/mail filter.
136 * SqWebmail - webmail CGI binary.
138 These applications fall into two classes:
139 * Mail delivery agents. These applications read some externally
140 defined table of mail recipients and their maildir quota.
141 * Mail clients. These applications read maildir quota information
142 that has been defined by the mail delivery agent.
144 Mail clients generally do not need any additional setup in order to
145 use the maildirquota extension. They will automatically read and
146 implement any quota specification set by the mail delivery agent.
148 On the other hand, mail delivery agents will require some kind of
149 configuration in order to activate the maildirquota extension for some
150 or all recipients. The instructions for doing that depends upon the
151 mail delivery agent. The documentation for the mail delivery agent
152 should be consulted for additional information.
153 _________________________________________________________________
155Mission statement
157 Maildir++ is a mail storage structure that's based on the Maildir
158 structure, first used in the Qmail mail server. Actually, Maildir++ is
159 just a minor extension to the standard Maildir structure.
161 For more information, see http://www.qmail.org/man/man5/maildir.html.
162 I am not going to include the definition of a Maildir in this
163 document. Consider it included right here. This document only
164 describes the differences.
166 Maildir++ adds a couple of things to a standard Maildir: folders and
167 quotas.
169 Quotas enforce a maximum allowable size of a Maildir. In many
170 situations, using the quota mechanism of the underlying filesystem
171 won't work very well. If a filesystem quota mechanism is used, then
172 when a Maildir goes over quota, Qmail does not bounce additional mail,
173 but keeps it queued, changing one bad situation into another bad
174 situation. Not only know you have an account that's backed up, but now
175 your queue starts to back up too.
177Definitions, and goals
179 Maildir++ and Maildir shall be completely interchangeable. A Maildir++
180 client will be able to use a standard Maildir, automatically
181 "upgrading" it in the process. A Maildir client will be able to use a
182 Maildir++ just like a regular Maildir. Of course, a plain Maildir
183 client won't be able to enforce a quota, and won't be able to access
184 messages stored in folders.
186 Folders are created as subdirectories under the main Maildir. The name
187 of the subdirectory always starts with a period. For example, a folder
188 named "Important" will be a subdirectory called ".Important". You
189 can't have subdirectories that start with two periods.
191 A Maildir++ client ignores anything in the main Maildir that starts
192 with a period, but is not a subdirectory.
194 Each subdirectory is a fully-fledged Maildir of its own, that is you
195 have .Important/tmp, .Important/new, and .Important/cur. Everything
196 that applies to the main Maildir applies equally well to the
197 subdirectory, including automatically cleaning up old files in tmp. A
198 Maildir++ enhancement is that a message can be moved between folders
199 and/or the main Maildir simply by moving/renaming the file (into the
200 cur subdirectory of the destination folder). Therefore, the entire
201 Maildir++ must reside on the same filesystem.
203 Within each subdirectory there's an empty file, maildirfolder. Its
204 existence tells the mail delivery agent that this Maildir is a really
205 a folder underneath a parent Maildir++.
207 Only one special folder is reserved: Trash (subdirectory .Trash).
208 Instead of marking deleted messages with the D flag, Maildir++ clients
209 move the message into the Trash folder. Maildir++ readers are
210 responsible for expunging messages from Trash after a system-defined
211 retention interval.
213 When a Maildir++ reader sees a message marked with a D flag it may at
214 its option: remove the message immediately, move it into Trash, or
215 ignore it.
217 Can folders have subfolders, defined in a recursive fashion? The
218 answer is no. If you want to have a client with a hierarchy of
219 folders, emulate it. Pick a hierarchy separator character, say ":".
220 Then, folder foo/bar is subdirectory .foo:bar.
222 This is all that there's to say about folders. The rest of this
223 document deals with quotas.
225 The purpose of quotas is to temporarily disable a Maildir, if it goes
226 over the quota. There is one and only major goal that this quota
227 implementation tries to achieve:
228 * Place as little overhead as possible on the mail system that's
229 delivering to the Maildir++
231 That's it. To achieve that goal, certain compromises are made:
232 * Mail delivery will stop as soon as possible after Maildir++'s size
233 goes over quota. Certain race conditions may happen with Maildir++
234 going a lot over quota, in rare circumstances. That is taken into
235 account, and the situation will eventually resolve itself, but you
236 should not simply take your systemwide quota, multiply it by the
237 number of mail accounts, and allocate that much disk space. Always
238 leave room to spare.
239 * How well the quota mechanism will work will depend on whether or
240 not everything that accesses the Maildir++ is a Maildir++ client.
241 You can have a transition period where some of your mail clients
242 are just Maildir clients, and things should run more or less well.
243 There will be some additional load because the size of the Maildir
244 will be recalculated more often, but the additional load shouldn't
245 be noticeable.
247 This won't be a perfect solution, but it will hopefully be good
248 enough. Maildirs are simply designed to rely on the filesystem to
249 enforce individual quotas. If a filesystem-based quota works for you,
250 use it.
252 A Maildir++ may contain the following additional file: maildirsize.
254Contents of maildirsize
256 maildirsize contains two or more lines terminated by newline
257 characters.
259 The first line contains a copy of the quota definition as used by the
260 system's mail server. Each application that uses the maildir must know
261 what it's quota is. Instead of configuring each application with the
262 quota logic, and making sure that every application's quota definition
263 for the same maildir is exactly the same, the quota specification used
264 by the system mail server is saved as the first line of the
265 maildirsize file. All other application that enforce the maildir quota
266 simply read the first line of maildirsize.
268 The quota definition is a list, separate by commas. Each member of the
269 list consists of an integer followed by a letter, specifying the
270 nature of the quota. Currently defined quota types are 'S' - total
271 size of all messages, and 'C' - the maximum count of messages in the
272 maildir. For example, 10000000S,1000C specifies a quota of 10,000,000
273 bytes or 1,000 messages, whichever comes first.
275 All remaining lines all contain two integers separated by a single
276 space. The first integer is interpreted as a byte count. The second
277 integer is interpreted as a file count. A Maildir++ writer can add up
278 all byte counts and file counts from maildirsize and enforce a quota
279 based either on number of messages or the total size of all the
280 messages.
282Calculating maildirsize
284 In most cases, changes to maildirsize are recorded by appending an
285 additional line. Under some conditions maildirsize has to be
286 recalculated from scratch. These conditions are defined later. This is
287 the procedure that's used to recalculate maildirsize:
288 1. If we find a maildirfolder within the directory, we're delivering
289 to a folder, so back up to the parent directory, and start again.
290 2. Read the contents of the new and cur subdirectories. Also, read
291 the contents of the new and cur subdirectories in each Maildir++
292 folder, except Trash. Before reading each subdirectory, stat() the
293 subdirectory itself, and keep track of the latest timestamp you
294 get.
295 3. If the filename of each message is of the form xxxxx,S=nnnnn or
296 xxxxx,S=nnnnn:xxxxx where "xxxxx" represents arbitrary text, then
297 use nnnnn as the size of the file (which will be conveniently
298 recorded in the filename by a Maildir++ writer, within the
299 conventions of filename naming in a Maildir). If the message was
300 not written by a Maildir++ writer, stat() it to obtain the message
301 size. If stat() fails, a race condition removed the file, so just
302 ignore it and move on to the next one.
303 4. When done, you have the grand total of the number of messages and
304 their total size. Create a new maildirsize by: creating the file
305 in the tmp subdirectory, observing the conventions for writing to
306 a Maildir. Then rename the file as maildirsize.Afterwards, stat
307 all new and cur subdirectories again. If you find a timestamp
308 later than the saved timestamp, REMOVE maildirsize.
309 5. Before running this calculation procedure, the Maildir++ user
310 wanted to know the size of the Maildir++, so return the calculated
311 values. This is done even if maildirsize was removed.
313Calculating the quota for a Maildir++
315 This is the procedure for reading the contents of maildirsize for the
316 purpose of determine if the Maildir++ is over quota.
317 1. If maildirsize does not exist, or if its size is at least 5120
318 bytes, recalculate it using the procedure defined above, and use
319 the recalculated numbers. Otherwise, read the contents of
320 maildirsize, and add up the totals.
321 2. The most efficient way of doing this is to: open maildirsize, then
322 start reading it into a 5120 byte buffer (some broken NFS
323 implementations may return less than 5120 bytes read even before
324 reaching the end of the file). If we fill it, which, in most
325 cases, will happen with one read, close it, and run the
326 recalculation procedure.
327 3. In many cases the quota calculation is for the purpose of adding
328 or removing messages from a Maildir++, so keep the file descriptor
329 to maildirsize open. A file descriptor will not be available if
330 quota recalculation ended up removing maildirsize due to a race
331 condition, so the caller may or may not get a file descriptor
332 together with the Maildir++ size.
f988ce57 333 4. If the numbers we got indicated that the Maildir++ is over quota,
334 some additional logic is in order: if we did not recalculate
335 maildirsize, if the numbers in maildirsize indicated that we are
336 over quota, then if maildirsize was more than one line long, or if
337 the timestamp on maildirsize indicated that it's at least 15
338 minutes old, throw out the totals, and recalculate maildirsize
339 from scratch.
341 Eventually the 5120 byte limitation will always cause maildirsize to
342 be recalculated, which will compensate for any race conditions which
343 previously threw off the totals. Each time a message is delivered or
344 removed from a Maildir++, one line is added to maildirsize (this is
345 described below in greater detail). Most messages are less than 10K
346 long, so each line appended to maildirsize will be either between
347 seven and nine bytes long (four bytes for message count, space, digit
348 1, newline, optional minus sign in front of both counts if the message
349 was removed). This results in about 640 Maildir++ operations before a
350 recalculation is forced. Since most messages are added once and
351 removed once from a Maildir, expect recalculation to happen
352 approximately every 320 messages, keeping the overhead of a
353 recalculation to a minimum. Even if most messages include large
354 attachments, most attachments are less than 100K long, which brings
355 down the average recalculation frequency to about 150 messages.
357 Also, the effect of having non-Maildir++ clients accessing the
358 Maildir++ is reduced by forcing a recalculation when we're potentially
359 over quota. Even if non-Maildir++ clients are used to remove messages
360 from the Maildir, the fact that the Maildir++ is still over quota will
361 be verified every 15 minutes.
363Delivering to a Maildir++
365 Delivering to a Maildir++ is like delivering to a Maildir, with the
366 following exceptions:
367 1. Follow the usual Maildir conventions for naming the filename used
368 to store the message, except that append ,S=nnnnn to the name of
369 the file, where nnnnn is the size of the file. This eliminates the
370 need to stat() most messages when calculating the quota. If the
371 size of the message is not known at the beginning, append ,S=nnnnn
372 when renaming the message from tmp to new.
373 2. As soon as the size of the message is known (hopefully before it
374 is written into tmp), calculate Maildir++'s quota, using the
375 procedure defined previously. If the message is over quota, back
376 out, cleaning up anything that was created in tmp.
377 3. If a file descriptor to maildirsize was opened for us, after
378 moving the file from tmp to new append a line to the file
379 containing the message size, and "1".
381Reading from a Maildir++
383 Maildir++ readers should mind the following additional tasks:
384 1. Make sure to create the maildirfolder file in any new folders
385 created within the Maildir++.
386 2. When moving a message to the Trash folder, append a line to
387 maildirsize, containing a negative message size and a '-1'.
388 3. When moving a message from the Trash folder, follow the steps
389 described in "Delivering to Maildir++", as far as quota logic
390 goes. That is, refuse to move messages out of Trash if the
391 Maildir++ is over quota.
392 4. Moving a message between other folders carries no additional
393 requirements.