diff options
Diffstat (limited to 'tools/iksroster.c')
-rw-r--r-- | tools/iksroster.c | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/tools/iksroster.c b/tools/iksroster.c new file mode 100644 index 0000000..144f3de --- /dev/null +++ b/tools/iksroster.c @@ -0,0 +1,379 @@ +/* iksemel (XML parser for Jabber) +** Copyright (C) 2000-2004 Gurer Ozen <madcat@e-kolay.net> +** This code is free software; you can redistribute it and/or +** modify it under the terms of GNU Lesser General Public License. +*/ + +#include "common.h" +#include "iksemel.h" + +#ifdef HAVE_GETOPT_LONG +#include <getopt.h> +#endif + +#ifdef _WIN32 +#include <winsock.h> +#endif + +#ifdef HAVE_GETOPT_LONG +static struct option longopts[] = { + { "backup", required_argument, 0, 'b' }, + { "restore", required_argument, 0, 'r' }, + { "file", required_argument, 0, 'f' }, + { "timeout", required_argument, 0, 't' }, + { "secure", 0, 0, 's' }, + { "sasl", 0, 0, 'a' }, + { "log", 0, 0, 'l' }, + { "help", 0, 0, 'h' }, + { "version", 0, 0, 'V' }, + { 0, 0, 0, 0 } +}; +#endif + +static char *shortopts = "b:r:f:t:salhV"; + +static void +print_usage (void) +{ + puts ("Usage: iksroster [OPTIONS]\n" + "This is a backup tool for your jabber roster.\n" + " -b, --backup=JID Download roster from the server.\n" + " -r, --restore=JID Upload roster to the server.\n" + " -f, --file=FILE Load/Save roster to this file.\n" + " -t, --timeout=SECS Set network timeout.\n" + " -s, --secure Use encrypted connection.\n" + " -a, --sasl Use SASL authentication.\n" + " -l, --log Print exchanged xml data.\n" + " -h, --help Print this text and exit.\n" + " -V, --version Print version and exit.\n" +#ifndef HAVE_GETOPT_LONG + "(long options are not supported on your system)\n" +#endif +#ifndef HAVE_GNUTLS + "(secure connections are not supported on your system)\n" +#endif + "Report bugs to <iksemel-dev@jabberstudio.org>."); +} + +/* stuff we keep per session */ +struct session { + iksparser *prs; + iksid *acc; + char *pass; + int features; + int authorized; + int counter; + int set_roster; + int job_done; +}; + +/* precious roster we'll deal with */ +iks *my_roster; + +/* out packet filter */ +iksfilter *my_filter; + +/* connection time outs if nothing comes for this much seconds */ +int opt_timeout = 30; + +/* connection flags */ +int opt_use_tls; +int opt_use_sasl; +int opt_log; + +void +j_error (char *msg) +{ + fprintf (stderr, "iksroster: %s\n", msg); + exit (2); +} + +int +on_result (struct session *sess, ikspak *pak) +{ + iks *x; + + if (sess->set_roster == 0) { + x = iks_make_iq (IKS_TYPE_GET, IKS_NS_ROSTER); + iks_insert_attrib (x, "id", "roster"); + iks_send (sess->prs, x); + iks_delete (x); + } else { + iks_insert_attrib (my_roster, "type", "set"); + iks_send (sess->prs, my_roster); + } + return IKS_FILTER_EAT; +} + +int +on_stream (struct session *sess, int type, iks *node) +{ + sess->counter = opt_timeout; + + switch (type) { + case IKS_NODE_START: + if (opt_use_tls && !iks_is_secure (sess->prs)) { + iks_start_tls (sess->prs); + break; + } + if (!opt_use_sasl) { + iks *x; + + x = iks_make_auth (sess->acc, sess->pass, iks_find_attrib (node, "id")); + iks_insert_attrib (x, "id", "auth"); + iks_send (sess->prs, x); + iks_delete (x); + } + break; + + case IKS_NODE_NORMAL: + if (strcmp ("stream:features", iks_name (node)) == 0) { + sess->features = iks_stream_features (node); + if (opt_use_sasl) { + if (opt_use_tls && !iks_is_secure (sess->prs)) break; + if (sess->authorized) { + iks *t; + if (sess->features & IKS_STREAM_BIND) { + t = iks_make_resource_bind (sess->acc); + iks_send (sess->prs, t); + iks_delete (t); + } + if (sess->features & IKS_STREAM_SESSION) { + t = iks_make_session (); + iks_insert_attrib (t, "id", "auth"); + iks_send (sess->prs, t); + iks_delete (t); + } + } else { + if (sess->features & IKS_STREAM_SASL_MD5) + iks_start_sasl (sess->prs, IKS_SASL_DIGEST_MD5, sess->acc->user, sess->pass); + else if (sess->features & IKS_STREAM_SASL_PLAIN) + iks_start_sasl (sess->prs, IKS_SASL_PLAIN, sess->acc->user, sess->pass); + } + } + } else if (strcmp ("failure", iks_name (node)) == 0) { + j_error ("sasl authentication failed"); + } else if (strcmp ("success", iks_name (node)) == 0) { + sess->authorized = 1; + iks_send_header (sess->prs, sess->acc->server); + } else { + ikspak *pak; + + pak = iks_packet (node); + iks_filter_packet (my_filter, pak); + if (sess->job_done == 1) return IKS_HOOK; + } + break; + + case IKS_NODE_STOP: + j_error ("server disconnected"); + + case IKS_NODE_ERROR: + j_error ("stream error"); + } + + if (node) iks_delete (node); + return IKS_OK; +} + +int +on_error (void *user_data, ikspak *pak) +{ + j_error ("authorization failed"); + return IKS_FILTER_EAT; +} + +int +on_roster (struct session *sess, ikspak *pak) +{ + my_roster = pak->x; + sess->job_done = 1; + return IKS_FILTER_EAT; +} + +void +on_log (struct session *sess, const char *data, size_t size, int is_incoming) +{ + if (iks_is_secure (sess->prs)) fprintf (stderr, "Sec"); + if (is_incoming) fprintf (stderr, "RECV"); else fprintf (stderr, "SEND"); + fprintf (stderr, "[%s]\n", data); +} + +void +j_setup_filter (struct session *sess) +{ + if (my_filter) iks_filter_delete (my_filter); + my_filter = iks_filter_new (); + iks_filter_add_rule (my_filter, (iksFilterHook *) on_result, sess, + IKS_RULE_TYPE, IKS_PAK_IQ, + IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, + IKS_RULE_ID, "auth", + IKS_RULE_DONE); + iks_filter_add_rule (my_filter, on_error, sess, + IKS_RULE_TYPE, IKS_PAK_IQ, + IKS_RULE_SUBTYPE, IKS_TYPE_ERROR, + IKS_RULE_ID, "auth", + IKS_RULE_DONE); + iks_filter_add_rule (my_filter, (iksFilterHook *) on_roster, sess, + IKS_RULE_TYPE, IKS_PAK_IQ, + IKS_RULE_SUBTYPE, IKS_TYPE_RESULT, + IKS_RULE_ID, "roster", + IKS_RULE_DONE); +} + +void +j_connect (char *jabber_id, char *pass, int set_roster) +{ + struct session sess; + int e; + + memset (&sess, 0, sizeof (sess)); + sess.prs = iks_stream_new (IKS_NS_CLIENT, &sess, (iksStreamHook *) on_stream); + if (opt_log) iks_set_log_hook (sess.prs, (iksLogHook *) on_log); + sess.acc = iks_id_new (iks_parser_stack (sess.prs), jabber_id); + if (NULL == sess.acc->resource) { + /* user gave no resource name, use the default */ + char *tmp; + tmp = iks_malloc (strlen (sess.acc->user) + strlen (sess.acc->server) + 9 + 3); + sprintf (tmp, "%s@%s/%s", sess.acc->user, sess.acc->server, "iksroster"); + sess.acc = iks_id_new (iks_parser_stack (sess.prs), tmp); + iks_free (tmp); + } + sess.pass = pass; + sess.set_roster = set_roster; + + j_setup_filter (&sess); + + e = iks_connect_tcp (sess.prs, sess.acc->server, IKS_JABBER_PORT); + switch (e) { + case IKS_OK: + break; + case IKS_NET_NODNS: + j_error ("hostname lookup failed"); + case IKS_NET_NOCONN: + j_error ("connection failed"); + default: + j_error ("io error"); + } + + sess.counter = opt_timeout; + while (1) { + e = iks_recv (sess.prs, 1); + if (IKS_HOOK == e) break; + if (IKS_NET_TLSFAIL == e) j_error ("tls handshake failed"); + if (IKS_OK != e) j_error ("io error"); + sess.counter--; + if (sess.counter == 0) j_error ("network timeout"); + } + iks_parser_delete (sess.prs); +} + +int +main (int argc, char *argv[]) +{ + char *from = NULL; + char *to = NULL; + char *file = NULL; + char from_pw[128], to_pw[128]; + int c; +#ifdef HAVE_GETOPT_LONG + int i; + + while ((c = getopt_long (argc, argv, shortopts, longopts, &i)) != -1) { +#else + while ((c = getopt (argc, argv, shortopts)) != -1) { +#endif + switch (c) { + case 'b': + from = optarg; + printf ("Password for %s: ", optarg); + fflush (stdout); + fgets (from_pw, 127, stdin); + strtok (from_pw, "\r\n"); + break; + case 'r': + to = optarg; + printf ("Password for %s: ", optarg); + fflush (stdout); + fgets (to_pw, 127, stdin); + strtok (to_pw, "\r\n"); + break; + case 'f': + file = strdup (optarg); + break; + case 't': + opt_timeout = atoi (optarg); + if (opt_timeout < 10) opt_timeout = 10; + break; + case 's': + if (!iks_has_tls ()) { + puts ("Cannot make encrypted connections."); + puts ("iksemel library is not compiled with GnuTLS support."); + exit (1); + } + opt_use_tls = 1; + break; + case 'a': + opt_use_sasl = 1; + break; + case 'l': + opt_log = 1; + break; + case 'h': + print_usage (); + exit (0); + case 'V': + puts ("iksroster (iksemel) "VERSION); + exit (0); + } + } + if (from == NULL && to == NULL) { + puts ("What I'm supposed to do?"); + print_usage (); + exit (1); + } + if (to && (from == NULL && file == NULL)) { + puts ("Store which roster?"); + print_usage (); + exit (1); + } + +#ifdef _WIN32 + WSADATA wsaData; + WSAStartup (MAKEWORD (1,1), &wsaData); +#endif + + if (from) { + j_connect (from, from_pw, 0); + if (file) { + switch (iks_save (file, my_roster)) { + case IKS_OK: + break; + case IKS_FILE_NOACCESS: + j_error ("cannot write to file"); + default: + j_error ("file io error"); + } + } + } else { + switch (iks_load (file, &my_roster)) { + case IKS_OK: + break; + case IKS_FILE_NOFILE: + j_error ("file not found"); + case IKS_FILE_NOACCESS: + j_error ("cannot read file"); + default: + j_error ("file io error"); + } + } + if (to) { + j_connect (to, to_pw, 1); + } + +#ifdef _WIN32 + WSACleanup (); +#endif + + return 0; +} |