Subversion Repositories planix.SVN

Rev

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

Rev Author Line No. Line
2 - 1
/*
2
 * Plan B (mail2fs) mail box format.
3
 *
4
 * BUG: this does not reconstruct the
5
 * raw text for attachments.  So imap and others
6
 * will be unable to access any attachment using upas/fs.
7
 * As an aid, we add the path to the message directory
8
 * to the message body, so the user could build the path
9
 * for any attachment and open it.
10
 */
11
 
12
#include "common.h"
13
#include <ctype.h>
14
#include <plumb.h>
15
#include <libsec.h>
16
#include "dat.h"
17
 
18
static int
19
readmessage(Message *m, char *msg)
20
{
21
	int fd, i, n;
22
	char *buf, *name, *p;
23
	char hdr[128], sdigest[SHA1dlen*2+1];
24
	Dir *d;
25
 
26
	buf = nil;
27
	d = nil;
28
	name = smprint("%s/raw", msg);
29
	if(name == nil)
30
		return -1;
31
	if(m->filename != nil)
32
		s_free(m->filename);
33
	m->filename = s_copy(name);
34
	fd = open(name, OREAD);
35
	if(fd < 0)
36
		goto Fail;
37
	n = read(fd, hdr, sizeof(hdr)-1);
38
	if(n <= 0)
39
		goto Fail;
40
	hdr[n] = 0;
41
	close(fd);
42
	fd = -1;
43
	p = strchr(hdr, '\n');
44
	if(p != nil)
45
		*++p = 0;
46
	if(strncmp(hdr, "From ", 5) != 0)
47
		goto Fail;
48
	free(name);
49
	name = smprint("%s/text", msg);
50
	if(name == nil)
51
		goto Fail;
52
	fd = open(name, OREAD);
53
	if(fd < 0)
54
		goto Fail;
55
	d = dirfstat(fd);
56
	if(d == nil)
57
		goto Fail;
58
	buf = malloc(strlen(hdr) + d->length + strlen(msg) + 10); /* few extra chars */
59
	if(buf == nil)
60
		goto Fail;
61
	strcpy(buf, hdr);
62
	p = buf+strlen(hdr);
63
	n = readn(fd, p, d->length);
64
	if(n < 0)
65
		goto Fail;
66
	sprint(p+n, "\n[%s]\n", msg);
67
	n += 2 + strlen(msg) + 2;
68
	close(fd);
69
	free(name);
70
	free(d);
71
	free(m->start);
72
	m->start = buf;
73
	m->lim = m->end = p+n;
74
	if(*(m->end-1) == '\n')
75
		m->end--;
76
	*m->end = 0;
77
	m->bend = m->rbend = m->end;
78
	sha1((uchar*)m->start, m->end - m->start, m->digest, nil);
79
	for(i = 0; i < SHA1dlen; i++)
80
		sprint(sdigest+2*i, "%2.2ux", m->digest[i]);
81
	m->sdigest = s_copy(sdigest);
82
	return 0;
83
Fail:
84
	if(fd >= 0)
85
		close(fd);
86
	free(name);
87
	free(buf);
88
	free(d);
89
	return -1;
90
}
91
 
92
/*
93
 * Deleted messages are kept as spam instead.
94
 */
95
static void
96
archive(Message *m)
97
{
98
	char *dir, *p, *nname;
99
	Dir d;
100
 
101
	dir = strdup(s_to_c(m->filename));
102
	nname = nil;
103
	if(dir == nil)
104
		return;
105
	p = strrchr(dir, '/');
106
	if(p == nil)
107
		goto Fail;
108
	*p = 0;
109
	p = strrchr(dir, '/');
110
	if(p == nil)
111
		goto Fail;
112
	p++;
113
	if(*p < '0' || *p > '9')
114
		goto Fail;
115
	nname = smprint("s.%s", p);
116
	if(nname == nil)
117
		goto Fail;
118
	nulldir(&d);
119
	d.name = nname;
120
	dirwstat(dir, &d);
121
Fail:
122
	free(dir);
123
	free(nname);
124
}
125
 
126
int
127
purgembox(Mailbox *mb, int virtual)
128
{
129
	Message *m, *next;
130
	int newdels;
131
 
132
	/* forget about what's no longer in the mailbox */
133
	newdels = 0;
134
	for(m = mb->root->part; m != nil; m = next){
135
		next = m->next;
136
		if(m->deleted > 0 && m->refs == 0){
137
			if(m->inmbox){
138
				newdels++;
139
				/*
140
				 * virtual folders are virtual,
141
				 * we do not archive
142
				 */
143
				if(virtual == 0)
144
					archive(m);
145
			}
146
			delmessage(mb, m);
147
		}
148
	}
149
	return newdels;
150
}
151
 
