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 if(mLock.fd >= 0) 95 bye("mail lock deadlock"); 96 mLock.fd = openLocked(mboxDir, "L.mbox", OREAD); 97 if(mLock.fd >= 0) 98 return &mLock; 99 return nil; 100 } 101 102 void 103 mbUnlock(MbLock *ml) 104 { 105 if(ml != &mLock) 106 bye("bad mail unlock"); 107 if(ml->fd < 0) 108 bye("mail unlock when not locked"); 109 close(ml->fd); 110 ml->fd = -1; 111 } 112 113 void 114 mbLockRefresh(MbLock *ml) 115 { 116 char buf[1]; 117 118 seek(ml->fd, 0, 0); 119 read(ml->fd, buf, 1); 120 } 121 122 int 123 mbLocked(void) 124 { 125 return mLock.fd >= 0; 126 } 127 128 char* 129 impName(char *name) 130 { 131 char *s; 132 int n; 133 134 if(cistrcmp(name, "inbox") == 0) 135 name = "mbox"; 136 n = strlen(name) + STRLEN(".imp") + 1; 137 s = binalloc(&parseBin, n, 0); 138 if(s == nil) 139 return nil; 140 snprint(s, n, "%s.imp", name); 141 return s; 142 } 143 144 /* 145 * massage the mailbox name into something valid 146 * eliminates all .', and ..',s, redundatant and trailing /'s. 147 */ 148 char * 149 mboxName(char *s) 150 { 151 char *ss; 152 153 ss = mutf7str(s); 154 if(ss == nil) 155 return nil; 156 cleanname(ss); 157 return ss; 158 } 159 160 char * 161 strmutf7(char *s) 162 { 163 char *m; 164 int n; 165 166 n = strlen(s) * MUtf7Max + 1; 167 m = binalloc(&parseBin, n, 0); 168 if(m == nil) 169 return nil; 170 if(encmutf7(m, n, s) < 0) 171 return nil; 172 return m; 173 } 174 175 char * 176 mutf7str(char *s) 177 { 178 char *m; 179 int n; 180 181 /* 182 * n = strlen(s) * UTFmax / (2.67) + 1 183 * UTFMax / 2.67 == 3 / (8/3) == 9 / 8 184 */ 185 n = strlen(s); 186 n = (n * 9 + 7) / 8 + 1; 187 m = binalloc(&parseBin, n, 0); 188 if(m == nil) 189 return nil; 190 if(decmutf7(m, n, s) < 0) 191 return nil; 192 return m; 193 } 194 195 void 196 splitr(char *s, int c, char **left, char **right) 197 { 198 char *d; 199 int n; 200 201 n = strlen(s); 202 d = binalloc(&parseBin, n + 1, 0); 203 if(d == nil) 204 parseErr("out of memory"); 205 strcpy(d, s); 206 s = strrchr(d, c); 207 if(s != nil){ 208 *left = d; 209 *s++ = '\0'; 210 *right = s; 211 }else{ 212 *right = d; 213 *left = d + n; 214 } 215 } 216 217 /* 218 * create the mailbox and all intermediate components 219 * a trailing / implies the new mailbox is a directory; 220 * otherwise, it's a file. 221 * 222 * return with the file open for write, or directory open for read. 223 */ 224 int 225 createBox(char *mbox, int dir) 226 { 227 char *m; 228 int fd; 229 230 fd = -1; 231 for(m = mbox; *m; m++){ 232 if(*m == '/'){ 233 *m = '\0'; 234 if(access(mbox, AEXIST) < 0){ 235 if(fd >= 0) 236 close(fd); 237 fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775); 238 if(fd < 0) 239 return -1; 240 } 241 *m = '/'; 242 } 243 } 244 if(dir) 245 fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775); 246 else 247 fd = cdCreate(mboxDir, mbox, OWRITE, 0664); 248 return fd; 249 } 250 251 /* 252 * move one mail folder to another 253 * destination mailbox doesn't exist. 254 * the source folder may be a directory or a mailbox, 255 * and may be in the same directory as the destination, 256 * or a completely different directory. 257 */ 258 int 259 moveBox(char *from, char *to) 260 { 261 Dir *d; 262 char *fd, *fe, *td, *te, *fimp; 263 264 splitr(from, '/', &fd, &fe); 265 splitr(to, '/', &td, &te); 266 267 /* 268 * in the same directory: try rename 269 */ 270 d = cdDirstat(mboxDir, from); 271 if(d == nil) 272 return 0; 273 if(strcmp(fd, td) == 0){ 274 nulldir(d); 275 d->name = te; 276 if(cdDirwstat(mboxDir, from, d) >= 0){ 277 fimp = impName(from); 278 d->name = impName(te); 279 cdDirwstat(mboxDir, fimp, d); 280 free(d); 281 return 1; 282 } 283 } 284 285 /* 286 * directory copy is too hard for now 287 */ 288 if(d->mode & DMDIR) 289 return 0; 290 free(d); 291 292 return copyBox(from, to, 1); 293 } 294 295 /* 296 * copy the contents of one mailbox to another 297 * either truncates or removes the source box if it succeeds. 298 */ 299 int 300 copyBox(char *from, char *to, int doremove) 301 { 302 MbLock *ml; 303 char *fimp, *timp; 304 int ffd, tfd, ok; 305 306 if(cistrcmp(from, "inbox") == 0) 307 from = "mbox"; 308 309 ml = mbLock(); 310 if(ml == nil) 311 return 0; 312 ffd = openLocked(mboxDir, from, OREAD); 313 if(ffd < 0){ 314 mbUnlock(ml); 315 return 0; 316 } 317 tfd = createBox(to, 0); 318 if(tfd < 0){ 319 mbUnlock(ml); 320 close(ffd); 321 return 0; 322 } 323 324 ok = copyData(ffd, tfd, ml); 325 close(ffd); 326 close(tfd); 327 if(!ok){ 328 mbUnlock(ml); 329 return 0; 330 } 331 332 fimp = impName(from); 333 timp = impName(to); 334 if(fimp != nil && timp != nil){ 335 ffd = cdOpen(mboxDir, fimp, OREAD); 336 if(ffd >= 0){ 337 tfd = cdCreate(mboxDir, timp, OWRITE, 0664); 338 if(tfd >= 0){ 339 copyData(ffd, tfd, ml); 340 close(tfd); 341 } 342 close(ffd); 343 } 344 } 345 cdRemove(mboxDir, fimp); 346 if(doremove) 347 cdRemove(mboxDir, from); 348 else 349 close(cdOpen(mboxDir, from, OWRITE|OTRUNC)); 350 mbUnlock(ml); 351 return 1; 352 } 353 354 /* 355 * copies while holding the mail lock, 356 * then tries to copy permissions and group ownership 357 */ 358 static int 359 copyData(int ffd, int tfd, MbLock *ml) 360 { 361 Dir *fd, td; 362 char buf[BufSize]; 363 int n; 364 365 for(;;){ 366 n = read(ffd, buf, BufSize); 367 if(n <= 0){ 368 if(n < 0) 369 return 0; 370 break; 371 } 372 if(write(tfd, buf, n) != n) 373 return 0; 374 mbLockRefresh(ml); 375 } 376 fd = dirfstat(ffd); 377 if(fd != nil){ 378 nulldir(&td); 379 td.mode = fd->mode; 380 if(dirfwstat(tfd, &td) >= 0){ 381 nulldir(&td); 382 td.gid = fd->gid; 383 dirfwstat(tfd, &td); 384 } 385 } 386 return 1; 387 } 388