iksroster.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /* iksemel (XML parser for Jabber)
  2. ** Copyright (C) 2000-2004 Gurer Ozen <madcat@e-kolay.net>
  3. ** This code is free software; you can redistribute it and/or
  4. ** modify it under the terms of GNU Lesser General Public License.
  5. */
  6. #include "common.h"
  7. #include "iksemel.h"
  8. #ifdef HAVE_GETOPT_LONG
  9. #include <getopt.h>
  10. #endif
  11. #ifdef _WIN32
  12. #include <winsock.h>
  13. #endif
  14. #ifdef HAVE_GETOPT_LONG
  15. static struct option longopts[] = {
  16. { "backup", required_argument, 0, 'b' },
  17. { "restore", required_argument, 0, 'r' },
  18. { "file", required_argument, 0, 'f' },
  19. { "timeout", required_argument, 0, 't' },
  20. { "secure", 0, 0, 's' },
  21. { "sasl", 0, 0, 'a' },
  22. { "log", 0, 0, 'l' },
  23. { "help", 0, 0, 'h' },
  24. { "version", 0, 0, 'V' },
  25. { 0, 0, 0, 0 }
  26. };
  27. #endif
  28. static char *shortopts = "b:r:f:t:salhV";
  29. static void
  30. print_usage (void)
  31. {
  32. puts ("Usage: iksroster [OPTIONS]\n"
  33. "This is a backup tool for your jabber roster.\n"
  34. " -b, --backup=JID Download roster from the server.\n"
  35. " -r, --restore=JID Upload roster to the server.\n"
  36. " -f, --file=FILE Load/Save roster to this file.\n"
  37. " -t, --timeout=SECS Set network timeout.\n"
  38. " -s, --secure Use encrypted connection.\n"
  39. " -a, --sasl Use SASL authentication.\n"
  40. " -l, --log Print exchanged xml data.\n"
  41. " -h, --help Print this text and exit.\n"
  42. " -V, --version Print version and exit.\n"
  43. #ifndef HAVE_GETOPT_LONG
  44. "(long options are not supported on your system)\n"
  45. #endif
  46. #ifndef HAVE_GNUTLS
  47. "(secure connections are not supported on your system)\n"
  48. #endif
  49. "Report bugs to <iksemel-dev@jabberstudio.org>.");
  50. }
  51. /* stuff we keep per session */
  52. struct session {
  53. iksparser *prs;
  54. iksid *acc;
  55. char *pass;
  56. int features;
  57. int authorized;
  58. int counter;
  59. int set_roster;
  60. int job_done;
  61. };
  62. /* precious roster we'll deal with */
  63. iks *my_roster;
  64. /* out packet filter */
  65. iksfilter *my_filter;
  66. /* connection time outs if nothing comes for this much seconds */
  67. int opt_timeout = 30;
  68. /* connection flags */
  69. int opt_use_tls;
  70. int opt_use_sasl;
  71. int opt_log;
  72. void
  73. j_error (char *msg)
  74. {
  75. fprintf (stderr, "iksroster: %s\n", msg);
  76. exit (2);
  77. }
  78. int
  79. on_result (struct session *sess, ikspak *pak)
  80. {
  81. iks *x;
  82. if (sess->set_roster == 0) {
  83. x = iks_make_iq (IKS_TYPE_GET, IKS_NS_ROSTER);
  84. iks_insert_attrib (x, "id", "roster");
  85. iks_send (sess->prs, x);
  86. iks_delete (x);
  87. } else {
  88. iks_insert_attrib (my_roster, "type", "set");
  89. iks_send (sess->prs, my_roster);
  90. }
  91. return IKS_FILTER_EAT;
  92. }
  93. int
  94. on_stream (struct session *sess, int type, iks *node)
  95. {
  96. sess->counter = opt_timeout;
  97. switch (type) {
  98. case IKS_NODE_START:
  99. if (opt_use_tls && !iks_is_secure (sess->prs)) {
  100. iks_start_tls (sess->prs);
  101. break;
  102. }
  103. if (!opt_use_sasl) {
  104. iks *x;
  105. x = iks_make_auth (sess->acc, sess->pass, iks_find_attrib (node, "id"));
  106. iks_insert_attrib (x, "id", "auth");
  107. iks_send (sess->prs, x);
  108. iks_delete (x);
  109. }
  110. break;
  111. case IKS_NODE_NORMAL:
  112. if (strcmp ("stream:features", iks_name (node)) == 0) {
  113. sess->features = iks_stream_features (node);
  114. if (opt_use_sasl) {
  115. if (opt_use_tls && !iks_is_secure (sess->prs)) break;
  116. if (sess->authorized) {
  117. iks *t;
  118. if (sess->features & IKS_STREAM_BIND) {
  119. t = iks_make_resource_bind (sess->acc);
  120. iks_send (sess->prs, t);
  121. iks_delete (t);
  122. }
  123. if (sess->features & IKS_STREAM_SESSION) {
  124. t = iks_make_session ();
  125. iks_insert_attrib (t, "id", "auth");
  126. iks_send (sess->prs, t);
  127. iks_delete (t);
  128. }
  129. } else {
  130. if (sess->features & IKS_STREAM_SASL_MD5)
  131. iks_start_sasl (sess->prs, IKS_SASL_DIGEST_MD5, sess->acc->user, sess->pass);
  132. else if (sess->features & IKS_STREAM_SASL_PLAIN)
  133. iks_start_sasl (sess->prs, IKS_SASL_PLAIN, sess->acc->user, sess->pass);
  134. }
  135. }
  136. } else if (strcmp ("failure", iks_name (node)) == 0) {
  137. j_error ("sasl authentication failed");
  138. } else if (strcmp ("success", iks_name (node)) == 0) {
  139. sess->authorized = 1;
  140. iks_send_header (sess->prs, sess->acc->server);
  141. } else {
  142. ikspak *pak;
  143. pak = iks_packet (node);
  144. iks_filter_packet (my_filter, pak);
  145. if (sess->job_done == 1) return IKS_HOOK;
  146. }
  147. break;
  148. case IKS_NODE_STOP:
  149. j_error ("server disconnected");
  150. case IKS_NODE_ERROR:
  151. j_error ("stream error");
  152. }
  153. if (node) iks_delete (node);
  154. return IKS_OK;
  155. }
  156. int
  157. on_error (void *user_data, ikspak *pak)
  158. {
  159. j_error ("authorization failed");
  160. return IKS_FILTER_EAT;
  161. }
  162. int
  163. on_roster (struct session *sess, ikspak *pak)
  164. {
  165. my_roster = pak->x;
  166. sess->job_done = 1;
  167. return IKS_FILTER_EAT;
  168. }
  169. void
  170. on_log (struct session *sess, const char *data, size_t size, int is_incoming)
  171. {
  172. if (iks_is_secure (sess->prs)) fprintf (stderr, "Sec");
  173. if (is_incoming) fprintf (stderr, "RECV"); else fprintf (stderr, "SEND");
  174. fprintf (stderr, "[%s]\n", data);
  175. }
  176. void
  177. j_setup_filter (struct session *sess)
  178. {
  179. if (my_filter) iks_filter_delete (my_filter);
  180. my_filter = iks_filter_new ();
  181. iks_filter_add_rule (my_filter, (iksFilterHook *) on_result, sess,
  182. IKS_RULE_TYPE, IKS_PAK_IQ,
  183. IKS_RULE_SUBTYPE, IKS_TYPE_RESULT,
  184. IKS_RULE_ID, "auth",
  185. IKS_RULE_DONE);
  186. iks_filter_add_rule (my_filter, on_error, sess,
  187. IKS_RULE_TYPE, IKS_PAK_IQ,
  188. IKS_RULE_SUBTYPE, IKS_TYPE_ERROR,
  189. IKS_RULE_ID, "auth",
  190. IKS_RULE_DONE);
  191. iks_filter_add_rule (my_filter, (iksFilterHook *) on_roster, sess,
  192. IKS_RULE_TYPE, IKS_PAK_IQ,
  193. IKS_RULE_SUBTYPE, IKS_TYPE_RESULT,
  194. IKS_RULE_ID, "roster",
  195. IKS_RULE_DONE);
  196. }
  197. void
  198. j_connect (char *jabber_id, char *pass, int set_roster)
  199. {
  200. struct session sess;
  201. int e;
  202. memset (&sess, 0, sizeof (sess));
  203. sess.prs = iks_stream_new (IKS_NS_CLIENT, &sess, (iksStreamHook *) on_stream);
  204. if (opt_log) iks_set_log_hook (sess.prs, (iksLogHook *) on_log);
  205. sess.acc = iks_id_new (iks_parser_stack (sess.prs), jabber_id);
  206. if (NULL == sess.acc->resource) {
  207. /* user gave no resource name, use the default */
  208. char *tmp;
  209. tmp = iks_malloc (strlen (sess.acc->user) + strlen (sess.acc->server) + 9 + 3);
  210. sprintf (tmp, "%s@%s/%s", sess.acc->user, sess.acc->server, "iksroster");
  211. sess.acc = iks_id_new (iks_parser_stack (sess.prs), tmp);
  212. iks_free (tmp);
  213. }
  214. sess.pass = pass;
  215. sess.set_roster = set_roster;
  216. j_setup_filter (&sess);
  217. e = iks_connect_tcp (sess.prs, sess.acc->server, IKS_JABBER_PORT);
  218. switch (e) {
  219. case IKS_OK:
  220. break;
  221. case IKS_NET_NODNS:
  222. j_error ("hostname lookup failed");
  223. case IKS_NET_NOCONN:
  224. j_error ("connection failed");
  225. default:
  226. j_error ("io error");
  227. }
  228. sess.counter = opt_timeout;
  229. while (1) {
  230. e = iks_recv (sess.prs, 1);
  231. if (IKS_HOOK == e) break;
  232. if (IKS_NET_TLSFAIL == e) j_error ("tls handshake failed");
  233. if (IKS_OK != e) j_error ("io error");
  234. sess.counter--;
  235. if (sess.counter == 0) j_error ("network timeout");
  236. }
  237. iks_parser_delete (sess.prs);
  238. }
  239. int
  240. main (int argc, char *argv[])
  241. {
  242. char *from = NULL;
  243. char *to = NULL;
  244. char *file = NULL;
  245. char from_pw[128], to_pw[128];
  246. int c;
  247. #ifdef HAVE_GETOPT_LONG
  248. int i;
  249. while ((c = getopt_long (argc, argv, shortopts, longopts, &i)) != -1) {
  250. #else
  251. while ((c = getopt (argc, argv, shortopts)) != -1) {
  252. #endif
  253. switch (c) {
  254. case 'b':
  255. from = optarg;
  256. printf ("Password for %s: ", optarg);
  257. fflush (stdout);
  258. fgets (from_pw, 127, stdin);
  259. strtok (from_pw, "\r\n");
  260. break;
  261. case 'r':
  262. to = optarg;
  263. printf ("Password for %s: ", optarg);
  264. fflush (stdout);
  265. fgets (to_pw, 127, stdin);
  266. strtok (to_pw, "\r\n");
  267. break;
  268. case 'f':
  269. if (file) free(file);
  270. file = strdup (optarg);
  271. break;
  272. case 't':
  273. opt_timeout = atoi (optarg);
  274. if (opt_timeout < 10) opt_timeout = 10;
  275. break;
  276. case 's':
  277. if (!iks_has_tls ()) {
  278. puts ("Cannot make encrypted connections.");
  279. puts ("iksemel library is not compiled with GnuTLS support.");
  280. exit (1);
  281. }
  282. opt_use_tls = 1;
  283. break;
  284. case 'a':
  285. opt_use_sasl = 1;
  286. break;
  287. case 'l':
  288. opt_log = 1;
  289. break;
  290. case 'h':
  291. print_usage ();
  292. exit (0);
  293. case 'V':
  294. puts ("iksroster (iksemel) "VERSION);
  295. exit (0);
  296. }
  297. }
  298. if (from == NULL && to == NULL) {
  299. puts ("What I'm supposed to do?");
  300. print_usage ();
  301. exit (1);
  302. }
  303. if (to && (from == NULL && file == NULL)) {
  304. puts ("Store which roster?");
  305. print_usage ();
  306. exit (1);
  307. }
  308. #ifdef _WIN32
  309. WSADATA wsaData;
  310. WSAStartup (MAKEWORD (1,1), &wsaData);
  311. #endif
  312. if (from) {
  313. j_connect (from, from_pw, 0);
  314. if (file) {
  315. switch (iks_save (file, my_roster)) {
  316. case IKS_OK:
  317. break;
  318. case IKS_FILE_NOACCESS:
  319. j_error ("cannot write to file");
  320. default:
  321. j_error ("file io error");
  322. }
  323. }
  324. } else {
  325. switch (iks_load (file, &my_roster)) {
  326. case IKS_OK:
  327. break;
  328. case IKS_FILE_NOFILE:
  329. j_error ("file not found");
  330. case IKS_FILE_NOACCESS:
  331. j_error ("cannot read file");
  332. default:
  333. j_error ("file io error");
  334. }
  335. }
  336. if (to) {
  337. j_connect (to, to_pw, 1);
  338. }
  339. #ifdef _WIN32
  340. WSACleanup ();
  341. #endif
  342. if (file) free(file);
  343. return 0;
  344. }