152
static int
153
mustshow(char* name)
154
{
155
	if(isdigit(name[0]))
156
		return 1;
157
	if(0 && name[0] == 'a' && name[1] == '.')
158
		return 1;
159
	if(0 && name[0] == 's' && name[1] == '.')
160
		return 1;
161
	return 0;
162
}
163
 
164
static int
165
readpbmessage(Mailbox *mb, char *msg, int doplumb)
166
{
167
	Message *m, **l;
168
	char *x;
169
 
170
	m = newmessage(mb->root);
171
	m->mallocd = 1;
172
	m->inmbox = 1;
173
	if(readmessage(m, msg) < 0){
174
		delmessage(mb, m);
175
		mb->root->subname--;
176
		return -1;
177
	}
178
	for(l = &mb->root->part; *l != nil; l = &(*l)->next)
179
		if(strcmp(s_to_c((*l)->filename), s_to_c(m->filename)) == 0 &&
180
		    *l != m){
181
			if((*l)->deleted < 0)
182
				(*l)->deleted = 0;
183
			delmessage(mb, m);
184
			mb->root->subname--;
185
			return -1;
186
		}
187
	x = strchr(m->start, '\n');
188
	if(x == nil)
189
		m->header = m->end;
190
	else
191
		m->header = x + 1;
192
	m->mheader = m->mhend = m->header;
193
	parseunix(m);
194
	parse(m, 0, mb, 0);
195
	logmsg("new", m);
196
 
197
	/* chain in */
198
	*l = m;
199
	if(doplumb)
200
		mailplumb(mb, m, 0);
201
	return 0;
202
}
203
 
204
static int
205
dcmp(Dir *a, Dir *b)
206
{
207
	char *an, *bn;
208
 
209
	an = a->name;
210
	bn = b->name;
211
	if(an[0] != 0 && an[1] == '.')
212
		an += 2;
213
	if(bn[0] != 0 && bn[1] == '.')
214
		bn += 2;
215
	return strcmp(an, bn);
216
}
217
 
218
static void
219
readpbmbox(Mailbox *mb, int doplumb)
220
{
221
	int fd, i, j, nd, nmd;
222
	char *month, *msg;
223
	Dir *d, *md;
224
 
225
	fd = open(mb->path, OREAD);
226
	if(fd < 0){
227
		fprint(2, "%s: %s: %r\n", argv0, mb->path);
228
		return;
229
	}
230
	nd = dirreadall(fd, &d);
231
	close(fd);
232
	if(nd > 0)
233
		qsort(d, nd, sizeof d[0], (int (*)(void*, void*))dcmp);
234
	for(i = 0; i < nd; i++){
235
		month = smprint("%s/%s", mb->path, d[i].name);
236
		if(month == nil)
237
			break;
238
		fd = open(month, OREAD);
239
		if(fd < 0){
240
			fprint(2, "%s: %s: %r\n", argv0, month);
241
			free(month);
242
			continue;
243
		}
244
		md = dirfstat(fd);
245
		if(md != nil && (md->qid.type & QTDIR) != 0){
246
			free(md);
247
			md = nil;
248
			nmd = dirreadall(fd, &md);
249
			for(j = 0; j < nmd; j++)
250
				if(mustshow(md[j].name)){
251
					msg = smprint("%s/%s", month, md[j].name);
252
					readpbmessage(mb, msg, doplumb);
253
					free(msg);
254
				}
255
		}
256
		close(fd);
257
		free(month);
258
		free(md);
259
		md = nil;
260
	}
261
	free(d);
262
}
263
 
264
static void
265
readpbvmbox(Mailbox *mb, int doplumb)
266
{
267
	int fd, nr;
268
	long sz;
269
	char *data, *ln, *p, *nln, *msg;
270
	Dir *d;
271
 
272
	fd = open(mb->path, OREAD);
273
	if(fd < 0){
274
		fprint(2, "%s: %s: %r\n", argv0, mb->path);
275
		return;
276
	}
277
	d = dirfstat(fd);
278
	if(d == nil){
279
		fprint(2, "%s: %s: %r\n", argv0, mb->path);
280
		close(fd);
281
		return;
282
	}
283
	sz = d->length;
284
	free(d);
285
	if(sz > 2 * 1024 * 1024){
286
		sz = 2 * 1024 * 1024;
287
		fprint(2, "%s: %s: bug: folder too big\n", argv0, mb->path);
288
	}
289
	data = malloc(sz+1);
290
	if(data == nil){
291
		close(fd);
292
		fprint(2, "%s: no memory\n", argv0);
293
		return;
294
	}
295
	nr = readn(fd, data, sz);
296
	close(fd);
297
	if(nr < 0){
298
		fprint(2, "%s: %s: %r\n", argv0, mb->path);
299
		free(data);
300
		return;
301
	}
302
	data[nr] = 0;
303
 
304
	for(ln = data; *ln != 0; ln = nln){
305
		nln = strchr(ln, '\n');
306
		if(nln != nil)
307
			*nln++ = 0;
308
		else
309
			nln = ln + strlen(ln);
310
		p = strchr(ln , ' ');
311
		if(p != nil)
312
			*p = 0;
313
		p = strchr(ln, '\t');
314
		if(p != nil)
315
			*p = 0;
316
		p = strstr(ln, "/text");
317
		if(p != nil)
318
			*p = 0;
319
		msg = smprint("/mail/box/%s/msgs/%s", user, ln);
320
		if(msg == nil){
321
			fprint(2, "%s: no memory\n", argv0);
322
			continue;
323
		}
324
		readpbmessage(mb, msg, doplumb);
325
		free(msg);
326
	}
327
	free(data);
328
}
329
 
