1 #include <u.h> 2 #include <libc.h> 3 #include <bio.h> 4 #include <auth.h> 5 #include "imap4d.h" 6 7 #define SUBSCRIBED "imap.subscribed" 8 9 static int matches(char *ref, char *pat, char *name); 10 static int mayMatch(char *pat, char *name, int star); 11 static int checkMatch(char *cmd, char *ref, char *pat, char *mbox, long mtime, int isdir); 12 static int listAll(char *cmd, char *ref, char *pat, char *mbox, long mtime); 13 static int listMatch(char *cmd, char *ref, char *pat, char *mbox, char *mm); 14 static int mkSubscribed(void); 15 16 static long 17 listMtime(char *file) 18 { 19 Dir *d; 20 long mtime; 21 22 d = cdDirstat(mboxDir, file); 23 if(d == nil) 24 return 0; 25 mtime = d->mtime; 26 free(d); 27 return mtime; 28 } 29 30 /* 31 * check for subscribed mailboxes 32 * each line is either a comment starting with # 33 * or is a subscribed mailbox name 34 */ 35 int 36 lsubBoxes(char *cmd, char *ref, char *pat) 37 { 38 MbLock *mb; 39 Dir *d; 40 Biobuf bin; 41 char *s; 42 long mtime; 43 int fd, ok, isdir; 44 45 mb = mbLock(); 46 if(mb == nil) 47 return 0; 48 fd = cdOpen(mboxDir, SUBSCRIBED, OREAD); 49 if(fd < 0) 50 fd = mkSubscribed(); 51 if(fd < 0){ 52 mbUnlock(mb); 53 return 0; 54 } 55 ok = 0; 56 Binit(&bin, fd, OREAD); 57 while(s = Brdline(&bin, '\n')){ 58 s[Blinelen(&bin) - 1] = '\0'; 59 if(s[0] == '#') 60 continue; 61 isdir = 1; 62 if(cistrcmp(s, "INBOX") == 0){ 63 if(access("msgs", AEXIST) == 0) 64 mtime = listMtime("msgs"); 65 else 66 mtime = listMtime("mbox"); 67 isdir = 0; 68 }else{ 69 d = cdDirstat(mboxDir, s); 70 if(d != nil){ 71 mtime = d->mtime; 72 if(!(d->mode & DMDIR)) 73 isdir = 0; 74 free(d); 75 }else 76 mtime = 0; 77 } 78 ok |= checkMatch(cmd, ref, pat, s, mtime, isdir); 79 } 80 Bterm(&bin); 81 close(fd); 82 mbUnlock(mb); 83 return ok; 84 } 85 86 static int 87 mkSubscribed(void) 88 { 89 int fd; 90 91 fd = cdCreate(mboxDir, SUBSCRIBED, ORDWR, 0664); 92 if(fd < 0) 93 return -1; 94 fprint(fd, "#imap4 subscription list\nINBOX\n"); 95 seek(fd, 0, 0); 96 return fd; 97 } 98 99 /* 100 * either subscribe or unsubscribe to a mailbox 101 */ 102 int 103 subscribe(char *mbox, int how) 104 { 105 MbLock *mb; 106 char *s, *in, *ein; 107 int fd, tfd, ok, nmbox; 108 109 if(cistrcmp(mbox, "inbox") == 0) 110 mbox = "INBOX"; 111 mb = mbLock(); 112 if(mb == nil) 113 return 0; 114 fd = cdOpen(mboxDir, SUBSCRIBED, ORDWR); 115 if(fd < 0) 116 fd = mkSubscribed(); 117 if(fd < 0){ 118 mbUnlock(mb); 119 return 0; 120 } 121 in = readFile(fd); 122 if(in == nil){ 123 mbUnlock(mb); 124 return 0; 125 } 126 nmbox = strlen(mbox); 127 s = strstr(in, mbox); 128 while(s != nil && (s != in && s[-1] != '\n' || s[nmbox] != '\n')) 129 s = strstr(s+1, mbox); 130 ok = 0; 131 if(how == 's' && s == nil){ 132 if(fprint(fd, "%s\n", mbox) > 0) 133 ok = 1; 134 }else if(how == 'u' && s != nil){ 135 ein = strchr(s, '\0'); 136 memmove(s, &s[nmbox+1], ein - &s[nmbox+1]); 137 ein -= nmbox+1; 138 tfd = cdOpen(mboxDir, SUBSCRIBED, OWRITE|OTRUNC); 139 if(tfd >= 0 && seek(fd, 0, 0) >= 0 && write(fd, in, ein-in) == ein-in) 140 ok = 1; 141 if(tfd > 0) 142 close(tfd); 143 }else 144 ok = 1; 145 close(fd); 146 mbUnlock(mb); 147 return ok; 148 } 149 150 /* 151 * stupidly complicated so that % doesn't read entire directory structure 152 * yet * works 153 * note: in most places, inbox is case-insensitive, 154 * but here INBOX is checked for a case-sensitve match. 155 */ 156 int 157 listBoxes(char *cmd, char *ref, char *pat) 158 { 159 int ok; 160 161 ok = checkMatch(cmd, ref, pat, "INBOX", listMtime("mbox"), 0); 162 return ok | listMatch(cmd, ref, pat, ref, pat); 163 } 164 165 /* 166 * look for all messages which may match the pattern 167 * punt when a * is reached 168 */ 169 static int 170 listMatch(char *cmd, char *ref, char *pat, char *mbox, char *mm) 171 { 172 Dir *dir, *dirs; 173 char *mdir, *m, *mb, *wc; 174 long mode; 175 int c, i, nmb, nmdir, nd, ok, fd; 176 177 mdir = nil; 178 for(m = mm; c = *m; m++){ 179 if(c == '%' || c == '*'){ 180 if(mdir == nil){ 181 fd = cdOpen(mboxDir, ".", OREAD); 182 if(fd < 0) 183 return 0; 184 mbox = ""; 185 nmdir = 0; 186 }else{ 187 *mdir = '\0'; 188 fd = cdOpen(mboxDir, mbox, OREAD); 189 *mdir = '/'; 190 nmdir = mdir - mbox + 1; 191 if(fd < 0) 192 return 0; 193 dir = dirfstat(fd); 194 if(dir == nil){ 195 close(fd); 196 return 0; 197 } 198 mode = dir->mode; 199 free(dir); 200 if(!(mode & DMDIR)) 201 break; 202 } 203 wc = m; 204 for(; c = *m; m++) 205 if(c == '/') 206 break; 207 nmb = nmdir + strlen(m) + MboxNameLen + 3; 208 mb = emalloc(nmb); 209 strncpy(mb, mbox, nmdir); 210 ok = 0; 211 while((nd = dirread(fd, &dirs)) > 0){ 212 for(i = 0; i < nd; i++){ 213 if(strcmp(mbox, "") == 0 && 214 !okMbox(dirs[i].name)) 215 continue; 216 /* Safety: ignore message dirs */ 217 if(strstr(dirs[i].name, "mails") != 0 || 218 strcmp(dirs[i].name, "out") == 0 || 219 strcmp(dirs[i].name, "obox") == 0 || 220 strcmp(dirs[i].name, "ombox") == 0) 221 continue; 222 if(strcmp(dirs[i].name, "msgs") == 0) 223 dirs[i].mode &= ~DMDIR; 224 if(*wc == '*' && dirs[i].mode & DMDIR && 225 mayMatch(mm, dirs[i].name, 1)){ 226 snprint(mb+nmdir, nmb-nmdir, 227 "%s", dirs[i].name); 228 ok |= listAll(cmd, ref, pat, mb, 229 dirs[i].mtime); 230 }else if(mayMatch(mm, dirs[i].name, 0)){ 231 snprint(mb+nmdir, nmb-nmdir, 232 "%s%s", dirs[i].name, m); 233 if(*m == '\0') 234 ok |= checkMatch(cmd, 235 ref, pat, mb, 236 dirs[i].mtime, 237 dirs[i].mode & 238 DMDIR); 239 else if(dirs[i].mode & DMDIR) 240 ok |= listMatch(cmd, 241 ref, pat, mb, mb 242 + nmdir + strlen( 243 dirs[i].name)); 244 } 245 } 246 free(dirs); 247 } 248 close(fd); 249 free(mb); 250 return ok; 251 } 252 if(c == '/'){ 253 mdir = m; 254 mm = m + 1; 255 } 256 } 257 m = mbox; 258 if(*mbox == '\0') 259 m = "."; 260 dir = cdDirstat(mboxDir, m); 261 if(dir == nil) 262 return 0; 263 ok = checkMatch(cmd, ref, pat, mbox, dir->mtime, (dir->mode & DMDIR) == DMDIR); 264 free(dir); 265 return ok; 266 } 267 268 /* 269 * too hard: recursively list all files rooted at mbox, 270 * and list checkMatch figure it out 271 */ 272 static int 273 listAll(char *cmd, char *ref, char *pat, char *mbox, long mtime) 274 { 275 Dir *dirs; 276 char *mb; 277 int i, nmb, nd, ok, fd; 278 279 ok = checkMatch(cmd, ref, pat, mbox, mtime, 1); 280 fd = cdOpen(mboxDir, mbox, OREAD); 281 if(fd < 0) 282 return ok; 283 284 nmb = strlen(mbox) + MboxNameLen + 2; 285 mb = emalloc(nmb); 286 while((nd = dirread(fd, &dirs)) > 0){ 287 for(i = 0; i < nd; i++){ 288 snprint(mb, nmb, "%s/%s", mbox, dirs[i].name); 289 /* safety: do not recurr */ 290 if(0 && dirs[i].mode & DMDIR) 291 ok |= listAll(cmd, ref, pat, mb, dirs[i].mtime); 292 else 293 ok |= checkMatch(cmd, ref, pat, mb, dirs[i].mtime, 0); 294 } 295 free(dirs); 296 } 297 close(fd); 298 free(mb); 299 return ok; 300 } 301 302 static int 303 mayMatch(char *pat, char *name, int star) 304 { 305 Rune r; 306 int i, n; 307 308 for(; *pat && *pat != '/'; pat += n){ 309 r = *(uchar*)pat; 310 if(r < Runeself) 311 n = 1; 312 else 313 n = chartorune(&r, pat); 314 315 if(r == '*' || r == '%'){ 316 pat += n; 317 if(r == '*' && star || *pat == '\0' || *pat == '/') 318 return 1; 319 while(*name){ 320 if(mayMatch(pat, name, star)) 321 return 1; 322 name += chartorune(&r, name); 323 } 324 return 0; 325 } 326 for(i = 0; i < n; i++) 327 if(name[i] != pat[i]) 328 return 0; 329 name += n; 330 } 331 if(*name == '\0') 332 return 1; 333 return 0; 334 } 335 336 /* 337 * mbox is a mailbox name which might match pat. 338 * verify the match 339 * generates response 340 */ 341 static int 342 checkMatch(char *cmd, char *ref, char *pat, char *mbox, long mtime, int isdir) 343 { 344 char *s, *flags; 345 346 if(!matches(ref, pat, mbox) || !okMbox(mbox)) 347 return 0; 348 if(strcmp(mbox, ".") == 0) 349 mbox = ""; 350 351 if(isdir) 352 flags = "(\\Noselect)"; 353 else{ 354 s = impName(mbox); 355 if(s != nil && listMtime(s) < mtime) 356 flags = "(\\Noinferiors \\Marked)"; 357 else 358 flags = "(\\Noinferiors)"; 359 } 360 361 s = strmutf7(mbox); 362 if(s != nil) 363 Bprint(&bout, "* %s %s \"/\" %s\r\n", cmd, flags, s); 364 return 1; 365 } 366 367 static int 368 matches(char *ref, char *pat, char *name) 369 { 370 Rune r; 371 int i, n; 372 373 while(ref != pat) 374 if(*name++ != *ref++) 375 return 0; 376 for(; *pat; pat += n){ 377 r = *(uchar*)pat; 378 if(r < Runeself) 379 n = 1; 380 else 381 n = chartorune(&r, pat); 382 383 if(r == '*'){ 384 pat += n; 385 if(*pat == '\0') 386 return 1; 387 while(*name){ 388 if(matches(pat, pat, name)) 389 return 1; 390 name += chartorune(&r, name); 391 } 392 return 0; 393 } 394 if(r == '%'){ 395 pat += n; 396 while(*name && *name != '/'){ 397 if(matches(pat, pat, name)) 398 return 1; 399 name += chartorune(&r, name); 400 } 401 pat -= n; 402 continue; 403 } 404 for(i = 0; i < n; i++) 405 if(name[i] != pat[i]) 406 return 0; 407 name += n; 408 } 409 if(*name == '\0') 410 return 1; 411 return 0; 412 } 413