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