list.c 7.4 KB

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