main.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  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, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  19. */
  20. #include "isync.h"
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23. #include <sys/param.h>
  24. #include <stdlib.h>
  25. #include <unistd.h>
  26. #include <fcntl.h>
  27. #include <limits.h>
  28. #include <pwd.h>
  29. #include <stdio.h>
  30. #include <errno.h>
  31. #include <string.h>
  32. #include <ctype.h>
  33. #include <dirent.h>
  34. #ifdef HAVE_GETOPT_LONG
  35. # define _GNU_SOURCE
  36. # include <getopt.h>
  37. struct option Opts[] = {
  38. {"write", 0, NULL, 'w' },
  39. {"writeto", 0, NULL, 'W' },
  40. {"all", 0, NULL, 'a' },
  41. {"list", 0, NULL, 'l'},
  42. {"config", 1, NULL, 'c'},
  43. {"create", 0, NULL, 'C'},
  44. {"create-local", 0, NULL, 'L'},
  45. {"create-remote", 0, NULL, 'R'},
  46. {"delete", 0, NULL, 'd'},
  47. {"expunge", 0, NULL, 'e'},
  48. {"fast", 0, NULL, 'f'},
  49. {"help", 0, NULL, 'h'},
  50. {"remote", 1, NULL, 'r'},
  51. {"folder", 1, NULL, 'F'},
  52. {"maildir", 1, NULL, 'M'},
  53. {"one-to-one", 0, NULL, '1'},
  54. {"inbox", 1, NULL, 'I'},
  55. {"host", 1, NULL, 's'},
  56. {"port", 1, NULL, 'p'},
  57. {"debug", 0, NULL, 'D'},
  58. {"quiet", 0, NULL, 'q'},
  59. {"user", 1, NULL, 'u'},
  60. {"pass", 1, NULL, 'P'},
  61. {"version", 0, NULL, 'v'},
  62. {"verbose", 0, NULL, 'V'},
  63. {0, 0, 0, 0}
  64. };
  65. #endif
  66. static void
  67. version( void )
  68. {
  69. puts( PACKAGE " " VERSION );
  70. exit( 0 );
  71. }
  72. static void
  73. usage( int code )
  74. {
  75. fputs(
  76. PACKAGE " " VERSION " - mbsync wrapper: IMAP4 to maildir synchronizer\n"
  77. "Copyright (C) 2000-2002 Michael R. Elkins <me@mutt.org>\n"
  78. "Copyright (C) 2002-2004 Oswald Buddenhagen <ossi@users.sf.net>\n"
  79. "usage:\n"
  80. " " PACKAGE " [ flags ] mailbox [mailbox ...]\n"
  81. " " PACKAGE " [ flags ] -a\n"
  82. " " PACKAGE " [ flags ] -l\n"
  83. " -a, --all synchronize all defined mailboxes\n"
  84. " -l, --list list all defined mailboxes and exit\n"
  85. " -L, --create-local create local maildir mailbox if nonexistent\n"
  86. " -R, --create-remote create remote imap mailbox if nonexistent\n"
  87. " -C, --create create both local and remote mailboxes if nonexistent\n"
  88. " -d, --delete delete local msgs that don't exist on the server\n"
  89. " -e, --expunge expunge deleted messages\n"
  90. " -f, --fast only fetch new messages\n"
  91. " -r, --remote BOX remote mailbox\n"
  92. " -F, --folder DIR remote IMAP folder containing mailboxes\n"
  93. " -M, --maildir DIR local directory containing mailboxes\n"
  94. " -1, --one-to-one map every IMAP <folder>/box to <maildir>/box\n"
  95. " -I, --inbox BOX map IMAP INBOX to <maildir>/BOX (exception to -1)\n"
  96. " -s, --host HOST IMAP server address\n"
  97. " -p, --port PORT server IMAP port\n"
  98. " -u, --user USER IMAP user name\n"
  99. " -P, --pass PASSWORD IMAP password\n"
  100. " -c, --config CONFIG read an alternate config file (default: ~/.isyncrc)\n"
  101. " -D, --debug print debugging messages\n"
  102. " -V, --verbose verbose mode (display network traffic)\n"
  103. " -q, --quiet don't display progress info\n"
  104. " -v, --version display version\n"
  105. " -h, --help display this help message\n\n"
  106. "Note that this is a wrapper binary only; the \"real\" isync is named \"mbsync\".\n"
  107. "Options to permanently transform your old isync configuration:\n"
  108. " -w, --write write permanent mbsync configuration\n"
  109. " -W, --writeto FILE write permanent mbsync configuration to FILE\n",
  110. code ? stderr : stdout );
  111. exit( code );
  112. }
  113. static const char *
  114. strrstr( const char *h, const char *n )
  115. {
  116. char *p = strstr( h, n );
  117. if (!p)
  118. return 0;
  119. do {
  120. h = p;
  121. p = strstr( h + 1, n );
  122. } while (p);
  123. return h;
  124. }
  125. static void
  126. add_arg( char ***args, const char *arg )
  127. {
  128. int nu = 0;
  129. if (*args)
  130. for (; (*args)[nu]; nu++);
  131. *args = nfrealloc( *args, sizeof(char *) * (nu + 2));
  132. (*args)[nu] = nfstrdup( arg );
  133. (*args)[nu + 1] = 0;
  134. }
  135. #define OP_FAST (1<<2)
  136. #define OP_CREATE_REMOTE (1<<3)
  137. #define OP_CREATE_LOCAL (1<<4)
  138. int Quiet, Verbose, Debug;
  139. config_t global, *boxes;
  140. const char *maildir, *xmaildir, *folder, *inbox;
  141. int o2o, altmap, delete, expunge;
  142. const char *Home;
  143. int HomeLen;
  144. int
  145. main( int argc, char **argv )
  146. {
  147. config_t *box, **stor;
  148. char *config = 0, *outconfig = 0, **args;
  149. int i, pl, fd, mod, all, list, ops, writeout;
  150. struct stat st;
  151. char path1[_POSIX_PATH_MAX], path2[_POSIX_PATH_MAX];
  152. if (!(Home = getenv("HOME"))) {
  153. fputs( "Fatal: $HOME not set\n", stderr );
  154. return 1;
  155. }
  156. HomeLen = strlen( Home );
  157. /* defaults */
  158. /* XXX the precedence is borked:
  159. it's defaults < cmdline < file instead of defaults < file < cmdline */
  160. #ifdef BSD
  161. global.user = getenv( "USER" );
  162. #else
  163. global.user = getenv( "LOGNAME" );
  164. #endif
  165. global.port = 143;
  166. global.box = "INBOX";
  167. global.use_namespace = 1;
  168. global.require_ssl = 1;
  169. global.use_tlsv1 = 1;
  170. folder = "";
  171. maildir = "~";
  172. xmaildir = Home;
  173. #define FLAGS "wW:alCLRc:defhp:qu:P:r:F:M:1I:s:vVD"
  174. mod = all = list = ops = writeout = Quiet = Verbose = Debug = 0;
  175. #ifdef HAVE_GETOPT_LONG
  176. while ((i = getopt_long( argc, argv, FLAGS, Opts, NULL )) != -1)
  177. #else
  178. while ((i = getopt( argc, argv, FLAGS )) != -1)
  179. #endif
  180. {
  181. switch (i) {
  182. case 'W':
  183. outconfig = optarg;
  184. /* plopp */
  185. case 'w':
  186. writeout = 1;
  187. break;
  188. case 'l':
  189. list = 1;
  190. /* plopp */
  191. case 'a':
  192. all = 1;
  193. break;
  194. case '1':
  195. o2o = 1;
  196. mod = 1;
  197. break;
  198. case 'C':
  199. ops |= OP_CREATE_REMOTE|OP_CREATE_LOCAL;
  200. break;
  201. case 'L':
  202. ops |= OP_CREATE_LOCAL;
  203. break;
  204. case 'R':
  205. ops |= OP_CREATE_REMOTE;
  206. break;
  207. case 'c':
  208. config = optarg;
  209. break;
  210. case 'd':
  211. delete = 1;
  212. break;
  213. case 'e':
  214. expunge = 1;
  215. break;
  216. case 'f':
  217. ops |= OP_FAST;
  218. break;
  219. case 'p':
  220. global.port = atoi( optarg );
  221. mod = 1;
  222. break;
  223. case 'r':
  224. global.box = optarg;
  225. mod = 1;
  226. break;
  227. case 'F':
  228. folder = optarg;
  229. mod = 1;
  230. break;
  231. case 'M':
  232. maildir = optarg;
  233. mod = 1;
  234. break;
  235. case 'I':
  236. inbox = optarg;
  237. mod = 1;
  238. break;
  239. case 's':
  240. #ifdef HAVE_LIBSSL
  241. if (!strncasecmp( "imaps:", optarg, 6 )) {
  242. global.use_imaps = 1;
  243. global.port = 993;
  244. global.use_sslv2 = 1;
  245. global.use_sslv3 = 1;
  246. optarg += 6;
  247. }
  248. #endif
  249. global.host = optarg;
  250. mod = 1;
  251. break;
  252. case 'u':
  253. global.user = optarg;
  254. mod = 1;
  255. break;
  256. case 'P':
  257. global.pass = optarg;
  258. mod = 1;
  259. break;
  260. case 'D':
  261. Debug = 1;
  262. break;
  263. case 'V':
  264. Verbose++;
  265. break;
  266. case 'q':
  267. Quiet++;
  268. break;
  269. case 'v':
  270. version();
  271. case 'h':
  272. usage( 0 );
  273. default:
  274. usage( 1 );
  275. }
  276. }
  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 (!(box = 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. fprintf( stderr, "%s: %s\n", xmaildir, strerror(errno) );
  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. fprintf( stderr, "Error: cannot write new config %s: %s\n", outconfig, strerror(errno) );
  363. return 1;
  364. }
  365. } else {
  366. strcpy( path2, "/tmp/mbsyncrcXXXXXX" );
  367. if ((fd = mkstemp( path2 )) < 0) {
  368. fprintf( stderr, "Can't create temp file\n" );
  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. perror( args[0] );
  415. return 1;
  416. }