330
static char*
331
readmbox(Mailbox *mb, int doplumb, int virt)
332
{
333
	int fd;
334
	Dir *d;
335
	Message *m;
336
	static char err[Errlen];
337
 
338
	if(debug)
339
		fprint(2, "read mbox %s\n", mb->path);
340
	fd = open(mb->path, OREAD);
341
	if(fd < 0){
342
		errstr(err, sizeof(err));
343
		return err;
344
	}
345
 
346
	d = dirfstat(fd);
347
	if(d == nil){
348
		close(fd);
349
		errstr(err, sizeof(err));
350
		return err;
351
	}
352
	if(mb->d != nil){
353
		if(d->qid.path == mb->d->qid.path &&
354
		   d->qid.vers == mb->d->qid.vers){
355
			close(fd);
356
			free(d);
357
			return nil;
358
		}
359
		free(mb->d);
360
	}
361
	close(fd);
362
	mb->d = d;
363
	mb->vers++;
364
	henter(PATH(0, Qtop), mb->name,
365
		(Qid){PATH(mb->id, Qmbox), mb->vers, QTDIR}, nil, mb);
366
	snprint(err, sizeof err, "reading '%s'", mb->path);
367
	logmsg(err, nil);
368
 
369
	for(m = mb->root->part; m != nil; m = m->next)
370
		if(m->deleted == 0)
371
			m->deleted = -1;
372
	if(virt == 0)
373
		readpbmbox(mb, doplumb);
374
	else
375
		readpbvmbox(mb, doplumb);
376
 
377
	/*
378
	 * messages removed from the mbox; flag them to go.
379
	 */
380
	for(m = mb->root->part; m != nil; m = m->next)
381
		if(m->deleted < 0 && doplumb){
382
			m->inmbox = 0;
383
			m->deleted = 1;
384
			mailplumb(mb, m, 1);
385
		}
386
	logmsg("mbox read", nil);
387
	return nil;
388
}
389
 
390
static char*
391
mbsync(Mailbox *mb, int doplumb)
392
{
393
	char *rv;
394
 
395
	rv = readmbox(mb, doplumb, 0);
396
	purgembox(mb, 0);
397
	return rv;
398
}
399
 
400
static char*
401
mbvsync(Mailbox *mb, int doplumb)
402
{
403
	char *rv;
404
 
405
	rv = readmbox(mb, doplumb, 1);
406
	purgembox(mb, 1);
407
	return rv;
408
}
409
 
410
char*
411
planbmbox(Mailbox *mb, char *path)
412
{
413
	char *list;
414
	static char err[64];
415
 
416
	if(access(path, AEXIST) < 0)
417
		return Enotme;
418
	list = smprint("%s/list", path);
419
	if(access(list, AEXIST) < 0){
420
		free(list);
421
		return Enotme;
422
	}
423
	free(list);
424
	mb->sync = mbsync;
425
	if(debug)
426
		fprint(2, "planb mbox %s\n", path);
427
	return nil;
428
}
429
 
430
char*
431
planbvmbox(Mailbox *mb, char *path)
432
{
433
	int fd, nr, i;
434
	char buf[64];
435
	static char err[64];
436
 
437
	fd = open(path, OREAD);
438
	if(fd < 0)
439
		return Enotme;
440
	nr = read(fd, buf, sizeof(buf)-1);
441
	close(fd);
442
	if(nr < 7)
443
		return Enotme;
444
	buf[nr] = 0;
445
	for(i = 0; i < 6; i++)
446
		if(buf[i] < '0' || buf[i] > '9')
447
			return Enotme;
448
	if(buf[6] != '/')
449
		return Enotme;
450
	mb->sync = mbvsync;
451
	if(debug)
452
		fprint(2, "planb virtual mbox %s\n", path);
453
	return nil;
454
}