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