xref: /plan9/sys/src/cmd/ip/imap4d/copy.c (revision be0c1e85af510c365eaea6b4e03fd8ff93b34ae0)
1 #include <u.h>
2 #include <libc.h>
3 #include <bio.h>
4 #include <auth.h>
5 #include <libsec.h>
6 #include "imap4d.h"
7 
8 static int	saveMsg(char *dst, char *digest, int flags, char *head, int nhead, Biobuf *b, long n);
9 static int	saveb(int fd, DigestState *dstate, char *buf, int nr, int nw);
10 static long	appSpool(Biobuf *bout, Biobuf *bin, long n);
11 
12 /*
13  * check if the message exists
14  */
15 int
copyCheck(Box * box,Msg * m,int uids,void * v)16 copyCheck(Box *box, Msg *m, int uids, void *v)
17 {
18 	int fd;
19 
20 	USED(box);
21 	USED(uids);
22 	USED(v);
23 
24 	if(m->expunged)
25 		return 0;
26 	fd = msgFile(m, "raw");
27 	if(fd < 0){
28 		msgDead(m);
29 		return 0;
30 	}
31 	close(fd);
32 	return 1;
33 }
34 
35 int
copySave(Box * box,Msg * m,int uids,void * vs)36 copySave(Box *box, Msg *m, int uids, void *vs)
37 {
38 	Dir *d;
39 	Biobuf b;
40 	vlong length;
41 	char *head;
42 	int ok, hfd, bfd, nhead;
43 
44 	USED(box);
45 	USED(uids);
46 
47 	if(m->expunged)
48 		return 0;
49 
50 	hfd = msgFile(m, "unixheader");
51 	if(hfd < 0){
52 		msgDead(m);
53 		return 0;
54 	}
55 	head = readFile(hfd);
56 	if(head == nil){
57 		close(hfd);
58 		return 0;
59 	}
60 
61 	/*
62 	 * clear out the header if it doesn't end in a newline,
63 	 * since it is a runt and the "header" will show up in the raw file.
64 	 */
65 	nhead = strlen(head);
66 	if(nhead > 0 && head[nhead-1] != '\n')
67 		nhead = 0;
68 
69 	bfd = msgFile(m, "raw");
70 	close(hfd);
71 	if(bfd < 0){
72 		msgDead(m);
73 		return 0;
74 	}
75 
76 	d = dirfstat(bfd);
77 	if(d == nil){
78 		close(bfd);
79 		return 0;
80 	}
81 	length = d->length;
82 	free(d);
83 
84 	Binit(&b, bfd, OREAD);
85 	ok = saveMsg(vs, m->info[IDigest], m->flags, head, nhead, &b, length);
86 	Bterm(&b);
87 	close(bfd);
88 	return ok;
89 }
90 
91 /*
92  * first spool the input into a temorary file,
93  * and massage the input in the process.
94  * then save to real box.
95  */
96 int
appendSave(char * mbox,int flags,char * head,Biobuf * b,long n)97 appendSave(char *mbox, int flags, char *head, Biobuf *b, long n)
98 {
99 	Biobuf btmp;
100 	int fd, ok;
101 
102 	fd = imapTmp();
103 	if(fd < 0)
104 		return 0;
105 	Bprint(&bout, "+ Ready for literal data\r\n");
106 	if(Bflush(&bout) < 0)
107 		writeErr();
108 	Binit(&btmp, fd, OWRITE);
109 	n = appSpool(&btmp, b, n);
110 	Bterm(&btmp);
111 	if(n < 0){
112 		close(fd);
113 		return 0;
114 	}
115 
116 	seek(fd, 0, 0);
117 	Binit(&btmp, fd, OREAD);
118 	ok = saveMsg(mbox, nil, flags, head, strlen(head), &btmp, n);
119 	Bterm(&btmp);
120 	close(fd);
121 	return ok;
122 }
123 
124 /*
125  * copy from bin to bout,
126  * mapping "\r\n" to "\n" and "\nFrom " to "\n From "
127  * return the number of bytes in the mapped file.
128  *
129  * exactly n bytes must be read from the input,
130  * unless an input error occurs.
131  */
132 static long
appSpool(Biobuf * bout,Biobuf * bin,long n)133 appSpool(Biobuf *bout, Biobuf *bin, long n)
134 {
135 	int i, c;
136 
137 	c = '\n';
138 	while(n > 0){
139 		if(c == '\n' && n >= STRLEN("From ")){
140 			for(i = 0; i < STRLEN("From "); i++){
141 				c = Bgetc(bin);
142 				if(c != "From "[i]){
143 					if(c < 0)
144 						return -1;
145 					Bungetc(bin);
146 					break;
147 				}
148 				n--;
149 			}
150 			if(i == STRLEN("From "))
151 				Bputc(bout, ' ');
152 			Bwrite(bout, "From ", i);
153 		}
154 		c = Bgetc(bin);
155 		n--;
156 		if(c == '\r' && n-- > 0){
157 			c = Bgetc(bin);
158 			if(c != '\n')
159 				Bputc(bout, '\r');
160 		}
161 		if(c < 0)
162 			return -1;
163 		if(Bputc(bout, c) < 0)
164 			return -1;
165 	}
166 	if(c != '\n')
167 		Bputc(bout, '\n');
168 	if(Bflush(bout) < 0)
169 		return -1;
170 	return Boffset(bout);
171 }
172 
173 static int
saveMsg(char * dst,char * digest,int flags,char * head,int nhead,Biobuf * b,long n)174 saveMsg(char *dst, char *digest, int flags, char *head, int nhead, Biobuf *b, long n)
175 {
176 	DigestState *dstate;
177 	MbLock *ml;
178 	uchar shadig[SHA1dlen];
179 	char buf[BufSize + 1], digbuf[NDigest + 1];
180 	int i, fd, nr, nw, ok;
181 
182 	ml = mbLock();
183 	if(ml == nil)
184 		return 0;
185 	fd = openLocked(mboxDir, dst, OWRITE);
186 	if(fd < 0){
187 		mbUnlock(ml);
188 		return 0;
189 	}
190 	seek(fd, 0, 2);
191 
192 	dstate = nil;
193 	if(digest == nil)
194 		dstate = sha1(nil, 0, nil, nil);
195 	if(!saveb(fd, dstate, head, nhead, nhead)){
196 		if(dstate != nil)
197 			sha1(nil, 0, shadig, dstate);
198 		mbUnlock(ml);
199 		close(fd);
200 		return 0;
201 	}
202 	ok = 1;
203 	if(n == 0)
204 		ok = saveb(fd, dstate, "\n", 0, 1);
205 	while(n > 0){
206 		nr = n;
207 		if(nr > BufSize)
208 			nr = BufSize;
209 		nr = Bread(b, buf, nr);
210 		if(nr <= 0){
211 			saveb(fd, dstate, "\n\n", 0, 2);
212 			ok = 0;
213 			break;
214 		}
215 		n -= nr;
216 		nw = nr;
217 		if(n == 0){
218 			if(buf[nw - 1] != '\n')
219 				buf[nw++] = '\n';
220 			buf[nw++] = '\n';
221 		}
222 		if(!saveb(fd, dstate, buf, nr, nw)){
223 			ok = 0;
224 			break;
225 		}
226 		mbLockRefresh(ml);
227 	}
228 	close(fd);
229 
230 	if(dstate != nil){
231 		digest = digbuf;
232 		sha1(nil, 0, shadig, dstate);
233 		for(i = 0; i < SHA1dlen; i++)
234 			snprint(digest+2*i, NDigest+1-2*i, "%2.2ux", shadig[i]);
235 	}
236 	if(ok){
237 		fd = cdOpen(mboxDir, impName(dst), OWRITE);
238 		if(fd < 0)
239 			fd = emptyImp(dst);
240 		if(fd >= 0){
241 			seek(fd, 0, 2);
242 			wrImpFlags(buf, flags, 1);
243 			fprint(fd, "%.*s %.*lud %s\n", NDigest, digest, NUid, 0UL, buf);
244 			close(fd);
245 		}
246 	}
247 	mbUnlock(ml);
248 	return 1;
249 }
250 
251 static int
saveb(int fd,DigestState * dstate,char * buf,int nr,int nw)252 saveb(int fd, DigestState *dstate, char *buf, int nr, int nw)
253 {
254 	if(dstate != nil)
255 		sha1((uchar*)buf, nr, nil, dstate);
256 	if(write(fd, buf, nw) != nw)
257 		return 0;
258 	return 1;
259 }
260