1 /*
2 ** Copyright 2002-2003 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5
6 #if HAVE_CONFIG_H
7 #include "config.h"
8 #endif
9
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <time.h>
15 #if HAVE_UNISTD_H
16 #include <unistd.h>
17 #endif
18 #include <ctype.h>
19 #include <errno.h>
20 #include <stdio.h>
21 #if HAVE_DIRENT_H
22 #include <dirent.h>
23 #define NAMLEN(dirent) strlen((dirent)->d_name)
24 #else
25 #define dirent direct
26 #define NAMLEN(dirent) (dirent)->d_namlen
27 #if HAVE_SYS_NDIR_H
28 #include <sys/ndir.h>
29 #endif
30 #if HAVE_SYS_DIR_H
31 #include <sys/dir.h>
32 #endif
33 #if HAVE_NDIR_H
34 #include <ndir.h>
35 #endif
36 #endif
37
38 #include "maildirmisc.h"
39 #include "maildiraclt.h"
40
41 /* Hierarchical rename */
42
validrename(const char * oldname,const char * newname)43 static int validrename(const char *oldname, const char *newname)
44 {
45 size_t l=strlen(oldname);
46
47 if (strncmp(oldname, newname, l) == 0 &&
48 (newname[l] == 0 || newname[l] == '.'))
49 return (-1); /* Can't rename to its subdir */
50
51 if (strchr(oldname, '/') ||
52 strchr(newname, '/') ||
53 oldname[0] != '.' ||
54 newname[0] != '.' ||
55 strcmp(newname, ".") == 0)
56 return (-1);
57
58 while (*newname)
59 {
60 if (*newname == '.')
61 {
62 if (newname[1] == '.' || newname[1] == 0)
63 return -1;
64 }
65 ++newname;
66 }
67 return 0;
68 }
69
70 struct rename_list {
71 struct rename_list *next;
72 char *o, *n;
73 };
74
cmp_fn(const void * a,const void * b)75 static int cmp_fn(const void *a, const void *b)
76 {
77 return strcmp( (*(struct rename_list **)a)->o,
78 (*(struct rename_list **)b)->o);
79 }
80
81 static int scan_maildir_rename(const char *, const char *, const char *,
82 int,
83 struct rename_list **);
84
85 static int scan_aclhier_rename(const char *, const char *, const char *,
86 int,
87 struct rename_list **);
88
maildir_rename(const char * maildir,const char * oldname,const char * newname,int flags,void (* rename_func)(const char * old_path,const char * new_path))89 int maildir_rename(const char *maildir,
90 const char *oldname, const char *newname, int flags,
91 void (*rename_func)(const char *old_path,
92 const char *new_path))
93 {
94 struct rename_list *rl;
95 int rc= -1;
96
97 if (validrename(oldname, newname))
98 {
99 errno=EINVAL;
100 return (-1);
101 }
102
103 rl=NULL;
104
105 if (scan_maildir_rename(maildir, oldname, newname, flags, &rl) == 0 &&
106 scan_aclhier_rename(maildir, oldname+1, newname+1, flags, &rl)
107 == 0)
108 {
109 size_t n=0;
110 struct rename_list *p, **a;
111
112 for (p=rl; p; p=p->next)
113 ++n;
114
115 if ((a=malloc(sizeof(struct rename_list *)*(n+1))) != NULL)
116 {
117 n=0;
118 for (p=rl; p; p=p->next)
119 a[n++]=p;
120 a[n]=NULL;
121 if (n)
122 qsort(a, n, sizeof(*a), cmp_fn);
123
124
125 rc=0;
126 for (n=0; a[n]; n++)
127 {
128 if (rename(a[n]->o, a[n]->n))
129 {
130 rc= -1;
131 /* Try to undo the damage */
132
133 while (n)
134 {
135 --n;
136 rename(a[n]->n,
137 a[n]->o);
138 }
139 break;
140 }
141 if (rename_func)
142 (*rename_func)(a[n]->o,
143 a[n]->n);
144 }
145 free(a);
146 }
147 }
148 while (rl)
149 {
150 struct rename_list *p=rl;
151
152 rl=rl->next;
153 free(p->o);
154 free(p->n);
155 free(p);
156 }
157 return (rc);
158 }
159
add_rename_nochk(const char * on,const char * nn,struct rename_list ** rn)160 static int add_rename_nochk(const char *on, const char *nn,
161 struct rename_list **rn)
162 {
163 struct rename_list *p;
164
165 if ((p=malloc(sizeof(struct rename_list))) == NULL)
166 return -1;
167
168 if ((p->o=strdup(on)) == NULL)
169 {
170 free(p);
171 return -1;
172 }
173
174 if ((p->n=strdup(nn)) == NULL)
175 {
176 free(p);
177 return -1;
178 }
179
180 p->next= *rn;
181 *rn=p;
182 return 0;
183
184 }
185
add_rename(const char * on,const char * nn,struct rename_list ** rn)186 static int add_rename(const char *on, const char *nn,
187 struct rename_list **rn)
188 {
189 if (access(nn, 0) == 0)
190 {
191 errno=EEXIST;
192 return (-1); /* Destination folder exists */
193 }
194
195 return add_rename_nochk(on, nn, rn);
196 }
197
scan_maildir_rename(const char * maildir,const char * oldname,const char * newname,int flags,struct rename_list ** rename_list_head)198 static int scan_maildir_rename(const char *maildir,
199 const char *oldname,
200 const char *newname,
201 int flags,
202 struct rename_list **rename_list_head)
203 {
204 char *new_p;
205
206 int oldname_l=strlen(oldname);
207 DIR *dirp;
208 struct dirent *de;
209
210 dirp=opendir(maildir);
211 while (dirp && (de=readdir(dirp)) != 0)
212 {
213 char *tst_cur;
214
215 if (de->d_name[0] != '.')
216 continue;
217 if (strcmp(de->d_name, "..") == 0)
218 continue;
219
220 if ((tst_cur=malloc(strlen(maildir) + strlen(de->d_name)
221 + sizeof("//cur")))
222 == NULL)
223 {
224 closedir(dirp);
225 return (-1);
226 }
227 strcat(strcat(strcat(strcpy(tst_cur, maildir), "/"),
228 de->d_name), "/cur");
229 if (access(tst_cur, 0))
230 {
231 free(tst_cur);
232 continue;
233 }
234 if (strncmp(de->d_name, oldname, oldname_l))
235 {
236 free(tst_cur);
237 continue;
238 }
239
240 if (de->d_name[oldname_l] == 0)
241 {
242 if (!(flags & MAILDIR_RENAME_FOLDER))
243 {
244 free(tst_cur);
245 continue; /* Only the hierarchy */
246 }
247
248 strcat(strcat(strcpy(tst_cur, maildir), "/"),
249 de->d_name);
250
251 new_p=malloc(strlen(maildir)+sizeof("/")
252 +strlen(newname));
253
254 if (!new_p)
255 {
256 free(tst_cur);
257 closedir(dirp);
258 return (-1);
259 }
260
261 strcat(strcat(strcpy(new_p, maildir), "/"), newname);
262
263 if ( add_rename(tst_cur, new_p, rename_list_head))
264 {
265 free(new_p);
266 free(tst_cur);
267 closedir(dirp);
268 return (-1);
269 }
270 free(new_p);
271 free(tst_cur);
272 continue;
273 }
274 free(tst_cur);
275
276 if (de->d_name[oldname_l] != '.')
277 continue;
278
279 if (!(flags & MAILDIR_RENAME_SUBFOLDERS))
280 continue;
281
282 tst_cur=malloc(strlen(maildir) +
283 strlen(newname) + strlen(de->d_name+oldname_l)
284 + sizeof("/"));
285
286 if (!tst_cur)
287 {
288 closedir(dirp);
289 return (-1);
290 }
291
292 strcat(strcat(strcat(strcpy(tst_cur, maildir),
293 "/"), newname), de->d_name+oldname_l);
294
295 new_p=malloc(strlen(maildir) + strlen(de->d_name)
296 + sizeof("/"));
297
298 if (!new_p)
299 {
300 free(tst_cur);
301 closedir(dirp);
302 return (-1);
303 }
304
305 strcat(strcat(strcpy(new_p, maildir), "/"),
306 de->d_name);
307
308 if ( add_rename(new_p, tst_cur, rename_list_head))
309 {
310 free(new_p);
311 free(tst_cur);
312 closedir(dirp);
313 return (-1);
314 }
315 free(new_p);
316 free(tst_cur);
317 }
318 if (dirp) closedir(dirp);
319 return (0);
320 }
321
322 static int scan_aclhier2_rename(const char *aclhierdir,
323 const char *oldname,
324 const char *newname,
325 int flags,
326 struct rename_list **rename_list_head);
327
scan_aclhier_rename(const char * maildir,const char * oldname,const char * newname,int flags,struct rename_list ** rename_list_head)328 static int scan_aclhier_rename(const char *maildir,
329 const char *oldname,
330 const char *newname,
331 int flags,
332 struct rename_list **rename_list_head)
333 {
334
335 char *aclhier=malloc(strlen(maildir)+sizeof("/" ACLHIERDIR));
336 int rc;
337
338 if (!aclhier)
339 return -1;
340
341 rc=scan_aclhier2_rename(strcat(strcpy(aclhier, maildir),
342 "/" ACLHIERDIR), oldname, newname,
343 flags, rename_list_head);
344
345 free(aclhier);
346 return rc;
347 }
348
scan_aclhier2_rename(const char * aclhierdir,const char * oldname,const char * newname,int flags,struct rename_list ** rename_list_head)349 static int scan_aclhier2_rename(const char *aclhierdir,
350 const char *oldname,
351 const char *newname,
352 int flags,
353 struct rename_list **rename_list_head)
354 {
355 char *new_p;
356
357 int oldname_l=strlen(oldname);
358 DIR *dirp;
359 struct dirent *de;
360
361 dirp=opendir(aclhierdir);
362 while (dirp && (de=readdir(dirp)) != 0)
363 {
364 char *tst_cur;
365
366 if (de->d_name[0] == '.')
367 continue;
368
369 if (strncmp(de->d_name, oldname, oldname_l))
370 {
371 continue;
372 }
373
374 if ((tst_cur=malloc(strlen(aclhierdir) + strlen(de->d_name)
375 + sizeof("/")))
376 == NULL)
377 {
378 closedir(dirp);
379 return (-1);
380 }
381
382 if (de->d_name[oldname_l] == 0)
383 {
384 if (!(flags & MAILDIR_RENAME_FOLDER))
385 {
386 free(tst_cur);
387 continue; /* Only the hierarchy */
388 }
389
390 strcat(strcat(strcpy(tst_cur, aclhierdir), "/"),
391 de->d_name);
392
393 new_p=malloc(strlen(aclhierdir)+sizeof("/")
394 +strlen(newname));
395
396 if (!new_p)
397 {
398 free(tst_cur);
399 closedir(dirp);
400 return (-1);
401 }
402
403 strcat(strcat(strcpy(new_p, aclhierdir), "/"), newname);
404
405 if ( add_rename_nochk(tst_cur, new_p,
406 rename_list_head))
407 {
408 free(new_p);
409 free(tst_cur);
410 closedir(dirp);
411 return (-1);
412 }
413 free(new_p);
414 free(tst_cur);
415 continue;
416 }
417 free(tst_cur);
418
419 if (de->d_name[oldname_l] != '.')
420 continue;
421
422 if (!(flags & MAILDIR_RENAME_SUBFOLDERS))
423 continue;
424
425 tst_cur=malloc(strlen(aclhierdir) +
426 strlen(newname) + strlen(de->d_name+oldname_l)
427 + sizeof("/"));
428
429 if (!tst_cur)
430 {
431 closedir(dirp);
432 return (-1);
433 }
434
435 strcat(strcat(strcat(strcpy(tst_cur, aclhierdir),
436 "/"), newname), de->d_name+oldname_l);
437
438 new_p=malloc(strlen(aclhierdir) + strlen(de->d_name)
439 + sizeof("/"));
440
441 if (!new_p)
442 {
443 free(tst_cur);
444 closedir(dirp);
445 return (-1);
446 }
447
448 strcat(strcat(strcpy(new_p, aclhierdir), "/"),
449 de->d_name);
450
451 if ( add_rename_nochk(new_p, tst_cur, rename_list_head))
452 {
453 free(new_p);
454 free(tst_cur);
455 closedir(dirp);
456 return (-1);
457 }
458 free(new_p);
459 free(tst_cur);
460 }
461 if (dirp) closedir(dirp);
462 return (0);
463 }
464