Use getpass to get user password.
[cascardo/pubsub-bot.git] / status.c
index 78ccd6f..62572fb 100644 (file)
--- a/status.c
+++ b/status.c
  */
 
 
+#include <glib.h>
 #include <iksemel.h>
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib-lowlevel.h>
 
-struct udata
+static char * server = "vespa.holoscopio.com";
+static char * username = "pubsub";
+static char * password = NULL;
+static char * pbservice = "pubsub@vespa.holoscopio.com";
+static char * authed_jid = "vespa";
+
+static iks *
+createiq (char *type, char *to, char *qnam, char *xmlns, iks **query)
 {
-  iksparser *parser;
-  int auth;
-};
+  static int id = 0;
+  char sid[32];;
+  iks *iq;
+  snprintf (sid, 32, "ps%d", id++);
+  iq = iks_new ("iq");
+  iks_insert_attrib (iq, "type", type);
+  iks_insert_attrib (iq, "to", to);
+  iks_insert_attrib (iq, "id", sid);
+  *query = iks_insert (iq, qnam);
+  iks_insert_attrib (*query, "xmlns", xmlns);
+  return iq;
+}
 
-enum
+static void
+catnode (iksparser *parser, char *node)
 {
-  AUTH_NONE,
-  AUTH_TLS,
-  AUTH_SASL,
-  AUTH_BIND,
-  AUTH_SESSION
-} AUTH_STATE;
+  iks *iq;
+  iks *query;
+  iq = createiq ("get", pbservice, "query",
+                 "http://jabber.org/protocol/disco#info", &query);
+  if (node != NULL)
+    iks_insert_attrib (query, "node", node);
+  iks_send (parser, iq);
+  iks_delete (iq);
+}
+
+static void
+listnode (iksparser *parser, char *node)
+{
+  iks *iq;
+  iks *query;
+  iq = createiq ("get", pbservice, "query",
+                 "http://jabber.org/protocol/disco#items", &query);
+  if (node != NULL)
+    iks_insert_attrib (query, "node", node);
+  iks_send (parser, iq);
+  iks_delete (iq);
+}
+
+static void
+createnode (iksparser *parser, char *node)
+{
+  iks *iq;
+  iks *query;
+  iq = createiq ("set", pbservice, "pubsub",
+                 "http://jabber.org/protocol/pubsub", &query);
+  iks_insert_attrib (iks_insert (query, "create"), "node", node);
+  iks_send (parser, iq);
+  iks_delete (iq);
+}
+
+static void
+getnode (iksparser *parser, char *node)
+{
+  iks *iq;
+  iks *query;
+  iq = createiq ("get", pbservice, "pubsub",
+                 "http://jabber.org/protocol/pubsub", &query);
+  iks_insert_attrib (iks_insert (query, "items"), "node", node);
+  iks_send (parser, iq);
+  iks_delete (iq);
+}
+
+static void
+vcard (iksparser *parser)
+{
+  iks *iq;
+  iks *query;
+  iq = createiq ("get", pbservice, "vCard", "vcard-temp", &query);
+  iks_send (parser, iq);
+  iks_delete (iq);
+}
+
+static iks *
+createmood (char *line)
+{
+  iks *mood;
+  mood = iks_new ("mood");
+  iks_insert_attrib (mood, "xmlns", "http://jabber.org/protocol/mood");
+  iks_insert (mood, line);
+  return mood;
+}
+
+static void
+pushmood (iksparser *parser, char *node, char *line)
+{
+  iks *iq;
+  iks *query;
+  iks *publish;
+  iks *item;
+  iks *mood;
+  iq = createiq ("set", pbservice, "pubsub",
+                 "http://jabber.org/protocol/pubsub", &query);
+  publish = iks_insert (query, "publish");
+  iks_insert_attrib (publish, "node", node);
+  item = iks_insert (publish, "item");
+  mood = createmood (line);
+  iks_insert_node (item, mood);
+  iks_send (parser, iq);
+  iks_delete (iq);
+}
+
+static iks *
+createtune (char *line)
+{
+  iks *tune;
+  tune = iks_new ("tune");
+  iks_insert_attrib (tune, "xmlns", "http://jabber.org/protocol/tune");
+  iks_insert_cdata (iks_insert (tune, "artist"), line, 0);
+  return tune;
+}
+
+static void
+pushtune (iksparser *parser, char *node, iks *tune)
+{
+  iks *iq;
+  iks *query;
+  iks *publish;
+  iks *item;
+  iq = createiq ("set", pbservice, "pubsub",
+                 "http://jabber.org/protocol/pubsub", &query);
+  publish = iks_insert (query, "publish");
+  iks_insert_attrib (publish, "node", node);
+  item = iks_insert (publish, "item");
+  iks_insert_node (item, tune);
+  printf ("debug: %s\n", iks_string (iks_stack (iq), iq));
+  iks_send (parser, iq);
+  iks_delete (iq);
+}
+
+
+static void
+process_mood (iksparser *parser, char *cmdline)
+{
+  char *cmd;
+  char *orig_cmdline;
+  orig_cmdline = cmdline = strdup (cmdline);
+  cmd = strsep (&cmdline, " ");
+  if (!strcmp (cmd, "ls"))
+    listnode (parser, cmdline);
+  else if (!strcmp (cmd, "cat"))
+    catnode (parser, cmdline);
+  else if (!strcmp (cmd, "vcard"))
+    vcard (parser);
+  else if (!strcmp (cmd, "create"))
+    createnode (parser, cmdline);
+  else if (!strcmp (cmd, "get"))
+    getnode (parser, cmdline);
+  else if (!strcmp (cmd, "mood"))
+    {
+      char *node;
+      node = "http://jabber.org/protocol/mood";
+      pushmood (parser, node, cmdline);
+    }
+  else if (!strcmp (cmd, "tune"))
+    {
+      char *node;
+      iks *tune;
+      node = "http://jabber.org/protocol/tune";
+      tune = createtune (cmdline);
+      pushtune (parser, node, tune);
+    }
+  free (orig_cmdline);
+}
 
 static int
