comparison src/lib-storage/index/maildir/maildir-storage.c @ 0:3b1985cbc908 HEAD

Initial revision
author Timo Sirainen <tss@iki.fi>
date Fri, 09 Aug 2002 12:15:38 +0300
parents
children 1b34ec11fff8
comparison
equal deleted inserted replaced
-1:000000000000 0:3b1985cbc908
1 /* Copyright (C) 2002 Timo Sirainen */
2
3 #include "lib.h"
4 #include "unlink-directory.h"
5 #include "subscription-file/subscription-file.h"
6 #include "maildir-index.h"
7 #include "maildir-storage.h"
8
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <sys/stat.h>
13
14 #define CREATE_MODE 0770 /* umask() should limit it more */
15
16 extern MailStorage maildir_storage;
17 static Mailbox maildir_mailbox;
18
19 static const char *maildirs[] = { "cur", "new", "tmp", NULL };
20
21 static MailStorage *maildir_create(const char *data)
22 {
23 MailStorage *storage;
24 const char *home, *path;
25
26 if (data == NULL || *data == '\0') {
27 /* we'll need to figure out the maildir location ourself.
28 it's either root dir if we've already chroot()ed, or
29 $HOME/Maildir otherwise */
30 if (access("/cur", R_OK|W_OK|X_OK) == 0)
31 data = "/";
32 else {
33 home = getenv("HOME");
34 if (home != NULL) {
35 path = t_strconcat(home, "/Maildir", NULL);
36 if (access(path, R_OK|W_OK|X_OK) == 0)
37 data = path;
38 }
39 }
40 }
41
42 if (data == NULL)
43 return NULL;
44
45 storage = i_new(MailStorage, 1);
46 memcpy(storage, &maildir_storage, sizeof(MailStorage));
47
48 storage->dir = i_strdup(data);
49 return storage;
50 }
51
52 static void maildir_free(MailStorage *storage)
53 {
54 i_free(storage->dir);
55 i_free(storage);
56 }
57
58 static int maildir_autodetect(const char *data)
59 {
60 struct stat st;
61
62 return stat(t_strconcat(data, "/cur", NULL), &st) == 0 &&
63 S_ISDIR(st.st_mode);
64 }
65
66 static int maildir_is_valid_name(MailStorage *storage, const char *name)
67 {
68 return name[0] != '\0' && name[0] != storage->hierarchy_sep &&
69 strchr(name, '/') == NULL;
70 }
71
72 /* create or fix maildir, ignore if it already exists */
73 static int create_maildir(const char *dir, int verify)
74 {
75 const char **tmp;
76 char path[1024];
77
78 if (mkdir(dir, CREATE_MODE) == -1 && (errno != EEXIST || !verify))
79 return FALSE;
80
81 for (tmp = maildirs; *tmp != NULL; tmp++) {
82 i_snprintf(path, sizeof(path), "%s/%s", dir, *tmp);
83
84 if (mkdir(path, CREATE_MODE) == -1 &&
85 (errno != EEXIST || !verify))
86 return FALSE;
87 }
88
89 return TRUE;
90 }
91
92 static int verify_inbox(MailStorage *storage, const char *dir)
93 {
94 const char **tmp;
95 char src[1024], dest[1024];
96
97 /* first make sure the cur/ new/ and tmp/ dirs exist in root dir */
98 (void)create_maildir(dir, TRUE);
99
100 /* create the .INBOX directory */
101 i_snprintf(dest, sizeof(dest), "%s/.INBOX", dir);
102 if (mkdir(dest, CREATE_MODE) == -1 && errno != EEXIST) {
103 mail_storage_set_critical(storage, "Can't create directory "
104 "%s: %m", dest);
105 return FALSE;
106 }
107
108 /* then symlink the cur/ new/ and tmp/ into the .INBOX/ directory */
109 for (tmp = maildirs; *tmp != NULL; tmp++) {
110 i_snprintf(src, sizeof(src), "../%s", *tmp);
111 i_snprintf(dest, sizeof(dest), "%s/.INBOX/%s", dir, *tmp);
112
113 if (symlink(src, dest) == -1 && errno != EEXIST) {
114 mail_storage_set_critical(storage, "symlink(%s, %s) "
115 "failed: %m", src, dest);
116 return FALSE;
117 }
118 }
119
120 return TRUE;
121 }
122
123 static Mailbox *maildir_open(MailStorage *storage, const char *name,
124 int readonly)
125 {
126 IndexMailbox *ibox;
127 const char *path;
128
129 path = t_strconcat(storage->dir, "/.", name, NULL);
130
131 ibox = index_storage_init(storage, &maildir_mailbox,
132 maildir_index_alloc(path), name, readonly);
133 if (ibox != NULL)
134 ibox->expunge_locked = maildir_expunge_locked;
135 return (Mailbox *) ibox;
136 }
137
138 static Mailbox *maildir_open_mailbox(MailStorage *storage, const char *name,
139 int readonly)
140 {
141 struct stat st;
142 char path[1024];
143
144 mail_storage_clear_error(storage);
145
146 /* INBOX is always case-insensitive */
147 if (strcasecmp(name, "INBOX") == 0) {
148 if (!verify_inbox(storage, storage->dir))
149 return NULL;
150 return maildir_open(storage, "INBOX", readonly);
151 }
152
153 i_snprintf(path, sizeof(path), "%s/.%s", storage->dir, name);
154 if (stat(path, &st) == 0) {
155 /* exists - make sure the required directories are also there */
156 (void)create_maildir(path, TRUE);
157
158 return maildir_open(storage, name, readonly);
159 } else if (errno == ENOENT) {
160 mail_storage_set_error(storage, "Mailbox doesn't exist");
161 return NULL;
162 } else {
163 mail_storage_set_critical(storage, "Can't open mailbox %s: %m",
164 name);
165 return NULL;
166 }
167 }
168
169 static int maildir_create_mailbox(MailStorage *storage, const char *name)
170 {
171 char path[1024];
172
173 mail_storage_clear_error(storage);
174
175 if (strcasecmp(name, "INBOX") == 0)
176 name = "INBOX";
177
178 if (!maildir_is_valid_name(storage, name)) {
179 mail_storage_set_error(storage, "Invalid mailbox name");
180 return FALSE;
181 }
182
183 i_snprintf(path, sizeof(path), "%s/.%s", storage->dir, name);
184 if (create_maildir(path, FALSE))
185 return TRUE;
186 else if (errno == EEXIST) {
187 mail_storage_set_error(storage, "Mailbox already exists");
188 return FALSE;
189 } else {
190 mail_storage_set_critical(storage, "Can't create mailbox "
191 "%s: %m", name);
192 return FALSE;
193 }
194 }
195
196 static int maildir_delete_mailbox(MailStorage *storage, const char *name)
197 {
198 struct stat st;
199 char src[1024], dest[1024];
200 int count;
201
202 mail_storage_clear_error(storage);
203
204 if (strcasecmp(name, "INBOX") == 0) {
205 mail_storage_set_error(storage, "INBOX can't be deleted.");
206 return FALSE;
207 }
208
209 /* rename the .maildir into ..maildir which marks it as being
210 deleted. this way we never see partially deleted maildirs. */
211 i_snprintf(src, sizeof(src), "%s/.%s", storage->dir, name);
212 i_snprintf(dest, sizeof(dest), "%s/..%s", storage->dir, name);
213
214 if (stat(src, &st) != 0 && errno == ENOENT) {
215 mail_storage_set_error(storage, "Mailbox doesn't exist.");
216 return FALSE;
217 }
218
219 count = 0;
220 while (rename(src, dest) == -1 && count < 2) {
221 if (errno != EEXIST) {
222 mail_storage_set_critical(storage,
223 "rename(%s, %s) failed: %m",
224 src, dest);
225 return FALSE;
226 }
227
228 /* ..dir already existed? delete it and try again */
229 if (!unlink_directory(dest)) {
230 mail_storage_set_critical(storage,
231 "unlink_directory(%s) "
232 "failed: %m", dest);
233 return FALSE;
234 }
235 count++;
236 }
237
238 if (!unlink_directory(dest)) {
239 mail_storage_set_critical(storage, "unlink_directory(%s) "
240 "failed: %m", dest);
241 return FALSE;
242 }
243 return TRUE;
244 }
245
246 static int move_inbox_data(MailStorage *storage, const char *newdir)
247 {
248 const char **tmp;
249 char oldpath[1024], newpath[1024];
250
251 /* newpath points to the destination folder directory, which contains
252 symlinks to real INBOX directories. unlink() the symlinks and
253 move the real cur/ directory here. */
254 for (tmp = maildirs; *tmp != NULL; tmp++) {
255 i_snprintf(newpath, sizeof(newpath), "%s/%s", newdir, *tmp);
256
257 if (unlink(newpath) == -1 && errno != EEXIST) {
258 mail_storage_set_error(storage, "unlink(%s) failed: "
259 "%m", newpath);
260 return FALSE;
261 }
262 }
263
264 i_snprintf(oldpath, sizeof(oldpath), "%s/cur", storage->dir);
265 i_snprintf(newpath, sizeof(newpath), "%s/cur", newdir);
266 if (rename(oldpath, newpath) != 0) {
267 mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
268 oldpath, newpath);
269 return FALSE;
270 }
271
272 /* create back the cur/ directory for INBOX */
273 (void)mkdir(oldpath, CREATE_MODE);
274 return TRUE;
275 }
276
277 static int maildir_rename_mailbox(MailStorage *storage, const char *oldname,
278 const char *newname)
279 {
280 char oldpath[1024], newpath[1024];
281
282 mail_storage_clear_error(storage);
283
284 if (strcasecmp(oldname, "INBOX") == 0)
285 oldname = "INBOX";
286
287 /* NOTE: renaming INBOX works just fine with us, it's simply created
288 the next time it's needed. Only problem with it is that it's not
289 atomic operation but that can't be really helped. */
290 i_snprintf(oldpath, sizeof(oldpath), "%s/.%s", storage->dir, oldname);
291 i_snprintf(newpath, sizeof(newpath), "%s/.%s", storage->dir, newname);
292 if (rename(oldpath, newpath) == 0) {
293 if (strcmp(oldname, "INBOX") == 0)
294 return move_inbox_data(storage, newpath);
295 return TRUE;
296 }
297
298 if (errno == EEXIST) {
299 mail_storage_set_error(storage,
300 "Target mailbox already exists");
301 return FALSE;
302 } else {
303 mail_storage_set_critical(storage, "rename(%s, %s) failed: %m",
304 oldpath, newpath);
305 return FALSE;
306 }
307 }
308
309 static int maildir_get_mailbox_name_status(MailStorage *storage,
310 const char *name,
311 MailboxNameStatus *status)
312 {
313 struct stat st;
314 char path[1024];
315
316 mail_storage_clear_error(storage);
317
318 if (strcasecmp(name, "INBOX") == 0)
319 name = "INBOX";
320
321 if (!maildir_is_valid_name(storage, name)) {
322 *status = MAILBOX_NAME_INVALID;
323 return TRUE;
324 }
325
326 i_snprintf(path, sizeof(path), "%s/.%s", storage->dir, name);
327 if (stat(path, &st) == 0) {
328 *status = MAILBOX_NAME_EXISTS;
329 return TRUE;
330 } else if (errno == ENOENT) {
331 *status = MAILBOX_NAME_VALID;
332 return TRUE;
333 } else {
334 mail_storage_set_critical(storage, "mailbox name status: "
335 "stat(%s) failed: %m", path);
336 return FALSE;
337 }
338 }
339
340 MailStorage maildir_storage = {
341 "maildir", /* name */
342
343 '.', /* hierarchy_sep - can't be changed */
344
345 maildir_create,
346 maildir_free,
347 maildir_autodetect,
348 maildir_open_mailbox,
349 maildir_create_mailbox,
350 maildir_delete_mailbox,
351 maildir_rename_mailbox,
352 maildir_find_mailboxes,
353 subsfile_set_subscribed,
354 maildir_find_subscribed,
355 maildir_get_mailbox_name_status,
356 mail_storage_get_last_error
357 };
358
359 static Mailbox maildir_mailbox = {
360 NULL, /* name */
361 NULL, /* storage */
362
363 index_storage_close,
364 index_storage_get_status,
365 index_storage_sync,
366 index_storage_expunge,
367 index_storage_update_flags,
368 maildir_storage_copy,
369 index_storage_fetch,
370 index_storage_search,
371 maildir_storage_save,
372 mail_storage_is_inconsistency_error
373 };