folder.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. #include <u.h>
  2. #include <libc.h>
  3. #include <bio.h>
  4. #include <auth.h>
  5. #include "imap4d.h"
  6. static int copyData(int ffd, int tfd, MbLock *ml);
  7. static MbLock mLock =
  8. {
  9. .fd = -1
  10. };
  11. static char curDir[MboxNameLen];
  12. void
  13. resetCurDir(void)
  14. {
  15. curDir[0] = '\0';
  16. }
  17. int
  18. myChdir(char *dir)
  19. {
  20. if(strcmp(dir, curDir) == 0)
  21. return 0;
  22. if(dir[0] != '/' || strlen(dir) > MboxNameLen)
  23. return -1;
  24. strcpy(curDir, dir);
  25. if(chdir(dir) < 0){
  26. werrstr("mychdir failed: %r");
  27. return -1;
  28. }
  29. return 0;
  30. }
  31. int
  32. cdCreate(char *dir, char *file, int mode, ulong perm)
  33. {
  34. if(myChdir(dir) < 0)
  35. return -1;
  36. return create(file, mode, perm);
  37. }
  38. Dir*
  39. cdDirstat(char *dir, char *file)
  40. {
  41. if(myChdir(dir) < 0)
  42. return nil;
  43. return dirstat(file);
  44. }
  45. int
  46. cdExists(char *dir, char *file)
  47. {
  48. Dir *d;
  49. d = cdDirstat(dir, file);
  50. if(d == nil)
  51. return 0;
  52. free(d);
  53. return 1;
  54. }
  55. int
  56. cdDirwstat(char *dir, char *file, Dir *d)
  57. {
  58. if(myChdir(dir) < 0)
  59. return -1;
  60. return dirwstat(file, d);
  61. }
  62. int
  63. cdOpen(char *dir, char *file, int mode)
  64. {
  65. if(myChdir(dir) < 0)
  66. return -1;
  67. return open(file, mode);
  68. }
  69. int
  70. cdRemove(char *dir, char *file)
  71. {
  72. if(myChdir(dir) < 0)
  73. return -1;
  74. return remove(file);
  75. }
  76. /*
  77. * open the one true mail lock file
  78. */
  79. MbLock*
  80. mbLock(void)
  81. {
  82. if(mLock.fd >= 0)
  83. bye("mail lock deadlock");
  84. mLock.fd = openLocked(mboxDir, "L.mbox", OREAD);
  85. if(mLock.fd >= 0)
  86. return &mLock;
  87. return nil;
  88. }
  89. void
  90. mbUnlock(MbLock *ml)
  91. {
  92. if(ml != &mLock)
  93. bye("bad mail unlock");
  94. if(ml->fd < 0)
  95. bye("mail unlock when not locked");
  96. close(ml->fd);
  97. ml->fd = -1;
  98. }
  99. void
  100. mbLockRefresh(MbLock *ml)
  101. {
  102. char buf[1];
  103. seek(ml->fd, 0, 0);
  104. read(ml->fd, buf, 1);
  105. }
  106. int
  107. mbLocked(void)
  108. {
  109. return mLock.fd >= 0;
  110. }
  111. char*
  112. impName(char *name)
  113. {
  114. char *s;
  115. int n;
  116. if(cistrcmp(name, "inbox") == 0)
  117. name = "mbox";
  118. n = strlen(name) + STRLEN(".imp") + 1;
  119. s = binalloc(&parseBin, n, 0);
  120. if(s == nil)
  121. return nil;
  122. snprint(s, n, "%s.imp", name);
  123. return s;
  124. }
  125. /*
  126. * massage the mailbox name into something valid
  127. * eliminates all .', and ..',s, redundatant and trailing /'s.
  128. */
  129. char *
  130. mboxName(char *s)
  131. {
  132. char *ss;
  133. ss = mutf7str(s);
  134. if(ss == nil)
  135. return nil;
  136. cleanname(ss);
  137. return ss;
  138. }
  139. char *
  140. strmutf7(char *s)
  141. {
  142. char *m;
  143. int n;
  144. n = strlen(s) * MUtf7Max + 1;
  145. m = binalloc(&parseBin, n, 0);
  146. if(m == nil)
  147. return nil;
  148. if(encmutf7(m, n, s) < 0)
  149. return nil;
  150. return m;
  151. }
  152. char *
  153. mutf7str(char *s)
  154. {
  155. char *m;
  156. int n;
  157. /*
  158. * n = strlen(s) * UTFmax / (2.67) + 1
  159. * UTFMax / 2.67 == 3 / (8/3) == 9 / 8
  160. */
  161. n = strlen(s);
  162. n = (n * 9 + 7) / 8 + 1;
  163. m = binalloc(&parseBin, n, 0);
  164. if(m == nil)
  165. return nil;
  166. if(decmutf7(m, n, s) < 0)
  167. return nil;
  168. return m;
  169. }
  170. void
  171. splitr(char *s, int c, char **left, char **right)
  172. {
  173. char *d;
  174. int n;
  175. n = strlen(s);
  176. d = binalloc(&parseBin, n + 1, 0);
  177. if(d == nil)
  178. parseErr("out of memory");
  179. strcpy(d, s);
  180. s = strrchr(d, c);
  181. if(s != nil){
  182. *left = d;
  183. *s++ = '\0';
  184. *right = s;
  185. }else{
  186. *right = d;
  187. *left = d + n;
  188. }
  189. }
  190. /*
  191. * create the mailbox and all intermediate components
  192. * a trailing / implies the new mailbox is a directory;
  193. * otherwise, it's a file.
  194. *
  195. * return with the file open for write, or directory open for read.
  196. */
  197. int
  198. createBox(char *mbox, int dir)
  199. {
  200. char *m;
  201. int fd;
  202. fd = -1;
  203. for(m = mbox; *m; m++){
  204. if(*m == '/'){
  205. *m = '\0';
  206. if(access(mbox, AEXIST) < 0){
  207. if(fd >= 0)
  208. close(fd);
  209. fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775);
  210. if(fd < 0)
  211. return -1;
  212. }
  213. *m = '/';
  214. }
  215. }
  216. if(dir)
  217. fd = cdCreate(mboxDir, mbox, OREAD, DMDIR|0775);
  218. else
  219. fd = cdCreate(mboxDir, mbox, OWRITE, 0664);
  220. return fd;
  221. }
  222. /*
  223. * move one mail folder to another
  224. * destination mailbox doesn't exist.
  225. * the source folder may be a directory or a mailbox,
  226. * and may be in the same directory as the destination,
  227. * or a completely different directory.
  228. */
  229. int
  230. moveBox(char *from, char *to)
  231. {
  232. Dir *d;
  233. char *fd, *fe, *td, *te, *fimp;
  234. splitr(from, '/', &fd, &fe);
  235. splitr(to, '/', &td, &te);
  236. /*
  237. * in the same directory: try rename
  238. */
  239. d = cdDirstat(mboxDir, from);
  240. if(d == nil)
  241. return 0;
  242. if(strcmp(fd, td) == 0){
  243. nulldir(d);
  244. d->name = te;
  245. if(cdDirwstat(mboxDir, from, d) >= 0){
  246. fimp = impName(from);
  247. d->name = impName(te);
  248. cdDirwstat(mboxDir, fimp, d);
  249. free(d);
  250. return 1;
  251. }
  252. }
  253. /*
  254. * directory copy is too hard for now
  255. */
  256. if(d->mode & DMDIR)
  257. return 0;
  258. free(d);
  259. return copyBox(from, to, 1);
  260. }
  261. /*
  262. * copy the contents of one mailbox to another
  263. * either truncates or removes the source box if it succeeds.
  264. */
  265. int
  266. copyBox(char *from, char *to, int doremove)
  267. {
  268. MbLock *ml;
  269. char *fimp, *timp;
  270. int ffd, tfd, ok;
  271. if(cistrcmp(from, "inbox") == 0)
  272. from = "mbox";
  273. ml = mbLock();
  274. if(ml == nil)
  275. return 0;
  276. ffd = openLocked(mboxDir, from, OREAD);
  277. if(ffd < 0){
  278. mbUnlock(ml);
  279. return 0;
  280. }
  281. tfd = createBox(to, 0);
  282. if(tfd < 0){
  283. mbUnlock(ml);
  284. close(ffd);
  285. return 0;
  286. }
  287. ok = copyData(ffd, tfd, ml);
  288. close(ffd);
  289. close(tfd);
  290. if(!ok){
  291. mbUnlock(ml);
  292. return 0;
  293. }
  294. fimp = impName(from);
  295. timp = impName(to);
  296. if(fimp != nil && timp != nil){
  297. ffd = cdOpen(mboxDir, fimp, OREAD);
  298. if(ffd >= 0){
  299. tfd = cdCreate(mboxDir, timp, OWRITE, 0664);
  300. if(tfd >= 0){
  301. copyData(ffd, tfd, ml);
  302. close(tfd);
  303. }
  304. close(ffd);
  305. }
  306. }
  307. cdRemove(mboxDir, fimp);
  308. if(doremove)
  309. cdRemove(mboxDir, from);
  310. else
  311. close(cdOpen(mboxDir, from, OWRITE|OTRUNC));
  312. mbUnlock(ml);
  313. return 1;
  314. }
  315. /*
  316. * copies while holding the mail lock,
  317. * then tries to copy permissions and group ownership
  318. */
  319. static int
  320. copyData(int ffd, int tfd, MbLock *ml)
  321. {
  322. Dir *fd, td;
  323. char buf[BufSize];
  324. int n;
  325. for(;;){
  326. n = read(ffd, buf, BufSize);
  327. if(n <= 0){
  328. if(n < 0)
  329. return 0;
  330. break;
  331. }
  332. if(write(tfd, buf, n) != n)
  333. return 0;
  334. mbLockRefresh(ml);
  335. }
  336. fd = dirfstat(ffd);
  337. if(fd != nil){
  338. nulldir(&td);
  339. td.mode = fd->mode;
  340. if(dirfwstat(tfd, &td) >= 0){
  341. nulldir(&td);
  342. td.gid = fd->gid;
  343. dirfwstat(tfd, &td);
  344. }
  345. }
  346. return 1;
  347. }