1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <auth.h> 5 #include "imap4d.h" 6 7 static int copyData(int ffd, int tfd, MbLock *ml); 8 static MbLock mLock = 9 { 10 .fd = -1 11 }; 12 13 static char curDir[MboxNameLen]; 14 15 void 16 resetCurDir(void) 17 { 18 curDir[0] = '\0'; 19 } 20 21 int 22 myChdir(char *dir) 23 { 24 if(strcmp(dir, curDir) == 0) 25 return 0; 26 if(dir[0] != '/' || strlen(dir) > MboxNameLen) 27 return -1; 28 strcpy(curDir, dir); 29 if(chdir(dir) < 0){ 30 werrstr("mychdir failed: %r"); 31 return -1; 32 } 33 return 0; 34 } 35 36 int 37 cdCreate(char *dir, char *file, int mode, ulong perm) 38 { 39 if(myChdir(dir) < 0) 40 return -1; 41 return create(file, mode, perm); 42 } 43 44 Dir* 45 cdDirstat(char *dir, char *file) 46 { 47 if(myChdir(dir) < 0) 48 return nil; 49 return dirstat(file); 50 } 51 52 int 53 cdExists(char *dir, char *file) 54 { 55 Dir *d; 56 57 d = cdDirstat(dir, file); 58 if(d == nil) 59 return 0; 60 free(d); 61 return 1; 62 } 63 64 int 65 cdDirwstat(char *dir, char *file, Dir *d) 66 { 67 if(myChdir(dir) < 0) 68 return -1; 69 return dirwstat(file, d); 70 } 71 72 int 73 cdOpen(char *dir, char *file, int mode) 74 { 75 if(myChdir(dir) < 0) 76 return -1; 77 return open(file, mode); 78 } 79 80 int 81 cdRemove(char *dir, char *file) 82 { 83 if(myChdir(dir) < 0) 84 return -1; 85 return remove(file); 86 } 87 88 /* 89 * open the one true mail lock file 90 */ 91 MbLock* 92 mbLock(void) 93 { 94 int i; 95 96 if(mLock.fd >= 0) 97 bye("mail lock deadlock"); 98 for(i = 0; i < 5; i++){ 99 mLock.fd = openLocked(mboxDir, "L.mbox", OREAD); 100 if(mLock.fd >= 0) 101 return &mLock; 102 sleep(1000); 103 } 104 return nil; 105 } 106 107 void 108 mbUnlock(MbLock *ml) 109 { 110 if(ml != &mLock) 111 bye("bad mail unlock"); 112 if(ml->fd < 0) 113 bye("mail unlock when not locked"); 114 close(ml->fd); 115 ml->fd = -1; 116 } 117 118 void 119 mbLockRefresh(MbLock *ml) 120 { 121 char buf[1]; 122 123 seek(ml->fd, 0, 0); 124 read(ml->fd, buf, 1); 125 } 126 127 int 128 mbLocked(void) 129 { 130 return mLock.fd >= 0; 131 } 132 133 char* 134 impName(char *name) 135 { 136 char *s; 137 int n; 138 139 if(cistrcmp(name, "inbox") == 0) 140 if(access("msgs", AEXIST) == 0) 141 name = "msgs"; 142 else 143 name = "mbox"; 144 n = strlen(name) + STRLEN(".imp") + 1; 145 s = binalloc(&parseBin, n, 0); 146 if(s == nil) 147 return nil; 148 snprint(s, n, "%s.imp", name); 149 return s; 150 } 151 152 /* 153 * massage the mailbox name into something valid 154 * eliminates all .', and ..',s, redundatant and trailing /'s. 155 */ 156 char * 157 mboxName(char *s) 158 { 159 char *ss; 160 161 ss = mutf7str(s); 162 if(ss == nil) 163 return nil; 164 cleanname(ss); 165 return ss; 166 } 167 168 char * 169 strmutf7(char *s) 170 { 171 char *m; 172 int n; 173 174 n = strlen(s) * MUtf7Max + 1; 175 m = binalloc(&parseBin, n, 0); 176 if(m == nil) 177 return nil; 178 if(encmutf7(m, n, s) < 0) 179 return nil; 180 return m; 181 } 182 183 char * 184 mutf7str(char *s) 185 { 186 char *m; 187 int n; 188 189 /* 190 * n = strlen(s) * UTFmax / (2.67) + 1 191 * UTFMax / 2.67 == 3 / (8/3) == 9 / 8 192 */ 193 n = strlen(s); 194 n = (n * 9 + 7) / 8 + 1; 195 m = binalloc(&parseBin, n, 0); 196 if(m == nil) 197 return nil; 198 if(decmutf7(m, n, s) < 0) 199 return nil; 200 return m; 201 } 202 203 void 204 splitr(char *s, int c, char **left, char **right) 205 { 206 char *d; 207 int n; 208 209 n = strlen(s); 210 d = binalloc(&parseBin, n + 1, 0); 211 if(d == nil) 212 parseErr("out of memory"); 213 strcpy(d, s); 214 s = strrchr(d, c); 215 if(s != nil){ 216 *left = d; 217 *s++ = '\0'; 218 *right = s; 219 }else{ 220 *right = d; 221 *left = d + n; 222 } 223 } 224 225 /* 226 * create the mailbox and all intermediate components 227 * a trailing / implies the new mailbox is a directory; 228 * otherwise, it's a file. 229 * 230 * return with the file open for write, or directory open for read. 231 */ 232 int 233 createBox(char *mbox, int dir) 234 { 235 char *m; 236 int fd; 237 238 fd = -1; 239 for(m = mbox; *m; m++){ 240 if(*m == '/'){ 241 *m = '\0'; 242 if(access(mbox, AEXIST) < 0){ 243 if(fd >= 0) 244 close(fd); 245 fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775); 246 if(fd < 0) 247 return -1; 248 } 249 *m = '/'; 250 } 251 } 252 if(dir) 253 fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775); 254 else 255 fd = cdCreate(mboxDir, mbox, OWRITE, 0664); 256 return fd; 257 } 258 259 /* 260 * move one mail folder to another 261 * destination mailbox doesn't exist. 262 * the source folder may be a directory or a mailbox, 263 * and may be in the same directory as the destination, 264 * or a completely different directory. 265 */ 266 int 267 moveBox(char *from, char *to) 268 { 269 Dir *d; 270 char *fd, *fe, *td, *te, *fimp; 271 272 splitr(from, '/', &fd, &fe); 273 splitr(to, '/', &td, &te); 274 275 /* 276 * in the same directory: try rename 277 */ 278 d = cdDirstat(mboxDir, from); 279 if(d == nil) 280 return 0; 281 if(strcmp(fd, td) == 0){ 282 nulldir(d); 283 d->name = te; 284 if(cdDirwstat(mboxDir, from, d) >= 0){ 285 fimp = impName(from); 286 d->name = impName(te); 287 cdDirwstat(mboxDir, fimp, d); 288 free(d); 289 return 1; 290 } 291 } 292 293 /* 294 * directory copy is too hard for now 295 */ 296 if(d->mode & DMDIR) 297 return 0; 298 free(d); 299 300 return copyBox(from, to, 1); 301 } 302 303 /* 304 * copy the contents of one mailbox to another 305 * either truncates or removes the source box if it succeeds. 306 */ 307 int 308 copyBox(char *from, char *to, int doremove) 309 { 310 MbLock *ml; 311 char *fimp, *timp; 312 int ffd, tfd, ok; 313 314 if(cistrcmp(from, "inbox") == 0) 315 if(access("msgs", AEXIST) == 0) 316 from = "msgs"; 317 else 318 from = "mbox"; 319 320 ml = mbLock(); 321 if(ml == nil) 322 return 0; 323 ffd = openLocked(mboxDir, from, OREAD); 324 if(ffd < 0){ 325 mbUnlock(ml); 326 return 0; 327 } 328 tfd = createBox(to, 0); 329 if(tfd < 0){ 330 mbUnlock(ml); 331 close(ffd); 332 return 0; 333 } 334 335 ok = copyData(ffd, tfd, ml); 336 close(ffd); 337 close(tfd); 338 if(!ok){ 339 mbUnlock(ml); 340 return 0; 341 } 342 343 fimp = impName(from); 344 timp = impName(to); 345 if(fimp != nil && timp != nil){ 346 ffd = cdOpen(mboxDir, fimp, OREAD); 347 if(ffd >= 0){ 348 tfd = cdCreate(mboxDir, timp, OWRITE, 0664); 349 if(tfd >= 0){ 350 copyData(ffd, tfd, ml); 351 close(tfd); 352 } 353 close(ffd); 354 } 355 } 356 cdRemove(mboxDir, fimp); 357 if(doremove) 358 cdRemove(mboxDir, from); 359 else 360 close(cdOpen(mboxDir, from, OWRITE|OTRUNC)); 361 mbUnlock(ml); 362 return 1; 363 } 364 365 /* 366 * copies while holding the mail lock, 367 * then tries to copy permissions and group ownership 368 */ 369 static int 370 copyData(int ffd, int tfd, MbLock *ml) 371 { 372 Dir *fd, td; 373 char buf[BufSize]; 374 int n; 375 376 for(;;){ 377 n = read(ffd, buf, BufSize); 378 if(n <= 0){ 379 if(n < 0) 380 return 0; 381 break; 382 } 383 if(write(tfd, buf, n) != n) 384 return 0; 385 mbLockRefresh(ml); 386 } 387 fd = dirfstat(ffd); 388 if(fd != nil){ 389 nulldir(&td); 390 td.mode = fd->mode; 391 if(dirfwstat(tfd, &td) >= 0){ 392 nulldir(&td); 393 td.gid = fd->gid; 394 dirfwstat(tfd, &td); 395 } 396 } 397 return 1; 398 } 399