88ffe66b26d12fe2417dd86d4ed16e90d77e65b9
[cascardo/pubsub-bot.git] / status.c
1 /*
2  *  Copyright (C) 2009  Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19
20 #include <glib.h>
21 #include <iksemel.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <limits.h>
27 #include <dbus/dbus.h>
28 #include <dbus/dbus-glib-lowlevel.h>
29
30 static char * server = "vespa.holoscopio.com";
31 static char * username = "pubsub";
32 static char * password = NULL;
33 static char * pbservice = "pubsub@vespa.holoscopio.com";
34 static char * authed_jid = "vespa";
35
36 static iks *
37 createiq (char *type, char *to, char *qnam, char *xmlns, iks **query)
38 {
39   static int id = 0;
40   char sid[32];;
41   iks *iq;
42   snprintf (sid, 32, "ps%d", id++);
43   iq = iks_new ("iq");
44   iks_insert_attrib (iq, "type", type);
45   iks_insert_attrib (iq, "to", to);
46   iks_insert_attrib (iq, "id", sid);
47   *query = iks_insert (iq, qnam);
48   iks_insert_attrib (*query, "xmlns", xmlns);
49   return iq;
50 }
51
52 static void
53 catnode (iksparser *parser, char *node)
54 {
55   iks *iq;
56   iks *query;
57   iq = createiq ("get", pbservice, "query",
58                  "http://jabber.org/protocol/disco#info", &query);
59   if (node != NULL)
60     iks_insert_attrib (query, "node", node);
61   iks_send (parser, iq);
62   iks_delete (iq);
63 }
64
65 static void
66 listnode (iksparser *parser, char *node)
67 {
68   iks *iq;
69   iks *query;
70   iq = createiq ("get", pbservice, "query",
71                  "http://jabber.org/protocol/disco#items", &query);
72   if (node != NULL)
73     iks_insert_attrib (query, "node", node);
74   iks_send (parser, iq);
75   iks_delete (iq);
76 }
77
78 static void
79 createnode (iksparser *parser, char *node)
80 {
81   iks *iq;
82   iks *query;
83   iq = createiq ("set", pbservice, "pubsub",
84                  "http://jabber.org/protocol/pubsub", &query);
85   iks_insert_attrib (iks_insert (query, "create"), "node", node);
86   iks_send (parser, iq);
87   iks_delete (iq);
88 }
89
90 static void
91 getnode (iksparser *parser, char *node)
92 {
93   iks *iq;
94   iks *query;
95   iq = createiq ("get", pbservice, "pubsub",
96                  "http://jabber.org/protocol/pubsub", &query);
97   iks_insert_attrib (iks_insert (query, "items"), "node", node);
98   iks_send (parser, iq);
99   iks_delete (iq);
100 }
101
102 static void
103 vcard (iksparser *parser)
104 {
105   iks *iq;
106   iks *query;
107   iq = createiq ("get", pbservice, "vCard", "vcard-temp", &query);
108   iks_send (parser, iq);
109   iks_delete (iq);
110 }
111
112 static iks *
113 createmood (char *line)
114 {
115   iks *mood;
116   mood = iks_new ("mood");
117   iks_insert_attrib (mood, "xmlns", "http://jabber.org/protocol/mood");
118   iks_insert (mood, line);
119   return mood;
120 }
121
122 static void
123 pushmood (iksparser *parser, char *node, char *line)
124 {
125   iks *iq;
126   iks *query;
127   iks *publish;
128   iks *item;
129   iks *mood;
130   iq = createiq ("set", pbservice, "pubsub",
131                  "http://jabber.org/protocol/pubsub", &query);
132   publish = iks_insert (query, "publish");
133   iks_insert_attrib (publish, "node", node);
134   item = iks_insert (publish, "item");
135   mood = createmood (line);
136   iks_insert_node (item, mood);
137   iks_send (parser, iq);
138   iks_delete (iq);
139 }
140
141 static iks *
142 createtune (char *line)
143 {
144   iks *tune;
145   tune = iks_new ("tune");
146   iks_insert_attrib (tune, "xmlns", "http://jabber.org/protocol/tune");
147   iks_insert_cdata (iks_insert (tune, "artist"), line, 0);
148   return tune;
149 }
150
151 static void
152 pushtune (iksparser *parser, char *node, iks *tune)
153 {
154   iks *iq;
155   iks *query;
156   iks *publish;
157   iks *item;
158   iq = createiq ("set", pbservice, "pubsub",
159                  "http://jabber.org/protocol/pubsub", &query);
160   publish = iks_insert (query, "publish");
161   iks_insert_attrib (publish, "node", node);
162   item = iks_insert (publish, "item");
163   iks_insert_node (item, tune);
164   printf ("debug: %s\n", iks_string (iks_stack (iq), iq));
165   iks_send (parser, iq);
166   iks_delete (iq);
167 }
168
169
170 static void
171 process_mood (iksparser *parser, char *cmdline)
172 {
173   char *cmd;
174   char *orig_cmdline;
175   orig_cmdline = cmdline = strdup (cmdline);
176   cmd = strsep (&cmdline, " ");
177   if (!strcmp (cmd, "ls"))
178     listnode (parser, cmdline);
179   else if (!strcmp (cmd, "cat"))
180     catnode (parser, cmdline);
181   else if (!strcmp (cmd, "vcard"))
182     vcard (parser);
183   else if (!strcmp (cmd, "create"))
184     createnode (parser, cmdline);
185   else if (!strcmp (cmd, "get"))
186     getnode (parser, cmdline);
187   else if (!strcmp (cmd, "mood"))
188     {
189       char *node;
190       node = "http://jabber.org/protocol/mood";
191       pushmood (parser, node, cmdline);
192     }
193   else if (!strcmp (cmd, "tune"))
194     {
195       char *node;
196       iks *tune;
197       node = "http://jabber.org/protocol/tune";
198       tune = createtune (cmdline);
199       pushtune (parser, node, tune);
200     }
201   free (orig_cmdline);
202 }
203
204 static int
205 xmpp_session_hook (iksparser *parser, iks *node)
206 {
207   iks *iq;
208   iq = iks_new ("iq");
209   iks_insert_attrib (iq, "type", "set");
210   iks_insert_attrib (iq, "id", "session1");
211   iks_insert_attrib (iks_insert (iq, "session"),
212                      "xmlns", "urn:ietf:params:xml:ns:xmpp-session");
213   iks_send (parser, iq);
214   iks_delete (iq);
215   return 0;
216 }
217
218 static int
219 xmpp_initial_presence_hook (iksparser *parser, iks *node)
220 {
221   iks *pres;
222   pres = iks_make_pres (IKS_SHOW_AVAILABLE, "Microblogging here!");
223   iks_insert_cdata (iks_insert (pres, "priority"), "-1", 2);
224   iks_send (parser, pres);
225   iks_delete (pres);
226   return 0;
227 }
228
229 static int
230 xmpp_id_hook (iksparser *parser, iks *node, char *id)
231 {
232   if (!iks_strcmp (id, "bind1"))
233     {
234       xmpp_session_hook (parser, node);
235       return 0;
236     }
237   else if (!iks_strcmp (id, "session1"))
238     {
239       xmpp_initial_presence_hook (parser, node);
240       return 0;
241     }
242   else if (!iks_strncmp (id, "ps", 2))
243     {
244       printf ("%s\n", iks_string (iks_stack (node), node));
245     }
246   return 1;
247 }
248
249 static int
250 xmpp_ns_hook (iksparser *parser, iks *node, char *ns)
251 {
252   return 1;
253 }
254
255 static int
256 xmpp_iq_error (iksparser *parser, iks *node)
257 {
258   iks *enode;
259   char *to;
260   char *from;
261   if (!iks_strcmp (iks_find_attrib (node, "type"), "error"))
262     return 1;
263   to = iks_find_attrib (node, "to");
264   from = iks_find_attrib (node, "from");
265   if (to)
266     iks_insert_attrib (node, "from", to);
267   else
268     iks_insert_attrib (node, "from", NULL);
269   if (from)
270     iks_insert_attrib (node, "to", from);
271   else
272     iks_insert_attrib (node, "to", NULL);
273   iks_insert_attrib (node, "type", "error");
274   enode = iks_insert (node, "error");
275   iks_insert_attrib (enode, "type", "cancel");
276   iks_insert_attrib (iks_insert (enode, "feature-not-implemented"),
277                      "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
278   iks_send (parser, node);
279   return 0;
280 }
281
282 static int
283 xmpp_tls_hook (iksparser *parser, iks *node)
284 {
285   iks_start_tls (parser);
286   return 0;
287 }
288
289 static int
290 xmpp_sasl_hook (iksparser *parser, iks* node)
291 {
292   if (password == NULL)
293     return -1;
294   iks_start_sasl (parser, IKS_SASL_DIGEST_MD5, username, password);
295   return 0;
296 }
297
298 static int
299 xmpp_bind_hook (iksparser *parser, iks *node)
300 {
301   iks *iq;
302   if (password)
303     {
304       memset (password, 0, sysconf (_SC_PASS_MAX));
305       free (password);
306       password = NULL;
307     }
308   iq = iks_new ("iq");
309   iks_insert_attrib (iq, "type", "set");
310   iks_insert_attrib (iq, "id", "bind1");
311   iks_insert_attrib (iks_insert (iq, "bind"),
312                      "xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
313   iks_send (parser, iq);
314   iks_delete (iq);
315   return 0;
316 }
317
318 static int
319 xmpp_features_hook (iksparser *parser, iks *node)
320 {
321   iks *feat;
322   char *ns;
323   for (feat = iks_child (node); feat != NULL; feat = iks_next (feat))
324     {
325       ns = iks_find_attrib (feat, "xmlns");
326       if (!iks_strcmp (ns, "urn:ietf:params:xml:ns:xmpp-tls"))
327         {
328           xmpp_tls_hook (parser, node);
329           return 0;
330         }
331       else if (!iks_strcmp (ns, "urn:ietf:params:xml:ns:xmpp-sasl"))
332         {
333           xmpp_sasl_hook (parser, node);
334           return 0;
335         }
336       else if (!iks_strcmp (ns, "urn:ietf:params:xml:ns:xmpp-bind"))
337         {
338           xmpp_bind_hook (parser, node);
339           return 0;
340         }
341     }
342   return 1;
343 }
344
345 static int
346 xmpp_other_hook (iksparser *parser, iks *node, char *ns)
347 {
348   if (!iks_strcmp (ns, "urn:ietf:params:xml:ns:xmpp-sasl"))
349     {
350       if (!iks_strcmp (iks_name (node), "success"))
351         iks_send_header (parser, server);
352       else if (!iks_strcmp (iks_name (node), "failure"))
353         printf ("failture to authenticate: %s\n",
354                 iks_string (iks_stack (node), node));
355       return 0;
356     }
357   return 1;
358 }
359
360
361 static int
362 hook (void *data, int type, iks *node)
363 {
364   char *name;
365   char *id;
366   char *ns;
367   char *iqns;
368   iksparser *parser;
369   parser = *(iksparser **) data;
370   name = iks_name (node);
371   id = iks_find_attrib (node, "id");
372   ns = iks_find_attrib (node, "xmlns"); 
373   iqns = iks_find_attrib (iks_child (node), "xmlns"); 
374   if (!iks_strcmp (name, "message"))
375     {
376       char *from;
377       from = iks_find_attrib (node, "from");
378       if (!iks_strncmp (from, authed_jid, iks_strlen (authed_jid)))
379         {
380           char *body = iks_find_cdata (node, "body");
381           if (body != NULL)
382             process_mood (parser, body);
383         }
384       else
385         {
386           printf ("%s is not authorized\n", from);
387         }
388       return IKS_OK;
389     }
390   else if (!iks_strcmp (name, "presence"))
391     {
392       char *from;
393       from = iks_find_attrib (node, "from");
394       printf ("I sense a disturbance in the force: %s!\n", from);
395     }
396   else if (!iks_strcmp (name, "iq"))
397     {
398       if (xmpp_id_hook (parser, node, id) == 0)
399         return IKS_OK;
400       if (xmpp_ns_hook (parser, node, iqns) == 0)
401         return IKS_OK;
402       xmpp_iq_error (parser, node);
403     }
404   else if (!iks_strcmp (name, "stream:features"))
405     {
406       if (xmpp_features_hook (parser, node) == 0)
407         return IKS_OK;
408     }
409   else if (!iks_strcmp (name, "stream:error"))
410     {
411       printf ("streamerror: %s\n", iks_string (iks_stack (node), node));
412     }
413   else
414     {
415       if (xmpp_other_hook (parser, node, ns) == 0)
416         return IKS_OK;
417       printf ("No no: %s\n", name);
418     }
419   return IKS_OK;
420 }
421
422 static gboolean
423 handler (GIOChannel *channel, GIOCondition cond, gpointer data)
424 {
425   iksparser *parser = data;
426   iks_recv (parser, 0);
427   return TRUE;
428 }
429
430 struct
431   { char * key; char * val; } keymaps[] =
432 {
433   { "artist", "artist" },
434   { "duration", "length" },
435   { "album", "source" },
436   { "title", "title" },
437   { "track-number", "track" },
438   { "location", "uri" },
439   { NULL, NULL }
440 };
441
442 static char *
443 map_key (char *orig)
444 {
445   int i;
446   for (i = 0; keymaps[i].key != NULL; i++)
447     if (strcmp (orig, keymaps[i].key) == 0)
448       return keymaps[i].val;
449   return NULL;
450 }
451
452 static void
453 tune_add_dbus_arg (iks *tune, DBusMessageIter *args)
454 {
455   DBusMessageIter entry;
456   DBusMessageIter var;
457   char *strkey = NULL;
458   char *strval = NULL;
459   dbus_message_iter_recurse (args, &entry);
460   if (dbus_message_iter_get_arg_type (&entry) == DBUS_TYPE_STRING)
461     {
462       dbus_message_iter_get_basic (&entry, &strkey);
463       dbus_message_iter_next (&entry);
464       if (dbus_message_iter_get_arg_type (&entry) == DBUS_TYPE_VARIANT)
465         {
466           dbus_message_iter_recurse (&entry, &var);
467           if (dbus_message_iter_get_arg_type (&var) == DBUS_TYPE_STRING)
468             {
469               dbus_message_iter_get_basic (&var, &strval);
470             }
471         }
472     }
473   else
474     printf ("%c\n", dbus_message_iter_get_arg_type (&entry));
475   strkey = map_key (strkey);
476   if (strkey && strval)
477     {
478       iks_insert_cdata (iks_insert (tune, strkey), strval, 0);
479     }
480 }
481
482 static iks *
483 tune_from_dbus (DBusMessage *msg)
484 {
485   DBusMessageIter args;
486   iks *tune;
487   tune = iks_new ("tune");
488   iks_insert_attrib (tune, "xmlns", "http://jabber.org/protocol/tune");
489   dbus_message_iter_init (msg, &args);
490   if (dbus_message_iter_get_arg_type (&args) == DBUS_TYPE_ARRAY)
491     {
492       DBusMessageIter dict;
493       dbus_message_iter_recurse (&args, &dict);
494       while (dbus_message_iter_get_arg_type (&dict) ==
495              DBUS_TYPE_DICT_ENTRY)
496         {
497           tune_add_dbus_arg (tune, &dict);
498           dbus_message_iter_next (&dict);
499         }
500     }
501   return tune;
502 }
503
504 static DBusHandlerResult
505 gettune (DBusConnection *conn, DBusMessage *msg, void *data)
506 {
507   iks *tune;
508   if (dbus_message_is_signal (msg, "org.MetaPlayer.tag", "playing"))
509     {
510       printf("publishing tune\n");
511       tune = tune_from_dbus (msg);
512       pushtune (data, "http://jabber.org/protocol/tune", tune);
513       return DBUS_HANDLER_RESULT_HANDLED;
514     }
515   else if (dbus_message_is_signal (msg, "org.MetaPlayer.player", "stop"))
516     {
517       printf("tune stopped\n");
518       tune = iks_new ("tune");
519       iks_insert_attrib (tune, "xmlns", "http://jabber.org/protocol/tune");
520       pushtune (data, "http://jabber.org/protocol/tune", tune);
521       return DBUS_HANDLER_RESULT_HANDLED;
522     }
523   else
524     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
525 }
526
527 static void
528 prepare_dbus (gpointer parser)
529 {
530   DBusConnection *conn;
531   conn = dbus_bus_get (DBUS_BUS_SESSION, NULL);
532   dbus_bus_register (conn, NULL);
533   dbus_bus_add_match (conn,
534                       "type='signal'", NULL);
535   dbus_connection_flush (conn);
536   dbus_connection_setup_with_g_main (conn, g_main_context_default ());
537   dbus_connection_add_filter (conn, gettune, parser, NULL);
538 }
539
540 static void
541 loop (iksparser *parser)
542 {
543   GIOChannel *channel;
544   channel = g_io_channel_unix_new (iks_fd (parser));
545   g_io_add_watch (channel, G_IO_IN, handler, parser);
546   prepare_dbus (parser);
547   g_main_loop_run (g_main_loop_new (g_main_context_default (), TRUE));
548 }
549
550 int
551 main (int argc, char **argv)
552 {
553   iksparser *parser;
554   int c;
555   int askpasswd = 0;
556   char *passwd = strdup ("pubsub");
557   while ((c = getopt (argc, argv, "s:u:p:i:a:w")) != -1)
558     {
559       switch (c)
560         {
561         case 's':
562           server = optarg;
563           break;
564         case 'u':
565           username = optarg;
566           break;
567         case 'p':
568           free (passwd);
569           passwd = strdup (optarg);
570           break;
571         case 'i':
572           pbservice = optarg;
573           break;
574         case 'a':
575           authed_jid = optarg;
576           break;
577         case 'w':
578           askpasswd = 1;
579           break;
580         }
581     }
582   if (askpasswd)
583     passwd = getpass ("Type password: ");
584   password = malloc (sysconf (_SC_PASS_MAX));
585   strcpy (password, passwd);
586   memset (passwd, 0, strlen (passwd));
587   if (!askpasswd)
588     {
589       free (passwd);
590       passwd = NULL;
591     }
592   parser = iks_stream_new ("jabber:client", &parser, hook);
593   iks_connect_tcp (parser, server, 5222);
594   loop (parser);
595   return 0;
596 }