17dd7cddfSDavid du Colombier #include <u.h> 27dd7cddfSDavid du Colombier #include <libc.h> 37dd7cddfSDavid du Colombier #include <bio.h> 47dd7cddfSDavid du Colombier #include "imap4d.h" 57dd7cddfSDavid du Colombier 67dd7cddfSDavid du Colombier #define SUBSCRIBED "imap.subscribed" 77dd7cddfSDavid du Colombier 87dd7cddfSDavid du Colombier static int matches(char *ref, char *pat, char *name); 97dd7cddfSDavid du Colombier static int mayMatch(char *pat, char *name, int star); 107dd7cddfSDavid du Colombier static int checkMatch(char *cmd, char *ref, char *pat, char *mbox, long mtime, int isdir); 117dd7cddfSDavid du Colombier static int listAll(char *cmd, char *ref, char *pat, char *mbox, long mtime); 127dd7cddfSDavid du Colombier static int listMatch(char *cmd, char *ref, char *pat, char *mbox, char *mm); 137dd7cddfSDavid du Colombier static int mkSubscribed(void); 147dd7cddfSDavid du Colombier 157dd7cddfSDavid du Colombier static long 167dd7cddfSDavid du Colombier listMtime(char *file) 177dd7cddfSDavid du Colombier { 187dd7cddfSDavid du Colombier Dir d; 197dd7cddfSDavid du Colombier 207dd7cddfSDavid du Colombier if(cdDirstat(mboxDir, file, &d) < 0) 217dd7cddfSDavid du Colombier return 0; 227dd7cddfSDavid du Colombier return d.mtime; 237dd7cddfSDavid du Colombier } 247dd7cddfSDavid du Colombier 257dd7cddfSDavid du Colombier /* 267dd7cddfSDavid du Colombier * check for subscribed mailboxes 277dd7cddfSDavid du Colombier * each line is either a comment starting with # 287dd7cddfSDavid du Colombier * or is a subscribed mailbox name 297dd7cddfSDavid du Colombier */ 307dd7cddfSDavid du Colombier int 317dd7cddfSDavid du Colombier lsubBoxes(char *cmd, char *ref, char *pat) 327dd7cddfSDavid du Colombier { 337dd7cddfSDavid du Colombier MbLock *mb; 347dd7cddfSDavid du Colombier Dir d; 357dd7cddfSDavid du Colombier Biobuf bin; 367dd7cddfSDavid du Colombier char *s; 377dd7cddfSDavid du Colombier int fd, ok, isdir; 387dd7cddfSDavid du Colombier 397dd7cddfSDavid du Colombier mb = mbLock(); 407dd7cddfSDavid du Colombier if(mb == nil) 417dd7cddfSDavid du Colombier return 0; 427dd7cddfSDavid du Colombier fd = cdOpen(mboxDir, SUBSCRIBED, OREAD); 437dd7cddfSDavid du Colombier if(fd < 0) 447dd7cddfSDavid du Colombier fd = mkSubscribed(); 457dd7cddfSDavid du Colombier if(fd < 0){ 467dd7cddfSDavid du Colombier mbUnlock(mb); 477dd7cddfSDavid du Colombier return 0; 487dd7cddfSDavid du Colombier } 497dd7cddfSDavid du Colombier ok = 0; 507dd7cddfSDavid du Colombier Binit(&bin, fd, OREAD); 517dd7cddfSDavid du Colombier while(s = Brdline(&bin, '\n')){ 527dd7cddfSDavid du Colombier s[Blinelen(&bin) - 1] = '\0'; 537dd7cddfSDavid du Colombier if(s[0] == '#') 547dd7cddfSDavid du Colombier continue; 557dd7cddfSDavid du Colombier isdir = 1; 567dd7cddfSDavid du Colombier if(cistrcmp(s, "INBOX") == 0){ 577dd7cddfSDavid du Colombier d.mtime = listMtime("mbox"); 587dd7cddfSDavid du Colombier isdir = 0; 597dd7cddfSDavid du Colombier }else if(cdDirstat(mboxDir, s, &d) >= 0 && !(d.mode & CHDIR)) 607dd7cddfSDavid du Colombier isdir = 0; 617dd7cddfSDavid du Colombier ok |= checkMatch(cmd, ref, pat, s, d.mtime, isdir); 627dd7cddfSDavid du Colombier } 637dd7cddfSDavid du Colombier Bterm(&bin); 647dd7cddfSDavid du Colombier close(fd); 657dd7cddfSDavid du Colombier mbUnlock(mb); 667dd7cddfSDavid du Colombier return ok; 677dd7cddfSDavid du Colombier } 687dd7cddfSDavid du Colombier 697dd7cddfSDavid du Colombier static int 707dd7cddfSDavid du Colombier mkSubscribed(void) 717dd7cddfSDavid du Colombier { 727dd7cddfSDavid du Colombier int fd; 737dd7cddfSDavid du Colombier 747dd7cddfSDavid du Colombier fd = cdCreate(mboxDir, SUBSCRIBED, ORDWR, 0664); 757dd7cddfSDavid du Colombier if(fd < 0) 767dd7cddfSDavid du Colombier return -1; 777dd7cddfSDavid du Colombier fprint(fd, "#imap4 subscription list\nINBOX\n"); 787dd7cddfSDavid du Colombier seek(fd, 0, 0); 797dd7cddfSDavid du Colombier return fd; 807dd7cddfSDavid du Colombier } 817dd7cddfSDavid du Colombier 827dd7cddfSDavid du Colombier /* 837dd7cddfSDavid du Colombier * either subscribe or unsubscribe to a mailbox 847dd7cddfSDavid du Colombier */ 857dd7cddfSDavid du Colombier int 867dd7cddfSDavid du Colombier subscribe(char *mbox, int how) 877dd7cddfSDavid du Colombier { 887dd7cddfSDavid du Colombier MbLock *mb; 897dd7cddfSDavid du Colombier char *s, *in, *ein; 907dd7cddfSDavid du Colombier int fd, tfd, ok, nmbox; 917dd7cddfSDavid du Colombier 927dd7cddfSDavid du Colombier if(cistrcmp(mbox, "inbox") == 0) 937dd7cddfSDavid du Colombier mbox = "INBOX"; 947dd7cddfSDavid du Colombier mb = mbLock(); 957dd7cddfSDavid du Colombier if(mb == nil) 967dd7cddfSDavid du Colombier return 0; 977dd7cddfSDavid du Colombier fd = cdOpen(mboxDir, SUBSCRIBED, ORDWR); 987dd7cddfSDavid du Colombier if(fd < 0) 997dd7cddfSDavid du Colombier fd = mkSubscribed(); 1007dd7cddfSDavid du Colombier if(fd < 0){ 1017dd7cddfSDavid du Colombier mbUnlock(mb); 1027dd7cddfSDavid du Colombier return 0; 1037dd7cddfSDavid du Colombier } 1047dd7cddfSDavid du Colombier in = readFile(fd); 1057dd7cddfSDavid du Colombier if(in == nil){ 1067dd7cddfSDavid du Colombier mbUnlock(mb); 1077dd7cddfSDavid du Colombier return 0; 1087dd7cddfSDavid du Colombier } 1097dd7cddfSDavid du Colombier nmbox = strlen(mbox); 1107dd7cddfSDavid du Colombier s = strstr(in, mbox); 1117dd7cddfSDavid du Colombier while(s != nil && (s != in && s[-1] != '\n' || s[nmbox] != '\n')) 1127dd7cddfSDavid du Colombier s = strstr(s+1, mbox); 1137dd7cddfSDavid du Colombier ok = 0; 1147dd7cddfSDavid du Colombier if(how == 's' && s == nil){ 1157dd7cddfSDavid du Colombier if(fprint(fd, "%s\n", mbox) > 0) 1167dd7cddfSDavid du Colombier ok = 1; 1177dd7cddfSDavid du Colombier }else if(how == 'u' && s != nil){ 1187dd7cddfSDavid du Colombier ein = strchr(s, '\0'); 1197dd7cddfSDavid du Colombier memmove(s, &s[nmbox+1], ein - &s[nmbox+1]); 1207dd7cddfSDavid du Colombier ein -= nmbox+1; 1217dd7cddfSDavid du Colombier tfd = cdOpen(mboxDir, SUBSCRIBED, OWRITE|OTRUNC); 1227dd7cddfSDavid du Colombier if(tfd >= 0 && seek(fd, 0, 0) >= 0 && write(fd, in, ein-in) == ein-in) 1237dd7cddfSDavid du Colombier ok = 1; 1247dd7cddfSDavid du Colombier if(tfd > 0) 1257dd7cddfSDavid du Colombier close(tfd); 1267dd7cddfSDavid du Colombier }else 1277dd7cddfSDavid du Colombier ok = 1; 1287dd7cddfSDavid du Colombier close(fd); 1297dd7cddfSDavid du Colombier mbUnlock(mb); 1307dd7cddfSDavid du Colombier return ok; 1317dd7cddfSDavid du Colombier } 1327dd7cddfSDavid du Colombier 1337dd7cddfSDavid du Colombier /* 1347dd7cddfSDavid du Colombier * stupidly complicated so that % doesn't read entire directory structure 1357dd7cddfSDavid du Colombier * yet * works 1367dd7cddfSDavid du Colombier * note: in most places, inbox is case-insensitive, 1377dd7cddfSDavid du Colombier * but here INBOX is checked for a case-sensitve match. 1387dd7cddfSDavid du Colombier */ 1397dd7cddfSDavid du Colombier int 1407dd7cddfSDavid du Colombier listBoxes(char *cmd, char *ref, char *pat) 1417dd7cddfSDavid du Colombier { 1427dd7cddfSDavid du Colombier int ok; 1437dd7cddfSDavid du Colombier 1447dd7cddfSDavid du Colombier ok = checkMatch(cmd, ref, pat, "INBOX", listMtime("mbox"), 0); 1457dd7cddfSDavid du Colombier return ok | listMatch(cmd, ref, pat, ref, pat); 1467dd7cddfSDavid du Colombier } 1477dd7cddfSDavid du Colombier 1487dd7cddfSDavid du Colombier /* 1497dd7cddfSDavid du Colombier * look for all messages which may match the pattern 1507dd7cddfSDavid du Colombier * punt when a * is reached 1517dd7cddfSDavid du Colombier */ 1527dd7cddfSDavid du Colombier static int 1537dd7cddfSDavid du Colombier listMatch(char *cmd, char *ref, char *pat, char *mbox, char *mm) 1547dd7cddfSDavid du Colombier { 1557dd7cddfSDavid du Colombier Dir dir, *dirs; 1567dd7cddfSDavid du Colombier char *mdir, *m, *mb, *wc; 1577dd7cddfSDavid du Colombier int c, i, nmb, nmdir, nd, ok, fd; 1587dd7cddfSDavid du Colombier 1597dd7cddfSDavid du Colombier mdir = nil; 1607dd7cddfSDavid du Colombier for(m = mm; c = *m; m++){ 1617dd7cddfSDavid du Colombier if(c == '%' || c == '*'){ 1627dd7cddfSDavid du Colombier if(mdir == nil){ 1637dd7cddfSDavid du Colombier fd = cdOpen(mboxDir, ".", OREAD); 164*59cc4ca5SDavid du Colombier if(fd < 0) 165*59cc4ca5SDavid du Colombier return 0; 1667dd7cddfSDavid du Colombier mbox = ""; 1677dd7cddfSDavid du Colombier nmdir = 0; 1687dd7cddfSDavid du Colombier }else{ 1697dd7cddfSDavid du Colombier *mdir = '\0'; 1707dd7cddfSDavid du Colombier fd = cdOpen(mboxDir, mbox, OREAD); 1717dd7cddfSDavid du Colombier *mdir = '/'; 1727dd7cddfSDavid du Colombier nmdir = mdir - mbox + 1; 173*59cc4ca5SDavid du Colombier if(fd < 0 || dirfstat(fd, &dir) < 0) 1747dd7cddfSDavid du Colombier return 0; 175*59cc4ca5SDavid du Colombier if(!(dir.mode & CHDIR)) 176*59cc4ca5SDavid du Colombier break; 177*59cc4ca5SDavid du Colombier } 1787dd7cddfSDavid du Colombier wc = m; 1797dd7cddfSDavid du Colombier for(; c = *m; m++) 1807dd7cddfSDavid du Colombier if(c == '/') 1817dd7cddfSDavid du Colombier break; 1827dd7cddfSDavid du Colombier nmb = nmdir + strlen(m) + NAMELEN + 3; 1837dd7cddfSDavid du Colombier mb = emalloc(nmb); 1847dd7cddfSDavid du Colombier strncpy(mb, mbox, nmdir); 1857dd7cddfSDavid du Colombier dirs = emalloc(sizeof(Dir) * NDirs); 1867dd7cddfSDavid du Colombier ok = 0; 1877dd7cddfSDavid du Colombier while((nd = dirread(fd, dirs, sizeof(Dir) * NDirs)) >= sizeof(Dir)){ 1887dd7cddfSDavid du Colombier nd /= sizeof(Dir); 1897dd7cddfSDavid du Colombier for(i = 0; i < nd; i++){ 1907dd7cddfSDavid du Colombier if(*wc == '*' && (dirs[i].mode & CHDIR) && mayMatch(mm, dirs[i].name, 1)){ 1917dd7cddfSDavid du Colombier snprint(mb+nmdir, nmb-nmdir, "%s", dirs[i].name); 1927dd7cddfSDavid du Colombier ok |= listAll(cmd, ref, pat, mb, dirs[i].mtime); 1937dd7cddfSDavid du Colombier }else if(mayMatch(mm, dirs[i].name, 0)){ 1947dd7cddfSDavid du Colombier snprint(mb+nmdir, nmb-nmdir, "%s%s", dirs[i].name, m); 1957dd7cddfSDavid du Colombier if(*m == '\0') 1967dd7cddfSDavid du Colombier ok |= checkMatch(cmd, ref, pat, mb, dirs[i].mtime, (dirs[i].mode & CHDIR) == CHDIR); 1977dd7cddfSDavid du Colombier else if(dirs[i].mode & CHDIR) 1987dd7cddfSDavid du Colombier ok |= listMatch(cmd, ref, pat, mb, mb + nmdir + strlen(dirs[i].name)); 1997dd7cddfSDavid du Colombier } 2007dd7cddfSDavid du Colombier } 2017dd7cddfSDavid du Colombier } 2027dd7cddfSDavid du Colombier close(fd); 2037dd7cddfSDavid du Colombier free(dirs); 2047dd7cddfSDavid du Colombier free(mb); 2057dd7cddfSDavid du Colombier return ok; 2067dd7cddfSDavid du Colombier } 2077dd7cddfSDavid du Colombier if(c == '/'){ 2087dd7cddfSDavid du Colombier mdir = m; 2097dd7cddfSDavid du Colombier mm = m + 1; 2107dd7cddfSDavid du Colombier } 2117dd7cddfSDavid du Colombier } 2127dd7cddfSDavid du Colombier m = mbox; 2137dd7cddfSDavid du Colombier if(*mbox == '\0') 2147dd7cddfSDavid du Colombier m = "."; 2157dd7cddfSDavid du Colombier if(cdDirstat(mboxDir, m, &dir) < 0) 2167dd7cddfSDavid du Colombier return 0; 2177dd7cddfSDavid du Colombier return checkMatch(cmd, ref, pat, mbox, dir.mtime, (dir.mode & CHDIR) == CHDIR); 2187dd7cddfSDavid du Colombier } 2197dd7cddfSDavid du Colombier 2207dd7cddfSDavid du Colombier /* 2217dd7cddfSDavid du Colombier * too hard: recursively list all files rooted at mbox, 2227dd7cddfSDavid du Colombier * and list checkMatch figure it out 2237dd7cddfSDavid du Colombier */ 2247dd7cddfSDavid du Colombier static int 2257dd7cddfSDavid du Colombier listAll(char *cmd, char *ref, char *pat, char *mbox, long mtime) 2267dd7cddfSDavid du Colombier { 2277dd7cddfSDavid du Colombier Dir *dirs; 2287dd7cddfSDavid du Colombier char *mb; 2297dd7cddfSDavid du Colombier int i, nmb, nd, ok, fd; 2307dd7cddfSDavid du Colombier 2317dd7cddfSDavid du Colombier ok = checkMatch(cmd, ref, pat, mbox, mtime, 1); 2327dd7cddfSDavid du Colombier fd = cdOpen(mboxDir, mbox, OREAD); 2337dd7cddfSDavid du Colombier if(fd < 0) 2347dd7cddfSDavid du Colombier return ok; 2357dd7cddfSDavid du Colombier 2367dd7cddfSDavid du Colombier nmb = strlen(mbox) + NAMELEN + 2; 2377dd7cddfSDavid du Colombier mb = emalloc(nmb); 2387dd7cddfSDavid du Colombier dirs = emalloc(sizeof(Dir) * NDirs); 2397dd7cddfSDavid du Colombier while((nd = dirread(fd, dirs, sizeof(Dir) * NDirs)) >= sizeof(Dir)){ 2407dd7cddfSDavid du Colombier nd /= sizeof(Dir); 2417dd7cddfSDavid du Colombier for(i = 0; i < nd; i++){ 2427dd7cddfSDavid du Colombier snprint(mb, nmb, "%s/%s", mbox, dirs[i].name); 2437dd7cddfSDavid du Colombier if(dirs[i].mode & CHDIR) 2447dd7cddfSDavid du Colombier ok |= listAll(cmd, ref, pat, mb, dirs[i].mtime); 2457dd7cddfSDavid du Colombier else 2467dd7cddfSDavid du Colombier ok |= checkMatch(cmd, ref, pat, mb, dirs[i].mtime, 0); 2477dd7cddfSDavid du Colombier } 2487dd7cddfSDavid du Colombier } 2497dd7cddfSDavid du Colombier close(fd); 2507dd7cddfSDavid du Colombier free(dirs); 2517dd7cddfSDavid du Colombier free(mb); 2527dd7cddfSDavid du Colombier return ok; 2537dd7cddfSDavid du Colombier } 2547dd7cddfSDavid du Colombier 2557dd7cddfSDavid du Colombier static int 2567dd7cddfSDavid du Colombier mayMatch(char *pat, char *name, int star) 2577dd7cddfSDavid du Colombier { 2587dd7cddfSDavid du Colombier Rune r; 2597dd7cddfSDavid du Colombier int i, n; 2607dd7cddfSDavid du Colombier 2617dd7cddfSDavid du Colombier for(; *pat && *pat != '/'; pat += n){ 2627dd7cddfSDavid du Colombier r = *(uchar*)pat; 2637dd7cddfSDavid du Colombier if(r < Runeself) 2647dd7cddfSDavid du Colombier n = 1; 2657dd7cddfSDavid du Colombier else 2667dd7cddfSDavid du Colombier n = chartorune(&r, pat); 2677dd7cddfSDavid du Colombier 2687dd7cddfSDavid du Colombier if(r == '*' || r == '%'){ 2697dd7cddfSDavid du Colombier pat += n; 2707dd7cddfSDavid du Colombier if(r == '*' && star || *pat == '\0' || *pat == '/') 2717dd7cddfSDavid du Colombier return 1; 2727dd7cddfSDavid du Colombier while(*name){ 2737dd7cddfSDavid du Colombier if(mayMatch(pat, name, star)) 2747dd7cddfSDavid du Colombier return 1; 2757dd7cddfSDavid du Colombier name += chartorune(&r, name); 2767dd7cddfSDavid du Colombier } 2777dd7cddfSDavid du Colombier return 0; 2787dd7cddfSDavid du Colombier } 2797dd7cddfSDavid du Colombier for(i = 0; i < n; i++) 2807dd7cddfSDavid du Colombier if(name[i] != pat[i]) 2817dd7cddfSDavid du Colombier return 0; 2827dd7cddfSDavid du Colombier name += n; 2837dd7cddfSDavid du Colombier } 2847dd7cddfSDavid du Colombier if(*name == '\0') 2857dd7cddfSDavid du Colombier return 1; 2867dd7cddfSDavid du Colombier return 0; 2877dd7cddfSDavid du Colombier } 2887dd7cddfSDavid du Colombier 2897dd7cddfSDavid du Colombier /* 2907dd7cddfSDavid du Colombier * mbox is a mailbox name which might match pat. 2917dd7cddfSDavid du Colombier * verify the match 2927dd7cddfSDavid du Colombier * generates response 2937dd7cddfSDavid du Colombier */ 2947dd7cddfSDavid du Colombier static int 2957dd7cddfSDavid du Colombier checkMatch(char *cmd, char *ref, char *pat, char *mbox, long mtime, int isdir) 2967dd7cddfSDavid du Colombier { 2977dd7cddfSDavid du Colombier char *s, *flags; 2987dd7cddfSDavid du Colombier 2997dd7cddfSDavid du Colombier if(!matches(ref, pat, mbox) || !okMbox(mbox)) 3007dd7cddfSDavid du Colombier return 0; 3017dd7cddfSDavid du Colombier if(strcmp(mbox, ".") == 0) 3027dd7cddfSDavid du Colombier mbox = ""; 3037dd7cddfSDavid du Colombier 3047dd7cddfSDavid du Colombier if(isdir) 3057dd7cddfSDavid du Colombier flags = "(\\Noselect)"; 3067dd7cddfSDavid du Colombier else{ 3077dd7cddfSDavid du Colombier s = impName(mbox); 3087dd7cddfSDavid du Colombier if(s != nil && listMtime(s) < mtime) 3097dd7cddfSDavid du Colombier flags = "(\\Noinferiors \\Marked)"; 3107dd7cddfSDavid du Colombier else 3117dd7cddfSDavid du Colombier flags = "(\\Noinferiors)"; 3127dd7cddfSDavid du Colombier } 3137dd7cddfSDavid du Colombier 3147dd7cddfSDavid du Colombier s = strmutf7(mbox); 3157dd7cddfSDavid du Colombier if(s != nil) 3167dd7cddfSDavid du Colombier Bprint(&bout, "* %s %s \"/\" %s\r\n", cmd, flags, s); 3177dd7cddfSDavid du Colombier return 1; 3187dd7cddfSDavid du Colombier } 3197dd7cddfSDavid du Colombier 3207dd7cddfSDavid du Colombier static int 3217dd7cddfSDavid du Colombier matches(char *ref, char *pat, char *name) 3227dd7cddfSDavid du Colombier { 3237dd7cddfSDavid du Colombier Rune r; 3247dd7cddfSDavid du Colombier int i, n; 3257dd7cddfSDavid du Colombier 3267dd7cddfSDavid du Colombier while(ref != pat) 3277dd7cddfSDavid du Colombier if(*name++ != *ref++) 3287dd7cddfSDavid du Colombier return 0; 3297dd7cddfSDavid du Colombier for(; *pat; pat += n){ 3307dd7cddfSDavid du Colombier r = *(uchar*)pat; 3317dd7cddfSDavid du Colombier if(r < Runeself) 3327dd7cddfSDavid du Colombier n = 1; 3337dd7cddfSDavid du Colombier else 3347dd7cddfSDavid du Colombier n = chartorune(&r, pat); 3357dd7cddfSDavid du Colombier 3367dd7cddfSDavid du Colombier if(r == '*'){ 3377dd7cddfSDavid du Colombier pat += n; 3387dd7cddfSDavid du Colombier if(*pat == '\0') 3397dd7cddfSDavid du Colombier return 1; 3407dd7cddfSDavid du Colombier while(*name){ 3417dd7cddfSDavid du Colombier if(matches(pat, pat, name)) 3427dd7cddfSDavid du Colombier return 1; 3437dd7cddfSDavid du Colombier name += chartorune(&r, name); 3447dd7cddfSDavid du Colombier } 3457dd7cddfSDavid du Colombier return 0; 3467dd7cddfSDavid du Colombier } 3477dd7cddfSDavid du Colombier if(r == '%'){ 3487dd7cddfSDavid du Colombier pat += n; 3497dd7cddfSDavid du Colombier while(*name && *name != '/'){ 3507dd7cddfSDavid du Colombier if(matches(pat, pat, name)) 3517dd7cddfSDavid du Colombier return 1; 3527dd7cddfSDavid du Colombier name += chartorune(&r, name); 3537dd7cddfSDavid du Colombier } 3547dd7cddfSDavid du Colombier pat -= n; 3557dd7cddfSDavid du Colombier continue; 3567dd7cddfSDavid du Colombier } 3577dd7cddfSDavid du Colombier for(i = 0; i < n; i++) 3587dd7cddfSDavid du Colombier if(name[i] != pat[i]) 3597dd7cddfSDavid du Colombier return 0; 3607dd7cddfSDavid du Colombier name += n; 3617dd7cddfSDavid du Colombier } 3627dd7cddfSDavid du Colombier if(*name == '\0') 3637dd7cddfSDavid du Colombier return 1; 3647dd7cddfSDavid du Colombier return 0; 3657dd7cddfSDavid du Colombier } 366