convert.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. /*
  2. * isync - mbsync wrapper: IMAP4 to maildir mailbox synchronizer
  3. * Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>
  4. * Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "isync.h"
  20. #include <limits.h>
  21. #include <stdlib.h>
  22. #include <string.h>
  23. #include <dirent.h>
  24. #include <fcntl.h>
  25. #include <stdio.h>
  26. #include <unistd.h>
  27. #include <sys/stat.h>
  28. #include <time.h>
  29. #include <db.h>
  30. static const char *subdirs[] = { "cur", "new", "tmp" };
  31. static const char Flags[] = { 'D', 'F', 'R', 'S', 'T' };
  32. static int
  33. parse_info( const char *s )
  34. {
  35. unsigned i;
  36. int flags;
  37. flags = 0;
  38. if (s && *(s + 1) == '2' && *(s + 2) == ',')
  39. for (s += 3, i = 0; i < as(Flags); i++)
  40. if (strchr( s, Flags[i] ))
  41. flags |= (1 << i);
  42. return flags;
  43. }
  44. typedef struct {
  45. int uid, flags;
  46. } msg_t;
  47. static int
  48. compare_uids( const void *l, const void *r )
  49. {
  50. return ((msg_t *)l)->uid - ((msg_t *)r)->uid;
  51. }
  52. static DBT key, value;
  53. static struct flock lck;
  54. void
  55. convert( config_t *box )
  56. {
  57. DIR *d;
  58. struct dirent *e;
  59. char *s, *p, *mboxdir;
  60. FILE *fp;
  61. msg_t *msgs;
  62. DB *db;
  63. int i, ret, fd, uidval, maxuid, uid, rmsgs, nmsgs, uv[2];
  64. unsigned u;
  65. struct stat sb;
  66. char buf[_POSIX_PATH_MAX], diumname[_POSIX_PATH_MAX],
  67. uvname[_POSIX_PATH_MAX], sname[_POSIX_PATH_MAX],
  68. iuvname[_POSIX_PATH_MAX], imuname[_POSIX_PATH_MAX],
  69. ilname[_POSIX_PATH_MAX], iumname[_POSIX_PATH_MAX];
  70. mboxdir = expand_strdup( box->path );
  71. nfsnprintf( iuvname, sizeof(iuvname), "%s/isyncuidvalidity", mboxdir );
  72. nfsnprintf( diumname, sizeof(iumname), "%s/.isyncuidmap.db", mboxdir );
  73. nfsnprintf( uvname, sizeof(uvname), "%s/.uidvalidity", mboxdir );
  74. if (stat( iuvname, &sb )) {
  75. if (!stat( diumname, &sb ))
  76. altmap++;
  77. else if (!stat( uvname, &sb ))
  78. altmap--;
  79. err1:
  80. free( mboxdir );
  81. return;
  82. }
  83. for (i = 0; i < 3; i++) {
  84. nfsnprintf( buf, sizeof(buf), "%s/%s", mboxdir, subdirs[i] );
  85. if (stat( buf, &sb )) {
  86. sys_error( "ERROR: cannot access %s", buf );
  87. fprintf( stderr,
  88. "ERROR: '%s' does not appear to be a valid maildir style mailbox\n",
  89. mboxdir );
  90. goto err1;
  91. }
  92. }
  93. nfsnprintf( iumname, sizeof(iumname), "%s/isyncuidmap.db", mboxdir );
  94. nfsnprintf( imuname, sizeof(imuname), "%s/isyncmaxuid", mboxdir );
  95. nfsnprintf( ilname, sizeof(ilname), "%s/isynclock", mboxdir );
  96. nfsnprintf( sname, sizeof(sname), "%s/.mbsyncstate", mboxdir );
  97. if ((fd = open( ilname, O_WRONLY|O_CREAT, 0600 )) < 0) {
  98. sys_error( "Cannot create %s", ilname );
  99. goto err1;
  100. }
  101. #if SEEK_SET != 0
  102. lck.l_whence = SEEK_SET;
  103. #endif
  104. #if F_WRLCK != 0
  105. lck.l_type = F_WRLCK;
  106. #endif
  107. if (fcntl( fd, F_SETLKW, &lck )) {
  108. sys_error( "Cannot lock %s", ilname );
  109. err2:
  110. close( fd );
  111. goto err1;
  112. }
  113. if (!(fp = fopen( iuvname, "r" ))) {
  114. sys_error( "Cannot open %s", iuvname );
  115. goto err2;
  116. }
  117. if (fscanf( fp, "%d", &uidval ) != 1) {
  118. sys_error( "Cannot read %s", iuvname );
  119. err3:
  120. fclose( fp );
  121. goto err2;
  122. }
  123. fclose( fp );
  124. if (!(fp = fopen( imuname, "r" ))) {
  125. sys_error( "Cannot open %s", imuname );
  126. goto err2;
  127. }
  128. if (fscanf( fp, "%d", &maxuid ) != 1) {
  129. sys_error( "Cannot read %s", imuname );
  130. goto err3;
  131. }
  132. fclose( fp );
  133. if (!stat( iumname, &sb )) {
  134. if (db_create( &db, 0, 0 )) {
  135. fputs( "dbcreate failed\n", stderr );
  136. goto err2;
  137. }
  138. if ((db->open)( db, 0, iumname, 0, DB_HASH, 0, 0 )) {
  139. fputs( "cannot open db\n", stderr );
  140. db->close( db, 0 );
  141. goto err2;
  142. }
  143. altmap++;
  144. } else {
  145. db = 0;
  146. altmap--;
  147. }
  148. msgs = 0;
  149. rmsgs = 0;
  150. nmsgs = 0;
  151. for (i = 0; i < 2; i++) {
  152. nfsnprintf( buf, sizeof(buf), "%s/%s/", mboxdir, subdirs[i] );
  153. if (!(d = opendir( buf ))) {
  154. sys_error( "Cannot list %s", buf );
  155. err4:
  156. free( msgs );
  157. if (db)
  158. db->close( db, 0 );
  159. goto err2;
  160. }
  161. while ((e = readdir( d ))) {
  162. if (*e->d_name == '.')
  163. continue;
  164. s = strchr( e->d_name, ':' );
  165. if (db) {
  166. key.data = e->d_name;
  167. key.size = s ? (size_t)(s - e->d_name) : strlen( e->d_name );
  168. if ((ret = db->get( db, 0, &key, &value, 0 ))) {
  169. if (ret != DB_NOTFOUND)
  170. db->err( db, ret, "Maildir error: db->get()" );
  171. continue;
  172. }
  173. uid = *(int *)value.data;
  174. } else if ((p = strstr( e->d_name, ",U=" )))
  175. uid = atoi( p + 3 );
  176. else
  177. continue;
  178. if (nmsgs == rmsgs) {
  179. rmsgs = rmsgs * 2 + 100;
  180. msgs = nfrealloc( msgs, rmsgs * sizeof(msg_t) );
  181. }
  182. msgs[nmsgs].uid = uid;
  183. msgs[nmsgs++].flags = parse_info( s );
  184. }
  185. closedir( d );
  186. }
  187. qsort( msgs, nmsgs, sizeof(msg_t), compare_uids );
  188. if (!(fp = fopen( sname, "w" ))) {
  189. sys_error( "Cannot create %s", sname );
  190. goto err4;
  191. }
  192. if (box->max_messages) {
  193. if (!nmsgs)
  194. i = maxuid;
  195. else {
  196. i = nmsgs - box->max_messages;
  197. if (i < 0)
  198. i = 0;
  199. i = msgs[i].uid - 1;
  200. }
  201. } else
  202. i = 0;
  203. fprintf( fp, "%d:%d %d:%d:%d\n", uidval, maxuid, uidval, i, maxuid );
  204. for (i = 0; i < nmsgs; i++) {
  205. fprintf( fp, "%d %d ", msgs[i].uid, msgs[i].uid );
  206. for (u = 0; u < as(Flags); u++)
  207. if (msgs[i].flags & (1 << u))
  208. fputc( Flags[u], fp );
  209. fputc( '\n', fp );
  210. }
  211. fclose( fp );
  212. if (db) {
  213. key.data = (void *)"UIDVALIDITY";
  214. key.size = 11;
  215. uv[0] = uidval;
  216. uv[1] = maxuid;
  217. value.data = uv;
  218. value.size = sizeof(uv);
  219. if ((ret = db->put( db, 0, &key, &value, 0 ))) {
  220. db->err( db, ret, "Maildir error: db->put()" );
  221. goto err4;
  222. }
  223. db->close( db, 0 );
  224. if (rename( iumname, diumname )) {
  225. sys_error( "Cannot rename %s to %s", iumname, diumname );
  226. goto err4;
  227. }
  228. } else {
  229. if (!(fp = fopen( uvname, "w" ))) {
  230. sys_error( "Cannot create %s", uvname );
  231. goto err4;
  232. }
  233. fprintf( fp, "%d\n%d\n", uidval, maxuid );
  234. fclose( fp );
  235. }
  236. unlink( iuvname );
  237. unlink( imuname );
  238. close( fd );
  239. unlink( ilname );
  240. free( msgs );
  241. free( mboxdir );
  242. return;
  243. }