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