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 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 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 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 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 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 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