62572fbeeac7ac3b5fc5016d3823c628e1cf4dac
[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_send (parser, pres);
224   iks_delete (pres);
225   return 0;
226 }
227
228 static int
229 xmpp_id_hook (iksparser *parser, iks *node, char *id)
230 {
231   if (!iks_strcmp (id, "bind1"))
232     {
233       xmpp_session_hook (parser, node);
234       return 0;
235     }
236   else if (!iks_strcmp (id, "session1"))
237     {
238       xmpp_initial_presence_hook (parser, node);
239       return 0;
240     }
241   else if (!iks_strncmp (id, "ps", 2))
242     {
243       printf ("%s\n", iks_string (iks_stack (node), node));
244     }
245   return 1;
246 }
247
248 static int
249 xmpp_ns_hook (iksparser *parser, iks *node, char *ns)
250 {
251   return 1;
252 }
253
254 static int
255 xmpp_iq_error (iksparser *parser, iks *node)
256 {
257   iks *enode;
258   char *to;
259   char *from;
260   if (!iks_strcmp (iks_find_attrib (node, "type"), "error"))
261     return 1;
262   to = iks_find_attrib (node, "to");
263   from = iks_find_attrib (node, "from");
264   if (to)
265     iks_insert_attrib (node, "from", to);
266   else
267     iks_insert_attrib (node, "from", NULL);
268   if (from)
269     iks_insert_attrib (node, "to", from);
270   else
271     iks_insert_attrib (node, "to", NULL);
272   iks_insert_attrib (node, "type", "error");
273   enode = iks_insert (node, "error");
274   iks_insert_attrib (enode, "type", "cancel");
275   iks_insert_attrib (iks_insert (enode, "feature-not-implemented"),
276                      "xmlns", "urn:ietf:params:xml:ns:xmpp-stanzas");
277   iks_send (parser, node);
278   return 0;
279 }
280
281 static int
282 xmpp_tls_hook (iksparser *parser, iks *node)
283 {
284   iks_start_tls (parser);
285   return 0;
286 }
287
288 static int
289 xmpp_sasl_hook (iksparser *parser, iks* node)
290 {
291   if (password == NULL)
292     return -1;
293   iks_start_sasl (parser, IKS_SASL_DIGEST_MD5, username, password);
294   return 0;
295 }
296
297 static int
298 xmpp_bind_hook (iksparser *parser, iks *node)
299 {
300   iks *iq;
301   if (password)
302     {
303       memset (password, 0, sysconf (_SC_PASS_MAX));
304       free (password);
305       password = NULL;
306     }
307   iq = iks_new ("iq");
308   iks_insert_attrib (iq, "type", "set");
309   iks_insert_attrib (iq, "id", "bind1");
310   iks_insert_attrib (iks_insert (iq, "bind"),
311                      "xmlns", "urn:ietf:params:xml:ns:xmpp-bind");
312   iks_send (parser, iq);
313   iks_delete (iq);
314   return 0;
315 }
316
317 static int
318 xmpp_features_hook (iksparser *parser, iks *node)
319 {
320   iks *feat;
321   char *ns;
322   for (feat = iks_child (node); feat != NULL; feat = iks_next (feat))
323     {
324       ns = iks_find_attrib (feat, "xmlns");
325       if (!iks_strcmp (ns, "urn:ietf:params:xml:ns:xmpp-tls"))
326         {
327           xmpp_tls_hook (parser, node);
328           return 0;
329         }
330       else if (!iks_strcmp (ns, "urn:ietf:params:xml:ns:xmpp-sasl"))
331         {
332           xmpp_sasl_hook (parser, node);
333           return 0;
334         }
335       else if (!iks_strcmp (ns, "urn:ietf:params:xml:ns:xmpp-bind"))
336         {
337           xmpp_bind_hook (parser, node);
338           return 0;
339         }
340     }
341   return 1;
342 }
343
344 static int
345 xmpp_other_hook (iksparser *parser, iks *node, char *ns)
346 {
347   if (!iks_strcmp (ns, "urn:ietf:params:xml:ns:xmpp-sasl"))
348     {
349       if (!iks_strcmp (iks_name (node), "success"))
350         iks_send_header (parser, server);
351       else if (!iks_strcmp (iks_name (node), "failure"))
352         printf ("failture to authenticate: %s\n",
353                 iks_string (iks_stack (node), node));
354       return 0;
355     }
356   return 1;
357 }
358
359
360 static int
361 hook (void *data, int type, iks *node)
362 {
363   char *name;
364   char *id;
365   char *ns;
366   char *iqns;
367   iksparser *parser;
368   parser = *(iksparser **) data;
369   name = iks_name (node);
370   id = iks_find_attrib (node, "id");
371   ns = iks_find_attrib (node, "xmlns"); 
372   iqns = iks_find_attrib (iks_child (node), "xmlns"); 
373   if (!iks_strcmp (name, "message"))
374     {
375       char *from;
376       from = iks_find_attrib (node, "from");
377       if (!iks_strncmp (from, authed_jid, iks_strlen (authed_jid)))
378         {
379           char *body = iks_find_cdata (node, "body");
380           if (body != NULL)
381             process_mood (parser, body);
382         }
383       else
384         {
385           printf ("%s is not authorized\n", from);
386         }
387       return IKS_OK;
388     }
389   else if (!iks_strcmp (name, "presence"))
390     {
391       char *from;
392       from = iks_find_attrib (node, "from");
393       printf ("I sense a disturbance in the force: %s!\n", from);
394     }
395   else if (!iks_strcmp (name, "iq"))
396     {
397       if (xmpp_id_hook (parser, node, id) == 0)
398         return IKS_OK;
399       if (xmpp_ns_hook (parser, node, iqns) == 0)
400         return IKS_OK;
401       xmpp_iq_error (parser, node);
402     }
403   else if (!iks_strcmp (name, "stream:features"))
404     {
405       if (xmpp_features_hook (parser, node) == 0)
406         return IKS_OK;
407     }
408   else if (!iks_strcmp (name, "stream:error"))
409     {
410       printf ("streamerror: %s\n", iks_string (iks_stack (node), node));
411     }
412   else
413     {
414       if (xmpp_other_hook (parser, node, ns) == 0)
415         return IKS_OK;
416       printf ("No no: %s\n", name);
417     }
418   return IKS_OK;
419 }
420
421 static gboolean
422 handler (GIOChannel *channel, GIOCondition cond, gpointer data)
423 {
424   iksparser *parser = data;
425   iks_recv (parser, 0);
426   return TRUE;
427 }
428
429 struct
430   { char * key; char * val; } keymaps[] =
431 {
432   { "artist", "artist" },
433   { "duration", "length" },
434   { "album", "source" },
435   { "title", "title" },
436   { "track-number", "track" },
437   { "location", "uri" },
438   { NULL, NULL }
439 };
440
441 static char *
442 map_key (char *orig)
443 {
444   int i;
445   for (i = 0; keymaps[i].key != NULL; i++)
446     if (strcmp (orig, keymaps[i].key) == 0)
447       return keymaps[i].val;
448   return NULL;
449 }
450
451 static void
452 tune_add_dbus_arg (iks *tune, DBusMessageIter *args)
453 {
454   DBusMessageIter entry;
455   DBusMessageIter var;
456   char *strkey = NULL;
457   char *strval = NULL;
458   dbus_message_iter_recurse (args, &entry);
459   if (dbus_message_iter_get_arg_type (&entry) == DBUS_TYPE_STRING)
460     {
461       dbus_message_iter_get_basic (&entry, &strkey);
462       dbus_message_iter_next (&entry);
463       if (dbus_message_iter_get_arg_type (&entry) == DBUS_TYPE_VARIANT)
464         {
465           dbus_message_iter_recurse (&entry, &var);
466           if (dbus_message_iter_get_arg_type (&var) == DBUS_TYPE_STRING)
467             {
468               dbus_message_iter_get_basic (&var, &strval);
469             }
470         }
471     }
472   else
473     printf ("%c\n", dbus_message_iter_get_arg_type (&entry));
474   strkey = map_key (strkey);
475   if (strkey && strval)
476     {
477       iks_insert_cdata (iks_insert (tune, strkey), strval, 0);
478     }
479 }
480
481 static iks *
482 tune_from_dbus (DBusMessage *msg)
483 {
484   DBusMessageIter args;
485   iks *tune;
486   tune = iks_new ("tune");
487   iks_insert_attrib (tune, "xmlns", "http://jabber.org/protocol/tune");
488   dbus_message_iter_init (msg, &args);
489   if (dbus_message_iter_get_arg_type (&args) == DBUS_TYPE_ARRAY)
490     {
491       DBusMessageIter dict;
492       dbus_message_iter_recurse (&args, &dict);
493       while (dbus_message_iter_get_arg_type (&dict) ==
494              DBUS_TYPE_DICT_ENTRY)
495         {
496           tune_add_dbus_arg (tune, &dict);
497           dbus_message_iter_next (&dict);
498         }
499     }
500   return tune;
501 }
502
503 static DBusHandlerResult
504 gettune (DBusConnection *conn, DBusMessage *msg, void *data)
505 {
506   iks *tune;
507   if (dbus_message_is_signal (msg, "org.MetaPlayer.tag", "playing"))
508     {
509       printf("publishing tune\n");
510       tune = tune_from_dbus (msg);
511       pushtune (data, "http://jabber.org/protocol/tune", tune);
512       return DBUS_HANDLER_RESULT_HANDLED;
513     }
514   else if (dbus_message_is_signal (msg, "org.MetaPlayer.player", "stop"))
515     {
516       printf("tune stopped\n");
517       tune = iks_new ("tune");
518       iks_insert_attrib (tune, "xmlns", "http://jabber.org/protocol/tune");
519       pushtune (data, "http://jabber.org/protocol/tune", tune);
520       return DBUS_HANDLER_RESULT_HANDLED;
521     }
522   else
523     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
524 }
525
526 static void
527 prepare_dbus (gpointer parser)
528 {
529   DBusConnection *conn;
530   conn = dbus_bus_get (DBUS_BUS_SESSION, NULL);
531   dbus_bus_register (conn, NULL);
532   dbus_bus_add_match (conn,
533                       "type='signal'", NULL);
534   dbus_connection_flush (conn);
535   dbus_connection_setup_with_g_main (conn, g_main_context_default ());
536   dbus_connection_add_filter (conn, gettune, parser, NULL);
537 }
538
539 static void
540 loop (iksparser *parser)
541 {
542   GIOChannel *channel;
543   channel = g_io_channel_unix_new (iks_fd (parser));
544   g_io_add_watch (channel, G_IO_IN, handler, parser);
545   prepare_dbus (parser);
546   g_main_loop_run (g_main_loop_new (g_main_context_default (), TRUE));
547 }
548
549 int
550 main (int argc, char **argv)
551 {
552   iksparser *parser;
553   int c;
554   int askpasswd = 0;
555   char *passwd = strdup ("pubsub");
556   while ((c = getopt (argc, argv, "s:u:p:i:a:w")) != -1)
557     {
558       switch (c)
559         {
560         case 's':
561           server = optarg;
562           break;
563         case 'u':
564           username = optarg;
565           break;
566         case 'p':
567           free (passwd);
568           passwd = strdup (optarg);
569           break;
570         case 'i':
571           pbservice = optarg;
572           break;
573         case 'a':
574           authed_jid = optarg;
575           break;
576         case 'w':
577           askpasswd = 1;
578           break;
579         }
580     }
581   if (askpasswd)
582     passwd = getpass ("Type password: ");
583   password = malloc (sysconf (_SC_PASS_MAX));
584   strcpy (password, passwd);
585   memset (passwd, 0, strlen (passwd));
586   if (!askpasswd)
587     {
588       free (passwd);
589       passwd = NULL;
590     }
591   parser = iks_stream_new ("jabber:client", &parser, hook);
592   iks_connect_tcp (parser, server, 5222);
593   loop (parser);
594   return 0;
595 }