Mercurial > dovecot > original-hg > dovecot-1.2
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 }; |