main.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  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 <sys/types.h>
  21. #include <sys/stat.h>
  22. #include <sys/param.h>
  23. #include <stdlib.h>
  24. #include <unistd.h>
  25. #include <fcntl.h>
  26. #include <limits.h>
  27. #include <pwd.h>
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include <ctype.h>
  31. #include <dirent.h>
  32. #ifdef HAVE_GETOPT_LONG
  33. # include <getopt.h>
  34. struct option Opts[] = {
  35. {"write", 0, NULL, 'w' },
  36. {"writeto", 0, NULL, 'W' },
  37. {"all", 0, NULL, 'a' },
  38. {"list", 0, NULL, 'l'},
  39. {"config", 1, NULL, 'c'},
  40. {"create", 0, NULL, 'C'},
  41. {"create-local", 0, NULL, 'L'},
  42. {"create-remote", 0, NULL, 'R'},
  43. {"delete", 0, NULL, 'd'},
  44. {"expunge", 0, NULL, 'e'},
  45. {"fast", 0, NULL, 'f'},
  46. {"help", 0, NULL, 'h'},
  47. {"remote", 1, NULL, 'r'},
  48. {"folder", 1, NULL, 'F'},
  49. {"maildir", 1, NULL, 'M'},
  50. {"one-to-one", 0, NULL, '1'},
  51. {"inbox", 1, NULL, 'I'},
  52. {"host", 1, NULL, 's'},
  53. {"port", 1, NULL, 'p'},
  54. {"debug", 0, NULL, 'D'},
  55. {"quiet", 0, NULL, 'q'},
  56. {"user", 1, NULL, 'u'},
  57. {"pass", 1, NULL, 'P'},
  58. {"version", 0, NULL, 'v'},
  59. {"verbose", 0, NULL, 'V'},
  60. {0, 0, 0, 0}
  61. };
  62. #endif
  63. static void
  64. version( void )
  65. {
  66. puts( PACKAGE " " VERSION );
  67. exit( 0 );
  68. }
  69. static void
  70. usage( int code )
  71. {
  72. fputs(
  73. PACKAGE " " VERSION " - mbsync wrapper: IMAP4 to maildir synchronizer\n"
  74. "Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>\n"
  75. "Copyright (C) 2002-2006,2008,2010-2012 Oswald Buddenhagen <ossi@users.sf.net>\n"
  76. "Copyright (C) 2004 Theodore Ts'o <tytso@mit.edu>\n"
  77. "usage:\n"
  78. " " PACKAGE " [ flags ] mailbox [mailbox ...]\n"
  79. " " PACKAGE " [ flags ] -a\n"
  80. " " PACKAGE " [ flags ] -l\n"
  81. " -a, --all synchronize all defined mailboxes\n"
  82. " -l, --list list all defined mailboxes and exit\n"
  83. " -L, --create-local create local maildir mailbox if nonexistent\n"
  84. " -R, --create-remote create remote imap mailbox if nonexistent\n"
  85. " -C, --create create both local and remote mailboxes if nonexistent\n"
  86. " -d, --delete delete local msgs that don't exist on the server\n"
  87. " -e, --expunge expunge deleted messages\n"
  88. " -f, --fast only fetch new messages\n"
  89. " -r, --remote BOX remote mailbox\n"
  90. " -F, --folder DIR remote IMAP folder containing mailboxes\n"
  91. " -M, --maildir DIR local directory containing mailboxes\n"
  92. " -1, --one-to-one map every IMAP <folder>/box to <maildir>/box\n"
  93. " -I, --inbox BOX map IMAP INBOX to <maildir>/BOX (exception to -1)\n"
  94. " -s, --host HOST IMAP server address\n"
  95. " -p, --port PORT server IMAP port\n"
  96. " -u, --user USER IMAP user name\n"
  97. " -P, --pass PASSWORD IMAP password\n"
  98. " -c, --config CONFIG read an alternate config file (default: ~/.isyncrc)\n"
  99. " -D, --debug print debugging messages\n"
  100. " -V, --verbose verbose mode (display network traffic)\n"
  101. " -q, --quiet don't display progress info\n"
  102. " -v, --version display version\n"
  103. " -h, --help display this help message\n\n"
  104. "Note that this is a wrapper binary only; the \"real\" isync is named \"mbsync\".\n"
  105. "Options to permanently transform your old isync configuration:\n"
  106. " -w, --write write permanent mbsync configuration\n"
  107. " -W, --writeto FILE write permanent mbsync configuration to FILE\n",
  108. code ? stderr : stdout );
  109. exit( code );
  110. }
  111. static const char *
  112. strrstr( const char *h, const char *n )
  113. {
  114. char *p = strstr( h, n );
  115. if (!p)
  116. return 0;
  117. do {
  118. h = p;
  119. p = strstr( h + 1, n );
  120. } while (p);
  121. return h;
  122. }
  123. static void
  124. add_arg( char ***args, const char *arg )
  125. {
  126. int nu = 0;
  127. if (*args)
  128. for (; (*args)[nu]; nu++);
  129. *args = nfrealloc( *args, sizeof(char *) * (nu + 2));
  130. (*args)[nu] = nfstrdup( arg );
  131. (*args)[nu + 1] = 0;
  132. }
  133. #define OP_FAST (1<<2)
  134. #define OP_CREATE_REMOTE (1<<3)
  135. #define OP_CREATE_LOCAL (1<<4)
  136. int Quiet, Verbose, Debug;
  137. config_t global, *boxes;
  138. const char *maildir, *xmaildir, *folder, *inbox;
  139. int o2o, altmap, delete, expunge;
  140. const char *Home;
  141. int HomeLen;
  142. int
  143. main( int argc, char **argv )
  144. {
  145. config_t *box, **stor;
  146. char *config = 0, *outconfig = 0, **args;
  147. int i, pl, fd, mod, all, list, ops, writeout;
  148. struct stat st;
  149. char path1[_POSIX_PATH_MAX], path2[_POSIX_PATH_MAX];
  150. if (!(Home = getenv("HOME"))) {
  151. fputs( "Fatal: $HOME not set\n", stderr );
  152. return 1;
  153. }
  154. HomeLen = strlen( Home );
  155. /* defaults */
  156. /* XXX the precedence is borked:
  157. it's defaults < cmdline < file instead of defaults < file < cmdline */
  158. #ifdef BSD
  159. global.user = getenv( "USER" );
  160. #else
  161. global.user = getenv( "LOGNAME" );
  162. #endif
  163. global.port = 143;
  164. global.box = ""; /* implicit INBOX in resulting Master/Slave entries */
  165. global.use_namespace = 1;
  166. global.require_ssl = 1;
  167. global.use_tlsv1 = 1;
  168. folder = "";
  169. maildir = "~";
  170. xmaildir = Home;
  171. #define FLAGS "wW:alCLRc:defhp:qu:P:r:F:M:1I:s:vVD"
  172. mod = all = list = ops = writeout = Quiet = Verbose = Debug = 0;
  173. #ifdef HAVE_GETOPT_LONG
  174. while ((i = getopt_long( argc, argv, FLAGS, Opts, NULL )) != -1)
  175. #else
  176. while ((i = getopt( argc, argv, FLAGS )) != -1)
  177. #endif
  178. {
  179. switch (i) {
  180. case 'W':
  181. outconfig = optarg;
  182. /* plopp */
  183. case 'w':
  184. writeout = 1;
  185. break;
  186. case 'l':
  187. list = 1;
  188. /* plopp */
  189. case 'a':
  190. all = 1;
  191. break;
  192. case '1':
  193. o2o = 1;
  194. mod = 1;
  195. break;
  196. case 'C':
  197. ops |= OP_CREATE_REMOTE|OP_CREATE_LOCAL;
  198. break;
  199. case 'L':
  200. ops |= OP_CREATE_LOCAL;
  201. break;
  202. case 'R':
  203. ops |= OP_CREATE_REMOTE;
  204. break;
  205. case 'c':
  206. config = optarg;
  207. break;
  208. case 'd':
  209. delete = 1;
  210. break;
  211. case 'e':
  212. expunge = 1;
  213. break;
  214. case 'f':
  215. ops |= OP_FAST;
  216. break;
  217. case 'p':
  218. global.port = atoi( optarg );
  219. mod = 1;
  220. break;
  221. case 'r':
  222. global.box = optarg;
  223. mod = 1;
  224. break;
  225. case 'F':
  226. folder = optarg;
  227. mod = 1;
  228. break;
  229. case 'M':
  230. maildir = optarg;
  231. mod = 1;
  232. break;
  233. case 'I':
  234. inbox = optarg;
  235. mod = 1;
  236. break;
  237. case 's':
  238. #ifdef HAVE_LIBSSL
  239. if (!strncasecmp( "imaps:", optarg, 6 )) {
  240. global.use_imaps = 1;
  241. global.port = 993;
  242. global.use_sslv2 = 0;
  243. global.use_sslv3 = 1;
  244. optarg += 6;
  245. }
  246. #endif
  247. global.host = optarg;
  248. mod = 1;
  249. break;
  250. case 'u':
  251. global.user = optarg;
  252. mod = 1;
  253. break;
  254. case 'P':
  255. global.pass = optarg;
  256. mod = 1;
  257. break;
  258. case 'D':
  259. Debug = 1;
  260. break;
  261. case 'V':
  262. Verbose++;
  263. break;
  264. case 'q':
  265. Quiet++;
  266. break;
  267. case 'v':
  268. version();
  269. case 'h':
  270. usage( 0 );
  271. default:
  272. usage( 1 );
  273. }
  274. }
  275. if (!writeout)
  276. fputs( "Notice: please run 'isync -w' and start using 'mbsync' directly.\n", stderr );
  277. if (config) {
  278. if (*config != '/') {
  279. if (!getcwd( path1, sizeof(path1) )) {
  280. fprintf( stderr, "Can't obtain working directory\n" );
  281. return 1;
  282. }
  283. pl = strlen( path1 );
  284. nfsnprintf( path1 + pl, sizeof(path1) - pl, "/%s", config );
  285. config = path1;
  286. }
  287. } else {
  288. nfsnprintf( path1, sizeof(path1), "%s/.isyncrc", Home );
  289. config = path1;
  290. }
  291. stor = &boxes;
  292. load_config( config, &stor );
  293. if (!all && !o2o)
  294. for (i = optind; argv[i]; i++)
  295. if (!find_box( argv[i] )) {
  296. box = nfmalloc( sizeof(config_t) );
  297. memcpy( box, &global, sizeof(config_t) );
  298. box->path = argv[i];
  299. *stor = box;
  300. stor = &box->next;
  301. mod = 1;
  302. }
  303. if (writeout) {
  304. all = 1;
  305. if (mod)
  306. fprintf( stderr,
  307. "Warning: command line switches that influence the resulting config file\n"
  308. "have been supplied.\n" );
  309. } else {
  310. if (!argv[optind] && !all) {
  311. fprintf( stderr, "No mailbox specified. Try isync -h\n" );
  312. return 1;
  313. }
  314. }
  315. if (all) {
  316. if (o2o) {
  317. DIR * dir;
  318. struct dirent *de;
  319. if (!(dir = opendir( xmaildir ))) {
  320. sys_error( "Cannot list '%s'", xmaildir );
  321. return 1;
  322. }
  323. while ((de = readdir( dir ))) {
  324. if (*de->d_name == '.')
  325. continue;
  326. nfsnprintf( path2, sizeof(path2), "%s/%s/cur", xmaildir, de->d_name );
  327. if (stat( path2, &st ) || !S_ISDIR( st.st_mode ))
  328. continue;
  329. global.path = de->d_name;
  330. global.box = (inbox && !strcmp( inbox, global.path )) ?
  331. "INBOX" : global.path;
  332. convert( &global );
  333. }
  334. closedir( dir );
  335. } else
  336. for (box = boxes; box; box = box->next)
  337. convert( box );
  338. } else {
  339. for (i = optind; argv[i]; i++)
  340. if (o2o) {
  341. global.path = argv[i];
  342. global.box =
  343. (inbox && !strcmp( global.path, inbox )) ?
  344. "INBOX" : global.path;
  345. convert( &global );
  346. } else
  347. convert( find_box( argv[i] ) );
  348. }
  349. if (writeout) {
  350. if (!outconfig) {
  351. const char *p = strrchr( config, '/' );
  352. if (!p)
  353. p = config;
  354. p = strrstr( p, "isync" );
  355. if (!p)
  356. nfsnprintf( path2, sizeof(path2), "%s.mbsync", config );
  357. else
  358. nfsnprintf( path2, sizeof(path2), "%.*smb%s", p - config, config, p + 1 );
  359. outconfig = path2;
  360. }
  361. if ((fd = creat( outconfig, 0666 )) < 0) {
  362. sys_error( "Error: cannot create config file '%s'", outconfig );
  363. return 1;
  364. }
  365. } else {
  366. strcpy( path2, "/tmp/mbsyncrcXXXXXX" );
  367. if ((fd = mkstemp( path2 )) < 0) {
  368. sys_error( "Error: cannot create temporary config file" );
  369. return 1;
  370. }
  371. }
  372. write_config( fd );
  373. if (writeout)
  374. return 0;
  375. args = 0;
  376. add_arg( &args, "mbsync" );
  377. while (--Verbose >= 0)
  378. add_arg( &args, "-V" );
  379. if (Debug)
  380. add_arg( &args, "-D" );
  381. for (; Quiet; Quiet--)
  382. add_arg( &args, "-q" );
  383. add_arg( &args, "-cT" );
  384. add_arg( &args, path2 );
  385. if (ops & OP_FAST)
  386. add_arg( &args, "-Ln" );
  387. if (ops & OP_CREATE_REMOTE)
  388. add_arg( &args, "-Cm" );
  389. if (ops & OP_CREATE_LOCAL)
  390. add_arg( &args, "-Cs" );
  391. if (list)
  392. add_arg( &args, "-lC" );
  393. if (o2o) {
  394. if (all)
  395. add_arg( &args, "o2o" );
  396. else {
  397. char buf[1024];
  398. strcpy( buf, "o2o:" );
  399. strcat( buf, argv[optind] );
  400. while (argv[++optind]) {
  401. strcat( buf, "," );
  402. strcat( buf, argv[optind] );
  403. }
  404. add_arg( &args, buf );
  405. }
  406. } else {
  407. if (all)
  408. add_arg( &args, "-a" );
  409. else
  410. for (; argv[optind]; optind++)
  411. add_arg( &args, find_box( argv[optind] )->channel_name );
  412. }
  413. execvp( args[0], args );
  414. sys_error( "Cannot execute %s", args[0] );
  415. return 1;
  416. }