-hook (void *data, int type, iks *node)
+xmpp_session_hook (iksparser *parser, iks *node)
+{
+  iks *iq;
+  iq = iks_new ("iq");
+  iks_insert_attrib (iq, "type", "set");
+  iks_insert_attrib (iq, "id", "session1");
+  iks_insert_attrib (iks_insert (iq, "session"),
+                     "xmlns", "urn:ietf:params:xml:ns:xmpp-session");
+  iks_send (parser, iq);
+  iks_delete (iq);
+  return 0;
+}
+
+static int
+xmpp_initial_presence_hook (iksparser *parser, iks *node)
+{
+  iks *pres;
+  pres = iks_make_pres (IKS_SHOW_AVAILABLE, "Microblogging here!");
+  iks_send (parser, pres);
+  iks_delete (pres);
+  return 0;
+}
+
+static int
+xmpp_id_hook (iksparser *parser, iks *node, char *id)
+{
+  if (!iks_strcmp (id, "bind1"))
+    {
+      xmpp_session_hook (parser, node);
+      return 0;
+    }
+  else if (!iks_strcmp (id, "session1"))
+    {
+      xmpp_initial_presence_hook (parser, node);
+      return 0;
+    }
+  else if (!iks_strncmp (id, "ps", 2))
+    {
+      printf ("%s\n", iks_string (iks_stack (node), node));
+    }
+  return 1;
+}
+
+static int
+xmpp_ns_hook (iksparser *parser, iks *node, char *ns)
+{
+  return 1;
+}
+
+static int
+xmpp_iq_error (iksparser *parser, iks *node)
+{
+  iks *enode;
+  char *to;
+  char *from;
+  if (!iks_strcmp (iks_find_attrib (node, "type"), "error"))
+    return 1;
+  to = iks_find_attrib (node, "to");
+  from = iks_find_attrib (node, "from");
+  if (to)
+    iks_insert_attrib (node, "from", to);
+  else
+    iks_insert_attrib (node, "from", NULL);
+  if (from)
+    iks_insert_attrib (node, "to", from);
+  else
+    iks_insert_attrib (node, "to", NULL);
+  iks_insert_attrib (node, "type", "error");
+  enode = iks_insert (node, "error");
+  iks_insert_attrib (enode, "type", "cancel");
+  iks_insert_attrib (iks_insert (enode, "feature-not-implemented"),
+                     "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
+  iks_send (parser, node);
+  return 0;
+}
+
+static int
+xmpp_tls_hook (iksparser *parser, iks *node)
+{
+  iks_start_tls (parser);
+  return 0;
+}
+
+static int
+xmpp_sasl_hook (iksparser *parser, iks* node)
+{
+  if (password == NULL)
+    return -1;
+  iks_start_sasl (parser, IKS_SASL_DIGEST_MD5, username, password);
+  return 0;
+}
+
+static int
+xmpp_bind_hook (iksparser *parser, iks *node)
+{
+  iks *iq;
+  if (password)
+    {
+      memset (password, 0, sysconf (_SC_PASS_MAX));
+      free (password);
+      password = NULL;
+    }
+  iq = iks_new ("iq");
+  iks_insert_attrib (iq, "type", "set");
+  iks_insert_attrib (iq, "id", "bind1");
+  iks_insert_attrib (iks_insert (iq, "bind"),
+                     "xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
+  iks_send (parser, iq);
+  iks_delete (iq);
+  return 0;
+}
+
+static int
+xmpp_features_hook (iksparser *parser, iks *node)
 {
-  iks *sub;
-  struct udata *udata;
-  udata = (struct udata *) data;
-  if (type == IKS_NODE_START)
+  iks *feat;
+  char *ns;
+  for (feat = iks_child (node); feat != NULL; feat = iks_next (feat))
     {
-      if (!iks_is_secure (udata->parser) || udata->auth == AUTH_NONE)
+      ns = iks_find_attrib (feat, "xmlns");
+      if (!iks_strcmp (ns, "urn:ietf:params:xml:ns:xmpp-tls"))
         {
-          iks_start_tls (udata->parser);
-          udata->auth = AUTH_TLS;
+          xmpp_tls_hook (parser, node);
+          return 0;
         }
-      else if (udata->auth == AUTH_TLS)
+      else if (!iks_strcmp (ns, "urn:ietf:params:xml:ns:xmpp-sasl"))
         {
-          iks_start_sasl (udata->parser, IKS_SASL_PLAIN, "pubsub", "pubsub");
+          xmpp_sasl_hook (parser, node);
+          return 0;
         }
-      else if (udata->auth == AUTH_SASL)
+      else if (!iks_strcmp (ns, "urn:ietf:params:xml:ns:xmpp-bind"))
         {
-          iks_send_raw (udata->parser, "<iq type='set' id='boo'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/></iq>");
+          xmpp_bind_hook (parser, node);
+          return 0;
         }
     }
-  else
+  return 1;
+}
+
+static int
+xmpp_other_hook (iksparser *parser, iks *node, char *ns)
+{
+  if (!iks_strcmp (ns, "urn:ietf:params:xml:ns:xmpp-sasl"))
+    {
+      if (!iks_strcmp (iks_name (node), "success"))
+        iks_send_header (parser, server);
+      else if (!iks_strcmp (iks_name (node), "failure"))
+        printf ("failture to authenticate: %s\n",
+                iks_string (iks_stack (node), node));
+      return 0;
+    }
+  return 1;
+}
+
+
+static int
+hook (void *data, int type, iks *node)
+{
+  char *name;
+  char *id;
+  char *ns;
+  char *iqns;
+  iksparser *parser;
+  parser = *(iksparser **) data;
+  name = iks_name (node);
+  id = iks_find_attrib (node, "id");
+  ns = iks_find_attrib (node, "xmlns"); 
+  iqns = iks_find_attrib (iks_child (node), "xmlns"); 
+  if (!iks_strcmp (name, "message"))
     {
-      if (!iks_strcmp (iks_find_attrib (node, "id"), "boo"))
+      char *from;
+      from = iks_find_attrib (node, "from");
+      if (!iks_strncmp (from, authed_jid, iks_strlen (authed_jid)))
         {
-          udata->auth = AUTH_BIND;
-          iks_send_raw (udata->parser, "<iq type='set' id='goo'><session xmlns='urn:ietf:params:xml:ns:xmpp-session'/></iq>");
+          char *body = iks_find_cdata (node, "body");
+          if (body != NULL)
+            process_mood (parser, body);
         }
-      else if (!iks_strcmp (iks_find_attrib (node, "id"), "goo"))
+      else
         {
-          printf ("goooooo\n");
-          sub = iks_make_s10n (IKS_TYPE_SUBSCRIBED, "cascardo@jabber-br.org", "");
-          iks_send (udata->parser, sub);
-          iks_delete (sub);
-          sub = iks_make_pres (IKS_SHOW_AVAILABLE, "here");
-          iks_insert_attrib (sub, "to", "cascardo@jabber-br.org");
-          iks_send (udata->parser, sub);
-          iks_delete (sub);
+          printf ("%s is not authorized\n", from);
         }
-      if (!iks_strcmp (iks_name (node), "success"))
+      return IKS_OK;
+    }
+  else if (!iks_strcmp (name, "presence"))
+    {
+      char *from;
+      from = iks_find_attrib (node, "from");
+      printf ("I sense a disturbance in the force: %s!\n", from);
+    }
+  else if (!iks_strcmp (name, "iq"))
+    {
+      if (xmpp_id_hook (parser, node, id) == 0)
+        return IKS_OK;
+      if (xmpp_ns_hook (parser, node, iqns) == 0)
+        return IKS_OK;
+      xmpp_iq_error (parser, node);
+    }
+  else if (!iks_strcmp (name, "stream:features"))
+    {
+      if (xmpp_features_hook (parser, node) == 0)
+        return IKS_OK;
+    }
+  else if (!iks_strcmp (name, "stream:error"))
+    {
+      printf ("streamerror: %s\n", iks_string (iks_stack (node), node));
+    }
+  else
+    {
+      if (xmpp_other_hook (parser, node, ns) == 0)
+        return IKS_OK;
+      printf ("No no: %s\n", name);
+    }
+  return IKS_OK;
+}
+
+static gboolean
+handler (GIOChannel *channel, GIOCondition cond, gpointer data)
+{
+  iksparser *parser = data;
+  iks_recv (parser, 0);
+  return TRUE;
+}
+
+struct
+  { char * key; char * val; } keymaps[] =
+{
+  { "artist", "artist" },
+  { "duration", "length" },
+  { "album", "source" },
+  { "title", "title" },
+  { "track-number", "track" },
+  { "location", "uri" },
+  { NULL, NULL }
+};
+
+static char *
+map_key (char *orig)
+{
+  int i;
+  for (i = 0; keymaps[i].key != NULL; i++)
+    if (strcmp (orig, keymaps[i].key) == 0)
+      return keymaps[i].val;
+  return NULL;
+}
+
+static void
+tune_add_dbus_arg (iks *tune, DBusMessageIter *args)
+{
+  DBusMessageIter entry;
+  DBusMessageIter var;
+  char *strkey = NULL;
+  char *strval = NULL;
+  dbus_message_iter_recurse (args, &entry);
+  if (dbus_message_iter_get_arg_type (&entry) == DBUS_TYPE_STRING)
+    {
+      dbus_message_iter_get_basic (&entry, &strkey);
+      dbus_message_iter_next (&entry);
+      if (dbus_message_iter_get_arg_type (&entry) == DBUS_TYPE_VARIANT)
         {
-          iks_send_header (udata->parser, "jabber-br.org");
-          udata->auth = AUTH_SASL;
+          dbus_message_iter_recurse (&entry, &var);
+          if (dbus_message_iter_get_arg_type (&var) == DBUS_TYPE_STRING)
+            {
+              dbus_message_iter_get_basic (&var, &strval);
+            }
         }
-      printf ("%s\n", iks_string (iks_stack (node), node));
     }
-  return IKS_OK;
+  else
+    printf ("%c\n", dbus_message_iter_get_arg_type (&entry));
+  strkey = map_key (strkey);
+  if (strkey && strval)
+    {
+      iks_insert_cdata (iks_insert (tune, strkey), strval, 0);
+    }
+}
+
+static iks *
+tune_from_dbus (DBusMessage *msg)
+{
+  DBusMessageIter args;
+  iks *tune;
+  tune = iks_new ("tune");
+  iks_insert_attrib (tune, "xmlns", "http://jabber.org/protocol/tune");
+  dbus_message_iter_init (msg, &args);
+  if (dbus_message_iter_get_arg_type (&args) == DBUS_TYPE_ARRAY)
+    {
+      DBusMessageIter dict;
+      dbus_message_iter_recurse (&args, &dict);
+      while (dbus_message_iter_get_arg_type (&dict) ==
+             DBUS_TYPE_DICT_ENTRY)
+        {
+          tune_add_dbus_arg (tune, &dict);
+          dbus_message_iter_next (&dict);
+        }
+    }
+  return tune;
+}
+
+static DBusHandlerResult
+gettune (DBusConnection *conn, DBusMessage *msg, void *data)
+{
+  iks *tune;
+  if (dbus_message_is_signal (msg, "org.MetaPlayer.tag", "playing"))
+    {
+      printf("publishing tune\n");
+      tune = tune_from_dbus (msg);
+      pushtune (data, "http://jabber.org/protocol/tune", tune);
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+  else if (dbus_message_is_signal (msg, "org.MetaPlayer.player", "stop"))
+    {
+      printf("tune stopped\n");
+      tune = iks_new ("tune");
+      iks_insert_attrib (tune, "xmlns", "http://jabber.org/protocol/tune");
+      pushtune (data, "http://jabber.org/protocol/tune", tune);
+      return DBUS_HANDLER_RESULT_HANDLED;
+    }
+  else
+    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void
+prepare_dbus (gpointer parser)
+{
+  DBusConnection *conn;
+  conn = dbus_bus_get (DBUS_BUS_SESSION, NULL);
+  dbus_bus_register (conn, NULL);
+  dbus_bus_add_match (conn,
+                      "type='signal'", NULL);
+  dbus_connection_flush (conn);
+  dbus_connection_setup_with_g_main (conn, g_main_context_default ());
+  dbus_connection_add_filter (conn, gettune, parser, NULL);
+}
+
+static void
+loop (iksparser *parser)
+{
+  GIOChannel *channel;
+  channel = g_io_channel_unix_new (iks_fd (parser));
+  g_io_add_watch (channel, G_IO_IN, handler, parser);
+  prepare_dbus (parser);
+  g_main_loop_run (g_main_loop_new (g_main_context_default (), TRUE));
 }
 
 int
 main (int argc, char **argv)
 {
-  struct udata udata;
-  udata.auth = 0;
-  udata.parser = iks_stream_new ("jabber:client", &udata, hook);
-  iks_connect_tcp (udata.parser, "jabber-br.org", 5222);
-  while (1)
-    iks_recv (udata.parser, -1);
+  iksparser *parser;
+  int c;
+  int askpasswd = 0;
+  char *passwd = strdup ("pubsub");
+  while ((c = getopt (argc, argv, "s:u:p:i:a:w")) != -1)
+    {
+      switch (c)
+        {
+        case 's':
+          server = optarg;
+          break;
+        case 'u':
+          username = optarg;
+          break;
+        case 'p':
+          free (passwd);
+          passwd = strdup (optarg);
+          break;
+        case 'i':
+          pbservice = optarg;
+          break;
+        case 'a':
+          authed_jid = optarg;
+          break;
+        case 'w':
+          askpasswd = 1;
+          break;
+        }
+    }
+  if (askpasswd)
+    passwd = getpass ("Type password: ");
+  password = malloc (sysconf (_SC_PASS_MAX));
+  strcpy (password, passwd);
+  memset (passwd, 0, strlen (passwd));
+  if (!askpasswd)
+    {
+      free (passwd);
+      passwd = NULL;
+    }
+  parser = iks_stream_new ("jabber:client", &parser, hook);
+  iks_connect_tcp (parser, server, 5222);
+  loop (parser);
   return 0;
 }