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