]> git.lizzy.rs Git - plan9front.git/blob - sys/src/cmd/ip/imap4d/list.c
ip/torrent: remove unneeded assignment
[plan9front.git] / sys / src / cmd / ip / imap4d / list.c
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include "imap4d.h"
6
7 #define SUBSCRIBED      "imap.subscribed"
8
9 static int      matches(char *ref, char *pat, char *name);
10 static int      mayMatch(char *pat, char *name, int star);
11 static int      checkMatch(char *cmd, char *ref, char *pat, char *mbox, long mtime, int isdir);
12 static int      listAll(char *cmd, char *ref, char *pat, char *mbox, long mtime);
13 static int      listMatch(char *cmd, char *ref, char *pat, char *mbox, char *mm);
14 static int      mkSubscribed(void);
15
16 static long
17 listMtime(char *file)
18 {
19         Dir *d;
20         long mtime;
21
22         d = cdDirstat(mboxDir, file);
23         if(d == nil)
24                 return 0;
25         mtime = d->mtime;
26         free(d);
27         return mtime;
28 }
29
30 /*
31  * check for subscribed mailboxes
32  * each line is either a comment starting with #
33  * or is a subscribed mailbox name
34  */
35 int
36 lsubBoxes(char *cmd, char *ref, char *pat)
37 {
38         MbLock *mb;
39         Dir *d;
40         Biobuf bin;
41         char *s;
42         long mtime;
43         int fd, ok, isdir;
44
45         mb = mbLock();
46         if(mb == nil)
47                 return 0;
48         fd = cdOpen(mboxDir, SUBSCRIBED, OREAD);
49         if(fd < 0)
50                 fd = mkSubscribed();
51         if(fd < 0){
52                 mbUnlock(mb);
53                 return 0;
54         }
55         ok = 0;
56         Binit(&bin, fd, OREAD);
57         while(s = Brdline(&bin, '\n')){
58                 s[Blinelen(&bin) - 1] = '\0';
59                 if(s[0] == '#')
60                         continue;
61                 isdir = 1;
62                 if(cistrcmp(s, "INBOX") == 0){
63                         if(access("msgs", AEXIST) == 0)
64                                 mtime = listMtime("msgs");
65                         else
66                                 mtime = listMtime("mbox");
67                         isdir = 0;
68                 }else{
69                         d = cdDirstat(mboxDir, s);
70                         if(d != nil){
71                                 mtime = d->mtime;
72                                 if(!(d->mode & DMDIR))
73                                         isdir = 0;
74                                 free(d);
75                         }else
76                                 mtime = 0;
77                 }
78                 ok |= checkMatch(cmd, ref, pat, s, mtime, isdir);
79         }
80         Bterm(&bin);
81         close(fd);
82         mbUnlock(mb);
83         return ok;
84 }
85
86 static int
87 mkSubscribed(void)
88 {
89         int fd;
90
91         fd = cdCreate(mboxDir, SUBSCRIBED, ORDWR, 0664);
92         if(fd < 0)
93                 return -1;
94         fprint(fd, "#imap4 subscription list\nINBOX\n");
95         seek(fd, 0, 0);
96         return fd;
97 }
98
99 /*
100  * either subscribe or unsubscribe to a mailbox
101  */
102 int
103 subscribe(char *mbox, int how)
104 {
105         MbLock *mb;
106         char *s, *in, *ein;
107         int fd, tfd, ok, nmbox;
108
109         if(cistrcmp(mbox, "inbox") == 0)
110                 mbox = "INBOX";
111         mb = mbLock();
112         if(mb == nil)
113                 return 0;
114         fd = cdOpen(mboxDir, SUBSCRIBED, ORDWR);
115         if(fd < 0)
116                 fd = mkSubscribed();
117         if(fd < 0){
118                 mbUnlock(mb);
119                 return 0;
120         }
121         in = readFile(fd);
122         if(in == nil){
123                 mbUnlock(mb);
124                 return 0;
125         }
126         nmbox = strlen(mbox);
127         s = strstr(in, mbox);
128         while(s != nil && (s != in && s[-1] != '\n' || s[nmbox] != '\n'))
129                 s = strstr(s+1, mbox);
130         ok = 0;
131         if(how == 's' && s == nil){
132                 if(fprint(fd, "%s\n", mbox) > 0)
133                         ok = 1;
134         }else if(how == 'u' && s != nil){
135                 ein = strchr(s, '\0');
136                 memmove(s, &s[nmbox+1], ein - &s[nmbox+1]);
137                 ein -= nmbox+1;
138                 tfd = cdOpen(mboxDir, SUBSCRIBED, OWRITE|OTRUNC);
139                 if(tfd >= 0 && seek(fd, 0, 0) >= 0 && write(fd, in, ein-in) == ein-in)
140                         ok = 1;
141                 if(tfd > 0)
142                         close(tfd);
143         }else
144                 ok = 1;
145         close(fd);
146         mbUnlock(mb);
147         return ok;
148 }
149
150 /*
151  * stupidly complicated so that % doesn't read entire directory structure
152  * yet * works
153  * note: in most places, inbox is case-insensitive,
154  * but here INBOX is checked for a case-sensitve match.
155  */
156 int
157 listBoxes(char *cmd, char *ref, char *pat)
158 {
159         int ok;
160
161         ok = checkMatch(cmd, ref, pat, "INBOX", listMtime("mbox"), 0);
162         return ok | listMatch(cmd, ref, pat, ref, pat);
163 }
164
165 /*
166  * look for all messages which may match the pattern
167  * punt when a * is reached
168  */
169 static int
170 listMatch(char *cmd, char *ref, char *pat, char *mbox, char *mm)
171 {
172         Dir *dir, *dirs;
173         char *mdir, *m, *mb, *wc;
174         long mode;
175         int c, i, nmb, nmdir, nd, ok, fd;
176
177         mdir = nil;
178         for(m = mm; c = *m; m++){
179                 if(c == '%' || c == '*'){
180                         if(mdir == nil){
181                                 fd = cdOpen(mboxDir, ".", OREAD);
182                                 if(fd < 0)
183                                         return 0;
184                                 mbox = "";
185                                 nmdir = 0;
186                         }else{
187                                 *mdir = '\0';
188                                 fd = cdOpen(mboxDir, mbox, OREAD);
189                                 *mdir = '/';
190                                 nmdir = mdir - mbox + 1;
191                                 if(fd < 0)
192                                         return 0;
193                                 dir = dirfstat(fd);
194                                 if(dir == nil){
195                                         close(fd);
196                                         return 0;
197                                 }
198                                 mode = dir->mode;
199                                 free(dir);
200                                 if(!(mode & DMDIR))
201                                         break;
202                         }
203                         wc = m;
204                         for(; c = *m; m++)
205                                 if(c == '/')
206                                         break;
207                         nmb = nmdir + strlen(m) + MboxNameLen + 3;
208                         mb = emalloc(nmb);
209                         strncpy(mb, mbox, nmdir);
210                         ok = 0;
211                         while((nd = dirread(fd, &dirs)) > 0){
212                                 for(i = 0; i < nd; i++){
213                                         if(strcmp(mbox, "") == 0 &&
214                                             !okMbox(dirs[i].name))
215                                                 continue;
216                                         /* Safety: ignore message dirs */
217                                         if(strstr(dirs[i].name, "mails") != 0 ||
218                                            strcmp(dirs[i].name, "out") == 0 ||
219                                            strcmp(dirs[i].name, "obox") == 0 ||
220                                            strcmp(dirs[i].name, "ombox") == 0)
221                                                 continue;
222                                         if(strcmp(dirs[i].name, "msgs") == 0)
223                                                 dirs[i].mode &= ~DMDIR;
224                                         if(*wc == '*' && dirs[i].mode & DMDIR &&
225                                             mayMatch(mm, dirs[i].name, 1)){
226                                                 snprint(mb+nmdir, nmb-nmdir,
227                                                         "%s", dirs[i].name);
228                                                 ok |= listAll(cmd, ref, pat, mb,
229                                                         dirs[i].mtime);
230                                         }else if(mayMatch(mm, dirs[i].name, 0)){
231                                                 snprint(mb+nmdir, nmb-nmdir,
232                                                         "%s%s", dirs[i].name, m);
233                                                 if(*m == '\0')
234                                                         ok |= checkMatch(cmd,
235                                                                 ref, pat, mb,
236                                                                 dirs[i].mtime,
237                                                                 dirs[i].mode &
238                                                                 DMDIR);
239                                                 else if(dirs[i].mode & DMDIR)
240                                                         ok |= listMatch(cmd,
241                                                                 ref, pat, mb, mb
242                                                                 + nmdir + strlen(
243                                                                 dirs[i].name));
244                                         }
245                                 }
246                                 free(dirs);
247                         }
248                         close(fd);
249                         free(mb);
250                         return ok;
251                 }
252                 if(c == '/'){
253                         mdir = m;
254                         mm = m + 1;
255                 }
256         }
257         m = mbox;
258         if(*mbox == '\0')
259                 m = ".";
260         dir = cdDirstat(mboxDir, m);
261         if(dir == nil)
262                 return 0;
263         ok = checkMatch(cmd, ref, pat, mbox, dir->mtime, (dir->mode & DMDIR) == DMDIR);
264         free(dir);
265         return ok;
266 }
267
268 /*
269  * too hard: recursively list all files rooted at mbox,
270  * and list checkMatch figure it out
271  */
272 static int
273 listAll(char *cmd, char *ref, char *pat, char *mbox, long mtime)
274 {
275         Dir *dirs;
276         char *mb;
277         int i, nmb, nd, ok, fd;
278
279         ok = checkMatch(cmd, ref, pat, mbox, mtime, 1);
280         fd = cdOpen(mboxDir, mbox, OREAD);
281         if(fd < 0)
282                 return ok;
283
284         nmb = strlen(mbox) + MboxNameLen + 2;
285         mb = emalloc(nmb);
286         while((nd = dirread(fd, &dirs)) > 0){
287                 for(i = 0; i < nd; i++){
288                         snprint(mb, nmb, "%s/%s", mbox, dirs[i].name);
289                         /* safety: do not recurr */
290                         if(0 && dirs[i].mode & DMDIR)
291                                 ok |= listAll(cmd, ref, pat, mb, dirs[i].mtime);
292                         else
293                                 ok |= checkMatch(cmd, ref, pat, mb, dirs[i].mtime, 0);
294                 }
295                 free(dirs);
296         }
297         close(fd);
298         free(mb);
299         return ok;
300 }
301
302 static int
303 mayMatch(char *pat, char *name, int star)
304 {
305         Rune r;
306         int i, n;
307
308         for(; *pat && *pat != '/'; pat += n){
309                 r = *(uchar*)pat;
310                 if(r < Runeself)
311                         n = 1;
312                 else
313                         n = chartorune(&r, pat);
314
315                 if(r == '*' || r == '%'){
316                         pat += n;
317                         if(r == '*' && star || *pat == '\0' || *pat == '/')
318                                 return 1;
319                         while(*name){
320                                 if(mayMatch(pat, name, star))
321                                         return 1;
322                                 name += chartorune(&r, name);
323                         }
324                         return 0;
325                 }
326                 for(i = 0; i < n; i++)
327                         if(name[i] != pat[i])
328                                 return 0;
329                 name += n;
330         }
331         if(*name == '\0')
332                 return 1;
333         return 0;
334 }
335
336 /*
337  * mbox is a mailbox name which might match pat.
338  * verify the match
339  * generates response
340  */
341 static int
342 checkMatch(char *cmd, char *ref, char *pat, char *mbox, long mtime, int isdir)
343 {
344         char *s, *flags;
345
346         if(!matches(ref, pat, mbox) || !okMbox(mbox))
347                 return 0;
348         if(strcmp(mbox, ".") == 0)
349                 mbox = "";
350
351         if(isdir)
352                 flags = "(\\Noselect)";
353         else{
354                 s = impName(mbox);
355                 if(s != nil && listMtime(s) < mtime)
356                         flags = "(\\Noinferiors \\Marked)";
357                 else
358                         flags = "(\\Noinferiors)";
359         }
360
361         s = strmutf7(mbox);
362         if(s != nil)
363                 Bprint(&bout, "* %s %s \"/\" \"%s\"\r\n", cmd, flags, s);
364         return 1;
365 }
366
367 static int
368 matches(char *ref, char *pat, char *name)
369 {
370         Rune r;
371         int i, n;
372
373         while(ref != pat)
374                 if(*name++ != *ref++)
375                         return 0;
376         for(; *pat; pat += n){
377                 r = *(uchar*)pat;
378                 if(r < Runeself)
379                         n = 1;
380                 else
381                         n = chartorune(&r, pat);
382
383                 if(r == '*'){
384                         pat += n;
385                         if(*pat == '\0')
386                                 return 1;
387                         while(*name){
388                                 if(matches(pat, pat, name))
389                                         return 1;
390                                 name += chartorune(&r, name);
391                         }
392                         return 0;
393                 }
394                 if(r == '%'){
395                         pat += n;
396                         while(*name && *name != '/'){
397                                 if(matches(pat, pat, name))
398                                         return 1;
399                                 name += chartorune(&r, name);
400                         }
401                         pat -= n;
402                         continue;
403                 }
404                 for(i = 0; i < n; i++)
405                         if(name[i] != pat[i])
406                                 return 0;
407                 name += n;
408         }
409         if(*name == '\0')
410                 return 1;
411         return 0;
412 }