]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/imap4d/folder.c
ip/torrent: remove unneeded assignment
[plan9front.git] / sys / src / cmd / ip / imap4d / folder.c
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
16 resetCurDir(void)
17 {
18         curDir[0] = '\0';
19 }
20
21 int
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
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*
45 cdDirstat(char *dir, char *file)
46 {
47         if(myChdir(dir) < 0)
48                 return nil;
49         return dirstat(file);
50 }
51
52 int
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
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
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
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*
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
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
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
128 mbLocked(void)
129 {
130         return mLock.fd >= 0;
131 }
132
133 char*
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 *
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 *
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 *
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
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
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
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
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
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 }