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