Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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
}