1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include "imap4d.h"
6
7 static int copyData(int ffd, int tfd, MbLock *ml);
8 static MbLock mLock =
9 {
10 .fd = -1
11 };
12
13 static char curDir[MboxNameLen];
14
15 void
resetCurDir(void)16 resetCurDir(void)
17 {
18 curDir[0] = '\0';
19 }
20
21 int
myChdir(char * dir)22 myChdir(char *dir)
23 {
24 if(strcmp(dir, curDir) == 0)
25 return 0;
26 if(dir[0] != '/' || strlen(dir) > MboxNameLen)
27 return -1;
28 strcpy(curDir, dir);
29 if(chdir(dir) < 0){
30 werrstr("mychdir failed: %r");
31 return -1;
32 }
33 return 0;
34 }
35
36 int
cdCreate(char * dir,char * file,int mode,ulong perm)37 cdCreate(char *dir, char *file, int mode, ulong perm)
38 {
39 if(myChdir(dir) < 0)
40 return -1;
41 return create(file, mode, perm);
42 }
43
44 Dir*
cdDirstat(char * dir,char * file)45 cdDirstat(char *dir, char *file)
46 {
47 if(myChdir(dir) < 0)
48 return nil;
49 return dirstat(file);
50 }
51
52 int
cdExists(char * dir,char * file)53 cdExists(char *dir, char *file)
54 {
55 Dir *d;
56
57 d = cdDirstat(dir, file);
58 if(d == nil)
59 return 0;
60 free(d);
61 return 1;
62 }
63
64 int
cdDirwstat(char * dir,char * file,Dir * d)65 cdDirwstat(char *dir, char *file, Dir *d)
66 {
67 if(myChdir(dir) < 0)
68 return -1;
69 return dirwstat(file, d);
70 }
71
72 int
cdOpen(char * dir,char * file,int mode)73 cdOpen(char *dir, char *file, int mode)
74 {
75 if(myChdir(dir) < 0)
76 return -1;
77 return open(file, mode);
78 }
79
80 int
cdRemove(char * dir,char * file)81 cdRemove(char *dir, char *file)
82 {
83 if(myChdir(dir) < 0)
84 return -1;
85 return remove(file);
86 }
87
88 /*
89 * open the one true mail lock file
90 */
91 MbLock*
mbLock(void)92 mbLock(void)
93 {
94 int i;
95
96 if(mLock.fd >= 0)
97 bye("mail lock deadlock");
98 for(i = 0; i < 5; i++){
99 mLock.fd = openLocked(mboxDir, "L.mbox", OREAD);
100 if(mLock.fd >= 0)
101 return &mLock;
102 sleep(1000);
103 }
104 return nil;
105 }
106
107 void
mbUnlock(MbLock * ml)108 mbUnlock(MbLock *ml)
109 {
110 if(ml != &mLock)
111 bye("bad mail unlock");
112 if(ml->fd < 0)
113 bye("mail unlock when not locked");
114 close(ml->fd);
115 ml->fd = -1;
116 }
117
118 void
mbLockRefresh(MbLock * ml)119 mbLockRefresh(MbLock *ml)
120 {
121 char buf[1];
122
123 seek(ml->fd, 0, 0);
124 read(ml->fd, buf, 1);
125 }
126
127 int
mbLocked(void)128 mbLocked(void)
129 {
130 return mLock.fd >= 0;
131 }
132
133 char*
impName(char * name)134 impName(char *name)
135 {
136 char *s;
137 int n;
138
139 if(cistrcmp(name, "inbox") == 0)
140 if(access("msgs", AEXIST) == 0)
141 name = "msgs";
142 else
143 name = "mbox";
144 n = strlen(name) + STRLEN(".imp") + 1;
145 s = binalloc(&parseBin, n, 0);
146 if(s == nil)
147 return nil;
148 snprint(s, n, "%s.imp", name);
149 return s;
150 }
151
152 /*
153 * massage the mailbox name into something valid
154 * eliminates all .', and ..',s, redundatant and trailing /'s.
155 */
156 char *
mboxName(char * s)157 mboxName(char *s)
158 {
159 char *ss;
160
161 ss = mutf7str(s);
162 if(ss == nil)
163 return nil;
164 cleanname(ss);
165 return ss;
166 }
167
168 char *
strmutf7(char * s)169 strmutf7(char *s)
170 {
171 char *m;
172 int n;
173
174 n = strlen(s) * MUtf7Max + 1;
175 m = binalloc(&parseBin, n, 0);
176 if(m == nil)
177 return nil;
178 if(encmutf7(m, n, s) < 0)
179 return nil;
180 return m;
181 }
182
183 char *
mutf7str(char * s)184 mutf7str(char *s)
185 {
186 char *m;
187 int n;
188
189 /*
190 * n = strlen(s) * UTFmax / (2.67) + 1
191 * UTFMax / 2.67 == 3 / (8/3) == 9 / 8
192 */
193 n = strlen(s);
194 n = (n * 9 + 7) / 8 + 1;
195 m = binalloc(&parseBin, n, 0);
196 if(m == nil)
197 return nil;
198 if(decmutf7(m, n, s) < 0)
199 return nil;
200 return m;
201 }
202
203 void
splitr(char * s,int c,char ** left,char ** right)204 splitr(char *s, int c, char **left, char **right)
205 {
206 char *d;
207 int n;
208
209 n = strlen(s);
210 d = binalloc(&parseBin, n + 1, 0);
211 if(d == nil)
212 parseErr("out of memory");
213 strcpy(d, s);
214 s = strrchr(d, c);
215 if(s != nil){
216 *left = d;
217 *s++ = '\0';
218 *right = s;
219 }else{
220 *right = d;
221 *left = d + n;
222 }
223 }
224
225 /*
226 * create the mailbox and all intermediate components
227 * a trailing / implies the new mailbox is a directory;
228 * otherwise, it's a file.
229 *
230 * return with the file open for write, or directory open for read.
231 */
232 int
createBox(char * mbox,int dir)233 createBox(char *mbox, int dir)
234 {
235 char *m;
236 int fd;
237
238 fd = -1;
239 for(m = mbox; *m; m++){
240 if(*m == '/'){
241 *m = '\0';
242 if(access(mbox, AEXIST) < 0){
243 if(fd >= 0)
244 close(fd);
245 fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775);
246 if(fd < 0)
247 return -1;
248 }
249 *m = '/';
250 }
251 }
252 if(dir)
253 fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775);
254 else
255 fd = cdCreate(mboxDir, mbox, OWRITE, 0664);
256 return fd;
257 }
258
259 /*
260 * move one mail folder to another
261 * destination mailbox doesn't exist.
262 * the source folder may be a directory or a mailbox,
263 * and may be in the same directory as the destination,
264 * or a completely different directory.
265 */
266 int
moveBox(char * from,char * to)267 moveBox(char *from, char *to)
268 {
269 Dir *d;
270 char *fd, *fe, *td, *te, *fimp;
271
272 splitr(from, '/', &fd, &fe);
273 splitr(to, '/', &td, &te);
274
275 /*
276 * in the same directory: try rename
277 */
278 d = cdDirstat(mboxDir, from);
279 if(d == nil)
280 return 0;
281 if(strcmp(fd, td) == 0){
282 nulldir(d);
283 d->name = te;
284 if(cdDirwstat(mboxDir, from, d) >= 0){
285 fimp = impName(from);
286 d->name = impName(te);
287 cdDirwstat(mboxDir, fimp, d);
288 free(d);
289 return 1;
290 }
291 }
292
293 /*
294 * directory copy is too hard for now
295 */
296 if(d->mode & DMDIR)
297 return 0;
298 free(d);
299
300 return copyBox(from, to, 1);
301 }
302
303 /*
304 * copy the contents of one mailbox to another
305 * either truncates or removes the source box if it succeeds.
306 */
307 int
copyBox(char * from,char * to,int doremove)308 copyBox(char *from, char *to, int doremove)
309 {
310 MbLock *ml;
311 char *fimp, *timp;
312 int ffd, tfd, ok;
313
314 if(cistrcmp(from, "inbox") == 0)
315 if(access("msgs", AEXIST) == 0)
316 from = "msgs";
317 else
318 from = "mbox";
319
320 ml = mbLock();
321 if(ml == nil)
322 return 0;
323 ffd = openLocked(mboxDir, from, OREAD);
324 if(ffd < 0){
325 mbUnlock(ml);
326 return 0;
327 }
328 tfd = createBox(to, 0);
329 if(tfd < 0){
330 mbUnlock(ml);
331 close(ffd);
332 return 0;
333 }
334
335 ok = copyData(ffd, tfd, ml);
336 close(ffd);
337 close(tfd);
338 if(!ok){
339 mbUnlock(ml);
340 return 0;
341 }
342
343 fimp = impName(from);
344 timp = impName(to);
345 if(fimp != nil && timp != nil){
346 ffd = cdOpen(mboxDir, fimp, OREAD);
347 if(ffd >= 0){
348 tfd = cdCreate(mboxDir, timp, OWRITE, 0664);
349 if(tfd >= 0){
350 copyData(ffd, tfd, ml);
351 close(tfd);
352 }
353 close(ffd);
354 }
355 }
356 cdRemove(mboxDir, fimp);
357 if(doremove)
358 cdRemove(mboxDir, from);
359 else
360 close(cdOpen(mboxDir, from, OWRITE|OTRUNC));
361 mbUnlock(ml);
362 return 1;
363 }
364
365 /*
366 * copies while holding the mail lock,
367 * then tries to copy permissions and group ownership
368 */
369 static int
copyData(int ffd,int tfd,MbLock * ml)370 copyData(int ffd, int tfd, MbLock *ml)
371 {
372 Dir *fd, td;
373 char buf[BufSize];
374 int n;
375
376 for(;;){
377 n = read(ffd, buf, BufSize);
378 if(n <= 0){
379 if(n < 0)
380 return 0;
381 break;
382 }
383 if(write(tfd, buf, n) != n)
384 return 0;
385 mbLockRefresh(ml);
386 }
387 fd = dirfstat(ffd);
388 if(fd != nil){
389 nulldir(&td);
390 td.mode = fd->mode;
391 if(dirfwstat(tfd, &td) >= 0){
392 nulldir(&td);
393 td.gid = fd->gid;
394 dirfwstat(tfd, &td);
395 }
396 }
397 return 1;
398 }
399